1. 概述
结构型设计模式关注的是如何组织类和对象之间的关系,从而简化大型对象结构的设计。它们提供了一种可复用的方案,用于组合类与对象,让系统更加灵活、易于维护。
《设计模式:可复用面向对象软件的基础》(俗称“四人组”或 GoF)定义了七种结构型模式。在本文中,我们将通过 Java 核心库中的实际例子,来理解这些模式是如何被应用的。
2. 适配器模式 (Adapter)
适配器模式顾名思义,就是将一个接口转换为客户期望的另一个接口。它常用于当我们需要使用某个类,但它的接口不符合我们的需求时。
Java 集合框架中就有很多适配器模式的应用:
List<String> musketeers = Arrays.asList("Athos", "Aramis", "Porthos");
✅ 这里 Arrays.asList
就是把数组适配成了 List
接口。
I/O 框架也大量使用了这种模式。比如下面这段代码展示了如何将 InputStream
转换为 Reader
:
InputStreamReader input = new InputStreamReader(new FileInputStream("input.txt"));
3. 桥接模式 (Bridge)
桥接模式的作用是将抽象部分与实现部分分离,使它们可以独立变化,同时又能协同工作。
在 Java 中,JDBC API 是典型的桥接模式体现。它作为数据库(如 Oracle、MySQL、PostgreSQL)与其具体实现之间的桥梁。
JDBC 提供了一系列标准接口,例如 Driver
、Connection
和 ResultSet
。不同的数据库厂商可以根据这些接口提供自己的实现。
例如创建数据库连接:
Connection connection = DriverManager.getConnection(url);
这里的 url
字符串可以表示任意数据库:
// PostgreSQL 示例
String url = "jdbc:postgresql://localhost/demo";
// MySQL 示例
String url = "jdbc:mysql://localhost/demo";
4. 组合模式 (Composite)
组合模式用来处理树形结构的对象集合,使得客户端可以统一地对待单个对象和对象组合。
在 AWT/Swing 中,嵌套容器就是组合模式的经典应用。java.awt.Container
可以包含其他组件,形成一个组件树结构。
示例代码如下:
JTabbedPane pane = new JTabbedPane();
pane.addTab("1", new Container());
pane.addTab("2", new JButton());
pane.addTab("3", new JCheckBox());
📌 所有类如 JTabbedPane
、JButton
、JCheckBox
都继承自 Container
。这说明无论是一个组件还是整个组件树,都可以用同样的方式处理。
5. 装饰器模式 (Decorator)
当我们想在不修改原有对象的前提下增强其功能时,装饰器模式就派上用场了。它是通过包装对象的方式来附加职责。
Java I/O 包中广泛使用了装饰器模式:
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("test.txt")));
while (bis.available() > 0) {
char c = (char) bis.read();
System.out.println("Char: " + c);
}
✅ 在这个例子中,BufferedInputStream
装饰了 FileInputStream
,为其添加了缓冲功能。
⚠️ 注意:两个类都继承自 InputStream
,这是装饰器模式的典型特征。
6. 外观模式 (Facade)
外观模式为复杂的子系统提供了一个简单的接口,隐藏了底层的复杂性。
Faces API 中的 ExternalContext
就是一个很好的例子。它内部使用了 HttpServletRequest
、HttpServletResponse
等类,但对外暴露的是统一接口。
来看 Primefaces 如何利用外观模式写响应内容:
protected void writePDFToResponse(ExternalContext externalContext, ByteArrayOutputStream baos, String fileName)
throws IOException, DocumentException {
externalContext.setResponseContentType("application/pdf");
externalContext.setResponseHeader("Expires", "0");
// 设置更多相关 header
externalContext.setResponseContentLength(baos.size());
externalContext.addResponseCookie(
Constants.DOWNLOAD_COOKIE, "true", Collections.<String, Object>emptyMap());
OutputStream out = externalContext.getResponseOutputStream();
baos.writeTo(out);
// 清理资源
}
📌 客户端只需要操作 ExternalContext
,完全不需要知道底层的 HttpResponse
存在。
7. 享元模式 (Flyweight)
享元模式旨在通过共享对象来减少内存占用。对于不可变且可共享状态的对象,我们可以缓存它们以提升性能。
Java 的包装类型中就大量使用了享元模式:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high) {
return IntegerCache.cache[i + (-IntegerCache.low)];
}
return new Integer(i);
}
📌 Integer.valueOf()
方法会缓存 -128 到 127 之间的值,避免频繁创建新对象。
8. 代理模式 (Proxy)
代理模式为其他对象提供一个代理或占位符,以便控制对原对象的访问。
与外观模式不同的是,代理模式保持了与原对象一致的接口。
Java 提供了 java.lang.reflect.Proxy
类来支持动态代理:
Foo proxyFoo = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(),
new Class<?>[] { Foo.class }, handler);
📌 上面这段代码创建了一个 Foo
接口的代理对象 proxyFoo
。
9. 总结
在这篇文章中,我们回顾了 Java 核心库中结构型设计模式的实际应用场景。
我们依次介绍了以下七种模式,并通过代码片段说明了它们的用途:
- ✅ 适配器(Adapter)
- ✅ 桥接(Bridge)
- ✅ 组合(Composite)
- ✅ 装饰器(Decorator)
- ✅ 外观(Facade)
- ✅ 享元(Flyweight)
- ✅ 代理(Proxy)
希望这篇整理能帮你快速回忆起这些经典模式的本质和用法。