JDK中使用的设计模式

📅 2026/6/30 6:57:05
JDK中使用的设计模式
JDK里这些代码作者当时可能只是出于习惯而已并没有想往设计模式上靠因此有的解读可能会比较生硬。设计模式不应该生搬硬套而是因地制宜。设计模式 ≠ 面向对象。设计模式仅仅是设计模式是一套编码实践的方法论只不过经常和OOP相关联起来。将设计模式分为结构模式、生产模式和行为模式是经典的四人帮设计模式(以下简称GOF)的分类方式本文也沿用了这种分类。这几类具体的作用结构模式处理类或对象的组合创建模式创建对象的方式行为模式算法和对象间的职责分配结构模式 Structural适配器模式 Adaptor把一个类的接口换成希望使用的接口。举例java.util.Arrays#asList()public static T ListT asList(T... a) { return new ArrayList(a); }其中ArrayList的结构以及这个构造方法为private static class ArrayListE extends AbstractListE implements RandomAccess, java.io.Serializable { private static final long serialVersionUID -2764017481108945198L; private final E[] a; // 忽略其他代码 ArrayList(E[] array) { a Objects.requireNonNull(array); } }因为ArrayList底层就是一个数组a因此这里你看不到任何结构上的转换只有对外接口的转换。桥接模式 Bridge将一个类的抽象部分剥离出来允许更丰富的变化。如果我写的《大话设计模式》Python版代码实现里手机和手机软件的关系不好理解可以参考桥接里毛笔与颜色的例子。直观来看就是一个(抽象)类A中的成员B仍然是接口或抽象类。A和B可以独立的扩展。举例JDBCJDBC的情况比较复杂其中Connection接口和PreparedStatement接口即为桥接器模式。由于涉及源码较多具体可以参考JDBC源码分析桥接模式。组合模式 Composite将对象组合成树形结构以表示整体-部分的层次结构单个对象和组合对象使用方式具有一致性。举例1java.util.Map#putAll(Map)、java.util.List#addAll(Collection)、java.util.Set#addAll(Collection)这个例子包括其他几个例子都不是很恰当因为Map不是Map的子类事实上Java是不能循环继承的。仅仅是接口层面看上去符合接口和自身同属一个类。装饰器模式 Decorator给一个对象新增额外的功能提供这些功能的类和这个对象没有继承关系。这里有一个问题作为装饰器是要感知被装饰的对象的类型或接口的否则没有办法递归地调用被装饰对象的方法。举例java.io.BufferedInputStreamBufferedInputStream继承了FilterInputStream而FilterInputStream封装了一个InputStream对象。BufferedInputStream使用了一个数组做缓冲区从而提供缓冲输入的功能。FilterInputStream extends InputStream { /** * The input stream to be filtered. */ protected volatile InputStream in; ... } public class BufferedInputStream extends FilterInputStream { protected volatile byte buf[]; ... }外观模式 Facade为一组调用提供一个统一的门面。调用这个门面时会执行这些调用。举例java.lang.Class实例太多了随便选一个吧。这个方法调用的另外两个方法也是Class里的方法。public boolean isLocalClass() { return isLocalOrAnonymousClass() !isAnonymousClass(); }享元模式 Flyweight有效率的存储大量细粒度的对象。注意这个解释是结果并不是手段。简单来说为了共享不变的元数据很容易想到使用持久化的缓存。举例基本数据类型中对于特定范围的值是会缓存为常量值的如java.lang.Integer#valueOf(int)java.lang.Boolean#valueOf(boolean)java.lang.Byte#valueOf(byte)java.lang.Character#valueOf(char)这里有个引申话题Integer的1用判等的结果是两个129呢可以编写一下代码做个测试。代理模式 Proxy为其他对象提供一种代理以控制对这个对象的访问。注意这里强调控制是和其他结构模式的最大的区别。举例java.lang.reflect.Proxy如何使用Proxy类来代理一个对象的方法本文不再复述。创建模式 Creational抽象工厂 Abstract Factory创建一组有关联或相互依赖对象的接口而无需指定它们的实现。举例java.util.ResourceBundle#getBundle()java.util.Calendar#getInstance()、java.util.Arrays#asList()我个人感觉不太像。java.util.ResourceBundle#getBundle()有点沾边可以扫一眼public abstract class ResourceBundle { public static ResourceBundle getBundle(String baseName, Locale targetLocale, ClassLoader loader, Control control) { if (loader null || control null) { throw new NullPointerException(); } return getBundleImpl(baseName, targetLocale, loader, control); } private static ResourceBundle getBundleImpl(String baseName, Locale locale, ClassLoader loader, Control control) { if (locale null || control null) { throw new NullPointerException(); } // We create a CacheKey here for use by this call. The base // name and loader will never change during the bundle loading // process. We have to make sure that the locale is set before // using it as a cache key. CacheKey cacheKey new CacheKey(baseName, locale, loader); ResourceBundle bundle null; // Quick lookup of the cache. BundleReference bundleRef cacheList.get(cacheKey); if (bundleRef ! null) { bundle bundleRef.get(); bundleRef null; } //后续忽略 ... } //后续忽略 ... }ResourceBundle是抽象类。调用getBundle时调用了BundleReference的方法而这个方法本身是在抽象类Reference中的大体是满足这个定义的。建造者模式 Builder将一个复杂对象的构建(Director)与它的表示(Builder)分离使得同样的构建过程可以创建不同的表示(ConcreteBuilder)。举例java.lang.StringBuilder#append()分离的是String对象和String对象的构建过程这样解释可能有点牵强但是也能满足。public AbstractStringBuilder append(String str) { if (str null) return appendNull(); int len str.length(); ensureCapacityInternal(count len); str.getChars(0, len, value, count); count len; return this; }工厂方法模式 Factory Method定义一个用于创建对象的接口让子类决定实例化哪一个类。举例java.lang.Object#toString()很常见的用法是重写(override)子类toString方法。虽然子类只能实例化一个类但是可以有很多子类。原型模式 Prototype用原型实例指定创建对象的种类并且通过拷贝这些原型创建新的对象举例java.lang.Object#clone()、java.lang.Cloneable单例模式 Singleton只允许一个类只有一个实例。举例enum行为模式 Behavioral职责链模式 Chain of Responsibility使多个对象都有机会处理请求从而避免发送者和接收者的耦合关系。将对象连成链并沿着这条链传递请求直到被处理举例java.util.logging.Logger#log()public void log(LogRecord record) { if (!isLoggable(record.getLevel())) { return; } Filter theFilter filter; if (theFilter ! null !theFilter.isLoggable(record)) { return; } // Post the LogRecord to all our Handlers, and then to // our parents handlers, all the way up the tree. Logger logger this; while (logger ! null) { final Handler[] loggerHandlers isSystemLogger ? logger.accessCheckedHandlers() : logger.getHandlers(); for (Handler handler : loggerHandlers) { handler.publish(record); } final boolean useParentHdls isSystemLogger ? logger.useParentHandlers : logger.getUseParentHandlers(); if (!useParentHdls) { break; } logger isSystemLogger ? logger.parent : logger.getParent(); } }我对logger不是特别熟这里可以看到logger向上一级查找并执行。命令模式 Command将请求封装为对象举例java.lang.Runable/java.util.concurrent.Callable实现的run方法就是一系列的命令。将这些命令封装成了一个实现Runable接口的对象。解释器模式 Interpreter给定一个语言定义它的文法的一种表示并定义一个解释器这个解释器使用该表示来解释语言中的句子。