Java 23 种设计模式,通常指由 Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 四位作者(合称 GoF)在《设计模式:可复用面向对象软件的基础》一书中提出的 23 种经典模式。它们为解决软件设计中反复出现的特定问题提供了优雅、可复用的解决方案,是提升代码可维护性、可扩展性和可复用性的核心工具 。
这些模式根据其目的和范围,被划分为三大类:创建型模式 、结构型模式 和行为型模式 。
一、 创建型模式 (Creational Patterns)
目的:处理对象创建机制,将对象的创建与使用分离,使系统在创建对象时更加灵活和独立于具体类。
| 模式名称 | 核心功能简介 | 典型应用场景 |
|---|---|---|
| 单例模式 (Singleton) | 确保一个类只有一个实例,并提供一个全局访问点。 | 数据库连接池、线程池、配置管理类、日志对象等 。 |
| 工厂方法模式 (Factory Method) | 定义一个用于创建对象的接口,但让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。 | 日志记录器(输出到文件或控制台)、数据库访问层(连接不同数据库)。 |
| 抽象工厂模式 (Abstract Factory) | 提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。 | GUI 工具包(创建跨平台的按钮、文本框等组件)、数据库迁移工具(创建不同数据库的 Connection, Command 等对象)。 |
| 建造者模式 (Builder) | 将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。 | 构造复杂的套餐对象(如电脑配置:CPU、内存、硬盘等可选)、生成具有不同格式的文档(HTML, PDF)。 |
| 原型模式 (Prototype) | 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。 | 对象创建成本高昂(如从数据库加载大量数据),需要避免重复初始化;游戏中的敌人、道具的批量生成 。 |
二、 结构型模式 (Structural Patterns)
目的:关注类和对象的组合,形成更大的结构。它通过继承和组合机制,使多个对象或类能够协同工作,实现更复杂的功能。
| 模式名称 | 核心功能简介 | 典型应用场景 |
|---|---|---|
| 适配器模式 (Adapter) | 将一个类的接口转换成客户期望的另一个接口。使原本由于接口不兼容而不能一起工作的类可以一起工作。 | 旧系统接口升级、使用第三方库时接口不匹配、数据格式转换(如 XML 转 JSON)。 |
| 装饰器模式 (Decorator) | 动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式比生成子类更为灵活。 | Java I/O 流(如 BufferedInputStream 装饰 FileInputStream)、为图形界面组件添加滚动条或边框 。 |
| 代理模式 (Proxy) | 为其他对象提供一种代理以控制对这个对象的访问。 | 远程代理(RPC)、虚拟代理(延迟加载大图片)、保护代理(权限控制)、Spring AOP 中的动态代理 。 |
| 外观模式 (Facade) | 为子系统中的一组接口提供一个一致的界面。外观模式定义了一个高层接口,使得这一子系统更加容易使用。 | 简化复杂 API 的调用(如 SLF4J 日志门面)、整合多个模块为一个统一的操作入口 。 |
| 桥接模式 (Bridge) | 将抽象部分与它的实现部分分离,使它们都可以独立地变化。 | 跨平台图形库(抽象:窗口,实现:Windows/Linux 绘制)、消息发送(抽象:消息类型,实现:短信/邮件发送)。 |
| 组合模式 (Composite) | 将对象组合成树形结构以表示"部分-整体"的层次结构。使得用户对单个对象和组合对象的使用具有一致性。 | 文件系统(文件与文件夹)、GUI 中的容器与组件、公司组织架构 。 |
| 享元模式 (Flyweight) | 运用共享技术有效地支持大量细粒度的对象。 | 文本编辑器中的字符对象池、游戏中的粒子系统(共享颜色、纹理等内在状态)。 |
三、 行为型模式 (Behavioral Patterns)
目的:关注对象之间的职责分配和算法封装。它描述类或对象怎样交互以及怎样分配职责,使通信流程更清晰、灵活。
| 模式名称 | 核心功能简介 | 典型应用场景 |
|---|---|---|
| 策略模式 (Strategy) | 定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。策略模式使得算法可独立于使用它的客户而变化。 | 支付方式(支付宝、微信、银行卡)、排序算法(冒泡、快排、归并)、导航策略(驾车、步行、公交)。 |
| 模板方法模式 (Template Method) | 定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 | JDBC 模板、Servlet 的 doGet/doPost 方法、工作流框架中的流程骨架 。 |
| 观察者模式 (Observer) | 定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。 | 事件监听机制(如 Java Swing/AWT)、消息队列的发布-订阅模型、MVC 架构中模型与视图的关系 。 |
| 迭代器模式 (Iterator) | 提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。 | Java 集合框架中的 Iterator、遍历树形结构、分页查询数据 。 |
| 责任链模式 (Chain of Responsibility) | 使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。 | Java Web 中的 Filter 链、审批流程(经理->总监->CEO)、异常处理机制 。 |
| 命令模式 (Command) | 将一个请求封装为一个对象,从而使你可用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。 | 图形界面的菜单命令、事务操作、任务队列、宏命令(一键执行多个操作)。 |
| 备忘录模式 (Memento) | 在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态。这样以后就可将该对象恢复到原先保存的状态。 | 文本编辑器的撤销/重做、游戏存档、数据库事务回滚 。 |
| 状态模式 (State) | 允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。 | 订单状态(待支付、已发货、已完成)、TCP连接状态(建立连接、监听、关闭)、游戏角色状态(正常、中毒、眩晕)。 |
| 访问者模式 (Visitor) | 表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。 | 编译器语法树分析、对不同类型文件(XML, PDF)进行统一处理、报表生成器遍历复杂对象结构 。 |
| 中介者模式 (Mediator) | 用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。 | 聊天室程序(用户通过聊天室中介发送消息)、GUI 中组件间的通信(如点击按钮影响其他控件)、航班调度系统 。 |
| 解释器模式 (Interpreter) | 给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。 | 正则表达式引擎、SQL 解析、数学表达式计算器 。 |
四、 核心原则与价值
这些设计模式并非孤立存在,它们共同遵循并体现了面向对象设计的六大核心原则,如开闭原则(对扩展开放,对修改关闭) 、单一职责原则 、里氏替换原则 、依赖倒置原则 、接口隔离原则 和迪米特法则(最少知识原则)。掌握这些模式的价值在于:
- 提升代码复用性:提供了经过验证的解决方案模板。
- 增强代码可维护性:使代码结构清晰,职责分明。
- 提高系统可扩展性:便于应对需求变化,符合开闭原则。
- 促进团队沟通:提供了通用的设计词汇,便于交流。
五、 代码示例:策略模式
以下是一个简单的策略模式示例,演示如何封装不同的支付算法:
java
// 1. 策略接口
public interface PaymentStrategy {
void pay(double amount);
}
// 2. 具体策略类
public class CreditCardStrategy implements PaymentStrategy {
private String cardNumber;
private String cvv;
public CreditCardStrategy(String cardNumber, String cvv) {
this.cardNumber = cardNumber;
this.cvv = cvv;
}
@Override
public void pay(double amount) {
System.out.println("使用信用卡 " + cardNumber + " 支付了 " + amount + " 元。");
// 实际处理信用卡支付逻辑...
}
}
public class AlipayStrategy implements PaymentStrategy {
private String accountId;
public AlipayStrategy(String accountId) {
this.accountId = accountId;
}
@Override
public void pay(double amount) {
System.out.println("使用支付宝账户 " + accountId + " 支付了 " + amount + " 元。");
// 实际处理支付宝支付逻辑...
}
}
// 3. 上下文类(使用策略)
public class ShoppingCart {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy strategy) {
this.paymentStrategy = strategy;
}
public void checkout(double amount) {
if (paymentStrategy == null) {
throw new IllegalStateException("请先设置支付方式");
}
paymentStrategy.pay(amount);
}
}
// 4. 客户端使用
public class Client {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
double total = 100.0;
// 选择信用卡支付
cart.setPaymentStrategy(new CreditCardStrategy("1234-5678-9012-3456", "123"));
cart.checkout(total); // 输出:使用信用卡 1234-5678-9012-3456 支付了 100.0 元。
// 动态切换为支付宝支付
cart.setPaymentStrategy(new AlipayStrategy("user@example.com"));
cart.checkout(total); // 输出:使用支付宝账户 user@example.com 支付了 100.0 元。
}
}
此示例清晰地展示了策略模式如何将支付算法(策略)封装成独立对象,并使它们可以相互替换,从而让购物车(上下文)的支付行为变得灵活且易于扩展 。