尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
标准库中的设计模式活教材
Java 标准库/Android/Spring 库之所以能成为无数应用的基石除了功能完备之外一个常被忽视却至关重要的原因在于它的设计本身就是设计模式最经典的“活教材”。GoF 的 23 种模式并非纸上谈兵在 java.lang、java.util、java.io 这些开发者每天打交道的包中几乎都能找到它们的影子。下面从创建型、结构型、行为型三个维度盘点那些藏在标准库及主流平台库源码中的设计模式并在每个模式后附上调用方视角的代码——因为模式的价值最终体现在调用方能够以多小的代价获得多大的灵活性。创建型模式控制对象的诞生方式单例模式java.lang.Runtime每个 Java 应用都运行在一个 JVM 进程之上Runtime 类正是这个进程的唯一句柄。它的实现方式是经典的饿汉式单例public class Runtime {private static final Runtime currentRuntime new Runtime();private Runtime() {}public static Runtime getRuntime() {return currentRuntime;}}私有构造器杜绝了外部实例化getRuntime() 始终返回同一个 currentRuntime 引用。调用方代码public class MemoryReporter {public static void main(String[] args) {Runtime rt1 Runtime.getRuntime();Runtime rt2 Runtime.getRuntime();System.out.println(rt1 rt2); // truelong free rt1.freeMemory();long total rt1.totalMemory();System.out.println(used (total - free) / 1024 / 1024 MB);rt1.addShutdownHook(new Thread(() - System.out.println(bye)));}}调用方无法 new Runtime()只能通过 getRuntime() 获取唯一实例。模式在调用方这里的体现就是获取实例的入口唯一且拿到的引用始终相同。工厂模式List.of() / Set.of() / Map.of()标准库里静态工厂方法随处可见而 List.of、Set.of、Map.of 是调用方每天都在用、却很少意识到“这是工厂”的一组设施。它们把“创建集合”从构造器调用变成了对接口的工厂调用调用方只声明自己要一个 List、Set 或 Map至于背后是哪个实现类、按元素个数选用哪种内部结构全部由工厂方法决定。// 调用方只面向接口不出现 new ArrayList() / new HashSet() / new HashMap()ListString names List.of(alice, bob, carol);SetInteger ids Set.of(1, 2, 3);MapString, Integer scores Map.of(alice, 90, bob, 85);// 变量类型可以是更抽象的父接口具体实现对调用方完全透明CollectionString c names;System.out.println(c.contains(bob)); // trueSystem.out.println(scores.get(alice)); // 90来源gitlab.com/leovance5tiae/etrxjdp/-/blob/main/osChw-16380.md// 工厂方法按参数个数返回不同的内部实现ListString empty List.of(); // 共享的空实例ListString one List.of(x); // 针对 1~2 个元素的紧凑实现ListString many List.of(x, y, z);// 元素更多时的通用实现// 不可变性也由工厂保证调用方无需自己包一层try {names.add(dave); // 抛 UnsupportedOperationException} catch (UnsupportedOperationException e) {System.out.println(immutable);}如果打印 List.of()、List.of(x)、List.of(x,y,z) 的 getClass()会看到不同的内部类名如 ImmutableCollections$ListN、List12但这些类并不在公开 API 中属于实现细节——调用方拿到的只是 List 接口具体类型由工厂内部选择。创建入口是 of(...) 这样的具名静态方法返回类型是抽象接口实现类的选择、实例的复用、不可变性的施加全部封装在工厂方法内部。抽象工厂模式JAXP DocumentBuilderFactory工厂方法关注“创建一个产品”抽象工厂关注“创建一族相关产品”。JAXP 中的 DocumentBuilderFactory 正是后者它本身是抽象类newInstance() 根据系统属性或 SPI 返回具体工厂再由这个工厂创建 DocumentBuilder、Document 等一整套 XML 处理对象。// 调用方通过抽象工厂的静态入口获取具体工厂DocumentBuilderFactory factory DocumentBuilderFactory.newInstance();factory.setNamespaceAware(true);// 由工厂创建一族产品DocumentBuilder 及其产出的 DocumentDocumentBuilder builder factory.newDocumentBuilder();Document doc builder.parse(new File(pom.xml));// 同一族中的另一个产品TransformerFactory → TransformerTransformerFactory tf TransformerFactory.newInstance();Transformer transformer tf.newTransformer();transformer.transform(new DOMSource(doc), new StreamResult(System.out));调用方代码中不出现任何具体实现类如 com.sun.org.apache.xerces...更换 XML 处理器只需调整系统属性或 classpath 中的 SPI 配置。一族产品的创建被收进同一个工厂调用方只面向抽象工厂编程。原型模式Cloneable / Object.clone()原型模式通过复制已有实例来创建新对象而不是调用构造器。Java 用 Cloneable 标记接口和 Object.clone() 提供了语言级支持ArrayList、HashMap 等集合类都实现了它。ArrayListString original new ArrayList(List.of(a, b));SuppressWarnings(unchecked)ArrayListString copy (ArrayListString) original.clone();来源gitlab.com/leovance5tiae/etrxjdp/-/blob/main/EPEdg-60348.mdcopy.add(c);System.out.println(original); // [a, b]System.out.println(copy); // [a, b, c]// 数组同样支持 cloneint[] src {1, 2, 3};int[] dst src.clone();dst[0] 99;System.out.println(Arrays.toString(src)); // [1, 2, 3]调用方用 clone() 得到新对象无需知道 ArrayList 内部数组的扩容策略或 HashMap 的桶结构。复制的细节由被复制对象自己承担调用方只声明“要一个副本”。需要注意 clone() 默认是浅拷贝深层复制仍需子类覆写。建造者模式StringBuilderStringBuilder 是一个流式建造者。append() 返回 this使得多次链式调用可以逐步“建造”出最终的字符串而建造过程本身与最终产品的表示分离。String sql new StringBuilder().append(SELECT id, name FROM user).append( WHERE age ).append(18).append( ORDER BY ).append(id).toString();// 条件化建造每一步都可选、可跳过StringBuilder sb new StringBuilder();sb.append(Hello);if (Boolean.TRUE) sb.append(, World);sb.append(!);String msg sb.toString();调用方通过链式 append 逐步组装最后 toString() 才产出产品。建造过程与最终对象分离调用方掌控建造的顺序和内容。结构型模式组装与增强的艺术装饰器模式Java IOjava.io 包是装饰器模式最 unmistakable 的实现。FileInputStream 是被装饰的组件BufferedInputStream 是具体装饰器两者都继承自 InputStream装饰器在持有被装饰对象的同时为其添加缓冲功能。// 逐层包裹字节文件 → 缓冲 → 解压 → 字符解码try (Reader reader new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(data.gz)),StandardCharsets.UTF_8))) {来源gitlab.com/leovance5tiae/etrxjdp/-/commit/786062f57f73fc474b9ed5ac6cef1130d96b4317String line;while ((line reader.readLine()) ! null) {System.out.println(line);}}// 同一套代码换个装饰组合能力就变了InputStream in new BufferedInputStream(new FileInputStream(a.bin));调用方通过嵌套构造叠加功能而变量类型始终是顶层抽象 Reader / InputStream。每包一层就多一项能力调用方的读取代码一行不改。FilterInputStream 作为抽象装饰器角色将所有方法默认委托给被包装的流子类只需覆写需要增强的方法。适配器模式Arrays.asList() / InputStreamReaderArrays.asList() 把数组适配成 List 接口InputStreamReader 把字节流的 InputStream 适配成字符流的 Reader。// 数组 → List 接口String[] arr {a, b, c};ListString list Arrays.asList(arr);System.out.println(list.contains(b)); // true// 字节流 → 字符流接口try (Reader r new InputStreamReader(new FileInputStream(notes.txt), StandardCharsets.UTF_8)) {char[] buf new char[256];int n r.read(buf);}调用方想要的是 List 或 Reader 的能力手上却只有数组或字节流。适配器让原本接口不匹配的两端在调用方这里无缝衔接。桥接模式JDBCJDBC API 是桥接模式在平台级设计中的经典体现。java.sql.Driver、Connection、Statement、ResultSet 这些接口构成了抽象部分各数据库厂商的驱动实现构成了实现部分。Class.forName(org.postgresql.Driver);try (Connection conn DriverManager.getConnection(jdbc:postgresql://localhost:5432/demo, user, pwd);PreparedStatement ps conn.prepareStatement(SELECT id, name FROM t WHERE id ?)) {ps.setInt(1, 42);try (ResultSet rs ps.executeQuery()) {while (rs.next()) {System.out.println(rs.getInt(id) rs.getString(name));}}}换成 MySQL 只需改驱动类名和 URL调用方代码一个字都不用动。抽象的 Connection/Statement 与具体数据库实现各自独立演化。来源gitlab.com/leovance5tiae/etrxjdp/-/blob/main/QrpmY-59127.md组合模式Android View / ViewGroupAndroid 的视图体系是组合模式在移动端的标准实现ViewGroup 继承自 View因此它既是一个可被绘制的“叶子”式组件又是一个可以容纳其他 View 的“树枝”式容器。来源gitlab.com/leovance5tiae/etrxjdp/-/blob/main/IThvu-62581.mdpublic class MainActivity extends Activity {Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);LinearLayout root new LinearLayout(this);root.setOrientation(LinearLayout.VERTICAL);TextView title new TextView(this);title.setText(Settings);LinearLayout row new LinearLayout(this); // ViewGroup 也是 Viewrow.setOrientation(LinearLayout.HORIZONTAL);row.addView(new Button(this));row.addView(new Switch(this));root.addView(title); // 叶子root.addView(row); // 树枝setContentView(root);// 统一操作无需区分叶子与容器root.setVisibility(View.VISIBLE);root.setAlpha(0.95f);root.setOnClickListener(v - Log.d(demo, root clicked));}}addView(View child) 的参数类型是 View而 LinearLayout、FrameLayout、RecyclerView 等容器本身也是 View。调用方对 root 调用 setVisibility、setAlpha、setOnClickListener 时既作用于单个组件也能递归作用于整棵子树。叶子与树枝共享同一接口调用方不需要为“单个组件”和“组件集合”写两套逻辑。外观模式Spring JdbcTemplateJdbcTemplate 是 Spring 对 JDBC 子系统的门面。获取连接、创建 Statement、执行 SQL、遍历 ResultSet、处理 SQLException、释放资源这一整套流程在原生 JDBC 中需要调用方逐步完成JdbcTemplate 把这些步骤收进一个高层接口。Repositorypublic class UserDao {private final JdbcTemplate jdbc;public UserDao(JdbcTemplate jdbc) {this.jdbc jdbc;}来源gitlab.com/leovance5tiae/etrxjdp/-/blob/main/xktfb-34722.md// 调用方不出现 Connection / PreparedStatement / ResultSet / try-finallypublic ListString findNamesOlderThan(int age) {String sql SELECT name FROM user WHERE age ?;return jdbc.queryForList(sql, String.class, age);}public int updateEmail(int id, String email) {String sql UPDATE user SET email ? WHERE id ?;return jdbc.update(sql, email, id);}public User findById(int id) {String sql SELECT id, name, email FROM user WHERE id ?;return jdbc.queryForObject(sql, (rs, rowNum) -new User(rs.getInt(id), rs.getString(name), rs.getString(email)),id);}}对比原生 JDBC调用方需要 getConnection()、prepareStatement()、setInt()、rs.next()、while 循环、try-catch-finally 关闭资源还要处理受检的 SQLException。JdbcTemplate 把所有这些“脏活”挡在门面之后调用方代码里只出现 SQL 和参数。子系统越复杂外观模式在调用方一侧的收益越明显。享元模式Android Message.obtain()Android 的 Message 类在文档中明确建议虽然构造器是公开的但获取 Message 实例的最佳方式是从对象池中“obtain”一个而不是 new。Message.obtain() 内部维护了一个消息池已经用过的 Message 在回收后可以再次取出、重置字段后复用。来源gitlab.com/leovance5tiae/etrxjdp/-/blob/main/xXpTd-76667.mdpublic class PlaybackService extends Service {private static final int PLAY 1;private static final int STOP 2;private final Handler handler new Handler(Looper.getMainLooper()) {Overridepublic void handleMessage(Message msg) {switch (msg.what) {case PLAY: startPlayback(); break;case STOP: stopPlayback(); break;}}};void requestPlay() {Message msg Message.obtain(); // 来自全局消息池msg.what PLAY;msg.obj track-001;handler.sendMessage(msg);}void requestStop() {Message msg Message.obtain(handler, STOP);handler.sendMessage(msg);}}调用方写的是 Message.obtain()而不是 new Message()。对象池、回收、重置这些享元逻辑全部由 Message 内部完成调用方只负责“设置本次需要变化的字段”然后发送。池中的对象可以被反复借出和归还但调用方看不到池只看到一个轻量的 obtain() 入口。代理模式java.lang.reflect.ProxyJDK 动态代理是代理模式在语言层面的支持。Proxy.newProxyInstance() 在运行时生成一个实现指定接口的代理对象所有方法调用都被转发到 InvocationHandler.invoke()。interface UserService {String find(int id);void save(String name);}UserService target new UserService() {public String find(int id) { return user- id; }public void save(String name) { System.out.println(saved name); }};InvocationHandler handler (proxy, method, args) - {long t0 System.nanoTime();try {return method.invoke(target, args); // 转发给真实对象} finally {System.out.printf(%s took %d ns%n,method.getName(), System.nanoTime() - t0);}};UserService proxy (UserService) Proxy.newProxyInstance(UserService.class.getClassLoader(),new Class?[]{UserService.class},handler);来源gitlab.com/leovance5tiae/etrxjdp/-/blob/main/hNJuA-15822.mdproxy.find(7); // 调用方写的是接口方法实际经过代理增强proxy.save(alice);调用方拿到的变量类型是接口但运行期是生成的代理类。增强逻辑由 handler 统一插入调用方代码保持原样。这一机制后来成为 Spring AOP 的底层基础之一。行为型模式算法与职责的分配策略模式ComparatorComparator 是标准库中最清晰的策略接口。Arrays.sort(T[], Comparator) 将排序算法的可变部分——比较规则——抽取为一个独立对象。String[] names {bob, Alice, carol, Dave};// 同一份排序骨架传入不同策略Arrays.sort(names); // 自然序Arrays.sort(names, Comparator.reverseOrder()); // 逆序Arrays.sort(names, String.CASE_INSENSITIVE_ORDER); // 忽略大小写Arrays.sort(names, Comparator.comparing(String::length)); // 按长度// 策略对象还可交给有序映射长期持有MapString, Integer map new TreeMap(Comparator.reverseOrder());map.put(a, 1);map.put(b, 2);System.out.println(map.keySet()); // [b, a]调用方每次只是替换传入的比较器对象排序算法本身固定不变。比较规则与排序过程在调用点完成解耦。来源gitlab.com/leovance5tiae/etrxjdp/-/blob/main/NOaZq-27234.md模板方法模式AbstractListAbstractList 定义了集合操作的算法骨架。以 addAll(int index, Collection c) 为例它规定了“检查索引 → 遍历集合 → 逐个调用 add(index, e)”的固定流程但 add(int, E) 本身留为抽象方法由子类提供具体实现。// 自定义容器只实现 get / size / add(int,E)其余骨架由 AbstractList 提供public class FixedListE extends AbstractListE {private final Object[] data;private int size 0;public FixedList(int capacity) { data new Object[capacity]; }Override public E get(int index) { return (E) data[index]; }Override public int size() { return size; }Override public void add(int index, E e) { data[index] e; size; }}// 调用方addAll 的检查索引→逐个插入骨架来自 AbstractListListString list new FixedList(8);list.addAll(0, List.of(a, b, c));System.out.println(list); // [a, b, c]调用方享用的是 AbstractList 已固化的算法流程子类只填空缺步骤。调用点看不到流程但流程被模板牢牢约束。解释器模式java.util.regex.Pattern / Matcher解释器模式定义一种语言用语法树表示其中的句子并提供一个解释器来执行这些句子。标准库中最典型的实现就是正则表达式Pattern.compile() 将正则表达式解析为内部 Pattern.Node 树如 GroupCurly、CharProperty、Branch 等节点Matcher 遍历这棵树对输入进行解释匹配。// 调用方用 compile 构建解释器用 matcher 执行解释Pattern pattern Pattern.compile((\\d{3})-(\\d{4}));Matcher matcher pattern.matcher(Call 555-1234 or 666-7890 now);while (matcher.find()) {System.out.println(area matcher.group(1) , number matcher.group(2));}// 同一棵语法树可以反复用于不同输入Matcher m2 pattern.matcher(Fax: 111-2222);System.out.println(m2.matches()); // falseSystem.out.println(m2.find()); // true调用方看不到 Pattern.Node 这棵语法树只面对 compile / matcher / find / group 这几个概念。语法规则与解释执行被封装在 Pattern/Matcher 内部调用方只负责提供表达式和输入。java.text.DateFormat/SimpleDateFormat 对日期格式串的解释javax.script 引擎对脚本语言的解释也都属于同一模式在不同层次上的体现。责任链模式java.util.logging / Servlet FilterChain责任链让多个处理者依次有机会处理请求处理者之间通过“下一个”引用串联。java.util.logging.Logger 在记录日志时会先交给自己的 Handler再沿父 Logger 的层次向上传递Servlet 的 FilterChain 则把请求依次交给每个 Filter。来源gitlab.com/leovance5tiae/etrxjdp/-/commit/cbaa867e243ff97b6d2773ebc5d139095976cd7d// java.util.loggingHandler 链 父 Logger 链Logger logger Logger.getLogger(com.example);logger.addHandler(new ConsoleHandler());logger.addHandler(new FileHandler(app.log));logger.info(hello); // 消息沿 Handler 链和父 Logger 传递// Servlet FilterChain每个 Filter 决定是否继续传递public class AuthFilter implements Filter {Overridepublic void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {if (!authenticated(req)) {((HttpServletResponse) res).sendError(401);return; // 中断链}chain.doFilter(req, res); // 交给下一个 Filter}}调用方只需注册处理者、发起一次请求请求沿链传递的过程由链本身驱动调用方不关心链上有几个节点、谁先谁后。备忘录模式java.sql.Savepoint备忘录模式在不破坏封装的前提下保存对象内部状态以便之后恢复。JDBC 的 Savepoint 是标准库中最直接的实现setSavepoint() 记录事务当前状态rollback(Savepoint) 恢复到该状态。来源gitlab.com/leovance5tiae/etrxjdp/-/commit/eda3d458d0c142be0d2626bc2cd0fc811101bcfdtry (Connection conn dataSource.getConnection()) {conn.setAutoCommit(false);Savepoint before conn.setSavepoint(); // 保存当前状态备忘录try {conn.createStatement().executeUpdate(UPDATE account SET balance balance - 100 WHERE id 1);conn.createStatement().executeUpdate(UPDATE account SET balance balance 100 WHERE id 2);conn.commit();} catch (SQLException e) {conn.rollback(before); // 恢复到备忘录记录的状态System.out.println(rolled back to savepoint);}}调用方只持有 Savepoint 这个不透明句柄状态的保存与恢复由数据库连接内部完成调用方不需要知道事务日志或回滚段的任何细节。访问者模式java.nio.file.FileVisitor访问者模式把“对一组对象的操作”从对象结构中分离出来。Files.walkFileTree() 遍历文件树每遇到文件或目录就调用 FileVisitor 的对应方法遍历逻辑固定而“遇到节点做什么”由调用方提供的访问者决定。Path start Paths.get(/tmp);Files.walkFileTree(start, new SimpleFileVisitorPath() {Overridepublic FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {System.out.println(dir: dir);return FileVisitResult.CONTINUE;}Overridepublic FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {System.out.println(file: file ( attrs.size() bytes));return FileVisitResult.CONTINUE;}Overridepublic FileVisitResult visitFileFailed(Path file, IOException exc) {System.out.println(skip: file);return FileVisitResult.SKIP_SUBTREE;}});调用方实现访问者接口walkFileTree 负责遍历结构并在合适的节点上回调。遍历算法与节点操作被拆开同一棵树可以配上不同的访问者执行不同任务。javax.lang.model.element.ElementVisitor 对注解处理中的元素树也采用了同样的结构。观察者模式Observer / Observablejava.util.Observer 接口只有一个 update() 方法Observable 维护观察者列表并在状态变化时通知它们。class Stock extends Observable {private double price;void setPrice(double p) {this.price p;setChanged();notifyObservers(p);}}class Logger implements Observer {public void update(Observable o, Object arg) {System.out.println(price changed to arg);}}Stock stock new Stock();stock.addObserver(new Logger());stock.setPrice(10.5); // 观察者被自动回调stock.deleteObservers();stock.setPrice(11.0); // 不再有输出调用方无需知道谁会收到通知只需 addObserver 注册、notifyObservers 触发发布者与订阅者在注册那一刻建立松耦合连接。虽然 Observable 在 Java 9 中被标记为过时但它仍然是理解观察者模式最直接的 JDK 范本。来源gitlab.com/leovance5tiae/etrxjdp/-/commit/541de448dcc315425cb93eeafbbe383cc3656a68迭代器模式IteratorCollection 的 iterator() 返回 Iterator将“遍历”从“容器”中分离出来。ListString list new ArrayList(List.of(a, b, c));IteratorString it list.iterator();while (it.hasNext()) {String s it.next();if (s.equals(b)) it.remove(); // 通过迭代器安全删除}System.out.println(list); // [a, c]// 客户端代码完全不依赖 ArrayList 的内部表示for (String s : Set.of(x, y)) {System.out.println(s);}调用方只依赖 Iterator 的两个方法。遍历协议从容器内部剥离出来成为可互换的独立对象。命令模式RunnableRunnable 将“一个计算或动作”封装为对象使得动作可以被传递、排队、组合。Runnable logTask () - System.out.println(write log);Runnable mailTask () - System.out.println(send mail);ExecutorService pool Executors.newSingleThreadExecutor();pool.execute(logTask);pool.execute(mailTask);new Thread(logTask).start();pool.shutdown();logTask 这个变量就是一个命令对象请求做什么与执行者谁来执行、何时执行在调用方这里被彻底分开。中介者模式Android Activity 协调 ViewGoF 的中介者模式通常这样描述一组同事对象Colleague之间需要通信但若彼此直接持有引用耦合会随对象数量呈网状增长于是引入中介者Mediator同事只与中介者交互由中介者负责转发和协调。Android 的 Activity 正是这种角色。一个界面上的 EditText、Button、TextView 彼此并不持有对方的引用它们之间的联动全部经由 Activity 中注册的回调完成public class FormActivity extends Activity {private EditText nameInput;private Button submitButton;private TextView statusText;Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);nameInput new EditText(this);submitButton new Button(this);statusText new TextView(this);// Button 不知道 EditText / TextView 的存在只把点击事件交给 ActivitysubmitButton.setOnClickListener(v - {String name nameInput.getText().toString();if (name.isEmpty()) {statusText.setText(name required);} else {statusText.setText(hello, name);}});// EditText 也不知道 Button 的存在只把文本变化交给 ActivitynameInput.addTextChangedListener(new TextWatcher() {Override public void beforeTextChanged(CharSequence s, int a, int b, int c) {}Override public void onTextChanged(CharSequence s, int a, int b, int c) {}Override public void afterTextChanged(Editable s) {submitButton.setEnabled(s.length() 0); // 由 Activity 协调另一个 View}});LinearLayout root new LinearLayout(this);root.setOrientation(LinearLayout.VERTICAL);root.addView(nameInput);root.addView(submitButton);root.addView(statusText);setContentView(root);}}在这段代码里submitButton、nameInput、statusText 三个同事对象之间没有任何直接引用。Button 的点击逻辑里出现了 nameInput 和 statusText但那是 Activity 的字段访问不是 Button 持有它们EditText 的文本监听里出现了 submitButton同样是在 Activity 的匿名内部类中通过外部类字段访问。所有跨 View 的协调都发生在 Activity 这个中介者内部。来源gitlab.com/leovance5tiae/etrxjdp/-/commit/22f655c8e42a4d15b3f140a8ea9247a15290a335与 GoF 标准结构对照MediatorActivity更准确地说是 Activity 中承载协调逻辑的那部分。ConcreteColleagueButton、EditText、TextView。交互方式GoF 的典型实现中Colleague 持有 Mediator 引用并调用 mediator.changed(this)Android 这里 View 并不持有 Activity 引用而是通过 setOnClickListener、addTextChangedListener 把回调注册给 Activity由 Activity 在回调中读取/修改其他 View。这是回调式中介者而非引用式中介者但“同事之间不直接通信、协调逻辑集中在中介者”这一本质完全一致。这种设计带来的收益也和中介者模式的目标吻合新增一个 View 时只需在 Activity 里加一个字段和相应回调不需要修改已有 View 的代码移除某个 View 时只要 Activity 中不再引用它其他 View 不受影响。耦合从“View 之间的网状引用”变成了“View 与 Activity 之间的星形回调”。JDK 自身也有中介者思想的应用但都不如 Android Activity 这样典型ExecutorService 可以看作生产者与消费者之间的中介者java.util.Timer 协调多个定时任务javax.swing.ButtonGroup 协调一组按钮的选中状态——它们都把“协调”内建在设施中没有暴露独立的 Mediator 接口属于中介者思想的应用而非教科书式实现。状态模式JDK 中的“弱存在”状态模式在标准库中没有严格意义上的 GoF 实现。java.lang.Thread 的 Thread.State 枚举描述了线程的生命周期状态NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED状态转换由 JVM 驱动调用方通过 getState() 观察。但线程的行为并非由状态对象多态分发——Thread 没有把“每个状态下的行为”委托给独立的 State 子类因此只能算“状态思想”的体现而非严格的状态模式。真正接近状态模式的代码往往出现在应用层如订单状态机、工作流引擎而不是 JDK 内部。来源gitlab.com/leovance5tiae/etrxjdp/-/blob/main/yWlEW-06616.md写在最后标准库对设计模式的使用有一个共同特征模式服务于库的扩展性与稳定性而非为了模式而模式。Java IO 用装饰器让流的功能可以任意叠加集合框架用迭代器和策略让遍历与比较从容器中解耦AbstractList 用模板方法把公共算法固化在抽象层Proxy 用动态代理把横切关注点的插入推迟到运行时JAXP 用抽象工厂统一一族 XML 产品的创建Pattern/Matcher 用解释器把正则语法与匹配执行封装在一起FileVisitor 用访问者把遍历算法与节点操作拆开Android 用 ViewGroup 把“整体与部分”统一为同一接口用 Message.obtain() 把高频对象的复用藏进池中用 Activity 把 View 之间的网状通信收敛为星形回调Spring 用 JdbcTemplate 把 JDBC 的复杂度隔离在门面之后。这些设计使得标准库与平台库在保持核心接口稳定的同时允许实现者、调用者、扩展者各自在恰当的层次上参与进来。而调用方代码最能说明设计模式的实际价值模式的存在感往往不在调用处而在调用方能够以多小的代价获得多大的灵活性。new BufferedReader(new FileInputStream(...)) 只多一层括号就换来了缓冲能力Arrays.sort(arr, cmp) 只多一个参数就换掉了排序规则Pattern.compile(...) 只多一行就把一套匹配语言交给调用方root.addView(row) 让容器和组件并排出现、被同一套操作覆盖Message.obtain() 只换了一个获取入口就绕开了高频分配setOnClickListener 只注册一个回调就让 View 不必持有彼此的引用。读 JDK 源码时带着“这里用了什么模式”的视角往往能比单纯记忆模式定义获得更具体的判断力你会看到模式在真实代码中常常是混合的、半透明的、带折中的——Activity 用回调而非同事持有中介者引用Observable 被标记过时Thread.State 只借了状态模式的思想——而这种“不纯粹”恰恰是工程设计的常态。
RELATED

相关推荐

AI如何革新工程管理:从知识图谱到智能决策

AI如何革新工程管理:从知识图谱到智能决策

1. 项目概述:当AI遇上工程管理在建筑工地现场,项目经理小王正对着堆积如山的进度报表发愁。突然,他的手机弹出一条通知:"B3区域混凝土浇筑延误2小时,建议调整塔吊使用顺序和钢筋工班次"。这条精准预警来自他…

📅 2026/10/30 7:10:02
空间分析数据工程化:坐标参考系、空间权重与格网聚合跑批

空间分析数据工程化:坐标参考系、空间权重与格网聚合跑批

简介:《空间分析数据》面向GIS初学者与ArcGIS实践者,围绕空间位置、空间关系、空间统计及栅格与矢量分析等主题,帮助读者在真实数据中理解缓冲区、网络分析、聚类和热点分析等方法的实现过程。压缩包为rar格式,共1555个文件&#…

📅 2026/10/30 7:10:02
paho-mqtt-c 编译链接与 SSL/TLS 双向认证避坑指南

paho-mqtt-c 编译链接与 SSL/TLS 双向认证避坑指南

简介:面向物联网与嵌入式开发者的 paho-mqtt-c 实践代码包,聚焦 C 语言环境下的 MQTT 客户端通信,涵盖连接 broker、发布订阅、遗嘱设置、自动重连以及 SSL/TLS 加密配置等常见需求,适合需要快速理解客户端封装思路或搭建测试链路…

📅 2026/10/30 7:10:02
MORE NEWS

更多资讯

📰

ARTEX漏洞推送六渠道完整配置:钉钉、飞书、企业微信、Telegram与邮件实战

【免费下载链接】ARTEX Autumn-27/ARTEX 持续维护开源版本 项目地址: https://gitcode.com/gh_mirrors/artex/ARTEX 点击查看 免费下载 ARTEX 漏洞推送功能可以让每一次高危漏洞发现都第一时间送达你的 IM 群或邮箱。它内置了钉钉、飞书、企业微信、通用 Webhook、…

📰

Macro desktop-cef 深度解析:用 Chromium(CEF)为 macOS 构建 Tauri 桌面端

后端前端协同办公即时通讯企业应用CRMAI Agent人工智能 【免费下载链接】macro Macro is a unified workspace for teams: email, chat, docs, tasks, agents, calls, and CRM — -linked together with shared AI memory. 项目地址: https://gitcode.com/GitHub_Tr…

📰

BlueprintJS 选型与上手指南:React 数据密集型应用的桌面 UI 底座

BlueprintJS 选型与上手指南:React 数据密集型应用的桌面 UI 底座 【免费下载链接】blueprint A React-based UI toolkit for the web 项目地址: https://gitcode.com/gh_mirrors/bl/blueprint BlueprintJS 是 Palantir 出品的 React 组件库,定位…

📰

awesome-openclaw技能生态指南:ClawHub与5400+个Skills如何挑选与安装

【免费下载链接】awesome-openclaw A curated list of the best OpenClaw resources: official projects, skills, plugins, dashboards, deployment tooling, memory systems, and guides. 项目地址: https://gitcode.com/gh_mirrors/awe/awesome-openclaw 点击查看…

📰

从自动点到AI智能助手:ShizukuDhizuku-list自动化AI工具盘点,让手机替你干活

【免费下载链接】ShizukuDhizuku-list 一个收集支持Shizuku和Dhizuku的软件的项目 项目地址: https://gitcode.com/gh_mirrors/sh/ShizukuDhizuku-list 点击查看 免费下载 想让手机自动点签到、自动执行脚本,甚至用一句话"指挥"手机&#xff…

📰

WPF三种启动方式 + 静态资源 + 数据绑定 满分知识点整理

模块一:WPF应用程序三种启动方式(必考大题)1.1 三种方式核心对比(背诵)方式1:StartupUri 指定启动页(推荐、最常用)特点:XAML直接配置、写法最简、无需后台代码缺点&…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬