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 提供了一系列标准接口,例如 DriverConnectionResultSet。不同的数据库厂商可以根据这些接口提供自己的实现。

例如创建数据库连接:

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());

📌 所有类如 JTabbedPaneJButtonJCheckBox 都继承自 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 就是一个很好的例子。它内部使用了 HttpServletRequestHttpServletResponse 等类,但对外暴露的是统一接口。

来看 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)

希望这篇整理能帮你快速回忆起这些经典模式的本质和用法。


原始标题:Structural Patterns in Core Java | Baeldung