设计模式教程
本文档系统介绍 Java 设计模式,涵盖创建型、结构型、行为型三大类共 23 种设计模式,包含完整的代码示例与应用场景说明。
💡 创作不易,请点个收藏关注!
目录
一、设计模式概述
1.1 什么是设计模式
设计模式(Design Pattern)是面向对象编程中针对常见问题的标准化解决方案,它不直接提供代码实现,而是通过定义类与对象的交互方式,提升代码的可复用性、可维护性和可扩展性。
1.2 设计模式的核心价值
- 避免重复造轮子:遵循业界公认的设计思路
- 降低代码复杂度:使设计结构更清晰
- 便于团队协作:开发人员可基于共同的模式语言沟通
- 提高代码质量:编写出更加灵活、可重用、易维护的代码
1.3 设计模式的分类
设计模式分为三大类:创建型模式 、结构型模式 和行为型模式。
1.3.1 创建型设计模式:对象的构建策略
创建型模式主要解决对象的创建问题,通过封装复杂的创建逻辑,使系统在创建对象时更灵活、解耦:
| 模式 | 核心作用 | 应用场景 |
|---|---|---|
| 工厂模式 | 封装对象创建细节 | 数据库连接工厂根据配置返回MySQL/Oracle连接 |
| 单例模式 | 控制对象创建的时机与方式 | 日志管理器、数据库连接池全局唯一实例 |
| 建造者模式 | 处理复杂对象的构建过程 | 创建不同配置的汽车(基础版、豪华版) |
| 原型模式 | 复制对象以避免重复初始化 | 游戏中复制NPC角色 |
1.3.2 结构型设计模式:对象与类的组合结构
结构型模式关注类和对象的组合方式,通过定义合理的结构,使系统更易于理解和维护:
| 模式 | 核心作用 | 应用场景 |
|---|---|---|
| 适配器模式 | 解决类之间的接口不兼容问题 | PS/2键盘适配USB接口、兼容旧系统 |
| 组合模式 | 构建层次化的对象结构 | 文件系统中文件与文件夹的树形关系 |
| 代理模式 | 代理其他对象以控制访问 | 网络图片虚拟代理(占位符→真实图片) |
| 桥接模式 | 动态替换对象的实现 | 将"颜色"和"形状"分离,避免类爆炸 |
| 外观模式 | 简化复杂对象的访问接口 | 电脑开机封装电源、CPU、内存等启动流程 |
| 装饰器模式 | 动态增强对象功能 | Java I/O流层层装饰 |
| 享元模式 | 共享细粒度对象 | 字符串常量池、棋子共享 |
1.3.3 行为型设计模式:对象间的交互与职责分配
行为型模式关注对象之间的通信和职责分配,通过定义合理的交互机制,使系统行为更清晰、可维护:
| 模式 | 核心作用 | 应用场景 |
|---|---|---|
| 观察者模式 | 定义对象间的通信机制 | 订阅号发布通知、事件监听系统 |
| 命令模式 | 将请求发送者与接收者解耦 | 撤销操作、日志记录、宏命令 |
| 策略模式 | 在不修改对象的情况下添加新功能 | 多种支付策略(支付宝、微信) |
| 责任链模式 | 处理请求的链式传递 | 审批流程(员工→组长→经理→总监) |
| 中介者模式 | 对象间的中介协调 | 聊天系统服务器协调客户端通信 |
| 备忘录模式 | 保存和恢复对象状态 | 游戏存档、事务回滚 |
| 状态模式 | 对象状态影响下的行为变化 | 订单状态机(待支付→已支付→已完成) |
| 模板方法模式 | 定义算法骨架,子类实现细节 | JDBC模板、HibernateCallback |
| 迭代器模式 | 顺序访问集合元素 | Java Iterator、ListIterator |
| 访问者模式 | 操作分离,添加新操作 | 编译器AST节点操作 |
| 解释器模式 | 定义语法解析 | SpEL表达式解析、正则表达式 |
1.4 GoF 23 种设计模式一览
| 类型 | 模式数量 | 具体模式 |
|---|---|---|
| 创建型 | 5 种 | 单例、工厂方法、抽象工厂、建造者、原型 |
| 结构型 | 7 种 | 适配器、桥接、组合、装饰器、外观、享元、代理 |
| 行为型 | 11 种 | 责任链、命令、迭代器、中介者、备忘录、观察者、状态、策略、模板方法、访问者、解释器 |
二、创建型模式(Creational Patterns)
创建型模式关注 对象的创建过程 ,它提供了一种创建对象的最佳方式,使得程序在创建对象时更加灵活和高效。
2.1 单例模式(Singleton Pattern)
单例模式确保一个类只有一个实例,并提供一个全局访问点。单例模式通常用于控制对共享资源的访问,例如数据库连接池、日志记录器等。
2.1.1 单例模式类图

单例模式时序图(以双重检查为例) :

真实应用场景:Spring 容器中的 Bean 默认单例

结构说明:
private static instance:持有唯一实例private Singleton():私有构造函数,防止外部 newpublic static getInstance():全局访问点
2.1.2 饿汉式单例

特点:类加载时立即创建,线程安全,无延迟加载
csharp
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton() {
// 私有构造函数,防止外部实例化
}
public static Singleton getInstance() {
return instance;
}
}
2.1.3 懒汉式单例

特点 :延迟加载,但 synchronized 有性能开销
csharp
public class Singleton {
private static Singleton instance;
private Singleton() {
// 私有构造函数,防止外部实例化
}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
}
2.1.4 双重检查锁定单例

特点 :volatile 防止指令重排 + 双重检查,线程安全且高效
csharp
public class Singleton {
private static Singleton instance;
private Singleton() {
// 私有构造函数,防止外部实例化
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
2.1.5 静态内部类单例

特点:利用类加载机制保证线程安全,延迟加载首选
csharp
public class Singleton {
private Singleton() {}
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
2.1.6 枚举单例(推荐最佳实践)
特点:线程安全、防反射、防反序列化,Effective Java 推荐
csharp
public enum Singleton {
INSTANCE;
public void doSomething() {}
}
// 使用
Singleton instance = Singleton.INSTANCE;
| 实现方式 | 线程安全 | 延迟加载 | 防反射 | 防反序列化 |
|---|---|---|---|---|
| 饿汉式 | ✅ | ❌ | ❌ | ❌ |
| 懒汉式 | ✅ | ✅ | ❌ | ❌ |
| 双重检查 | ✅ | ✅ | ❌ | ❌ |
| 静态内部类 | ✅ | ✅ | ❌ | ❌ |
| 枚举式 | ✅ | ✅ | ✅ | ✅ |
💡 Spring 中的应用 :Spring 默认 Bean 为单例,通过
DefaultSingletonBeanRegistry管理。
2.2 工厂模式(Factory Pattern)
工厂模式提供了一种创建对象的最佳方式,它将对象的创建过程封装在一个工厂类中,使得客户端代码不需要关心对象的创建细节。
💡 Spring 中的应用 :
BeanFactory是 Spring 的核心工厂,FactoryBean接口用于创建复杂 Bean。 简单工厂模式通过一个工厂类来创建对象,客户端只需要调用工厂类的方法即可获取对象。
适用场景:提供一个静态方法,根据输入参数创建不同的对象。。
优点:客户端只需要知道工厂类,就可以创建对象,不需要关心对象的创建细节。
缺点:当需要创建的对象种类较多时,工厂类的代码会变得非常复杂,不易维护。
typescript
public class SimpleFactory {
public static Product createProduct(String type) {
if ("A".equals(type)) {
return new ProductA();
} else if ("B".equals(type)) {
return new ProductB();
}
return null;
}
}
2.2.2 工厂方法模式

工厂方法模式时序图:

Spring BeanFactory 应用场景:

特点:子类决定创建哪个具体产品,开闭原则
适用场景:定义一个工厂接口,具体工厂实现类负责创建具体产品。
优点:客户端只需要知道工厂接口,就可以创建对象,不需要关心对象的创建细节。
缺点:当需要创建的对象种类较多时,需要定义较多的工厂类,代码量会成倍增加。
typescript
public interface Factory {
Product createProduct();
}
public class ProductAFactory implements Factory {
@Override
public Product createProduct() {
return new ProductA();
}
}
public class ProductBFactory implements Factory {
@Override
public Product createProduct() {
return new ProductB();
}
}
使用:
ini
Factory factory = new ProductAFactory();
Product product = factory.createProduct();
2.2.3 抽象工厂模式
抽象工厂模式通过定义一个抽象工厂接口,让子类决定实例化哪一个具体工厂类。客户端只需要知道抽象工厂接口,就可以创建对象。
适用场景:创建一系列相关的产品族,提供一个统一的接口。
优点:管理多个产品族,增强系统的一致性。
缺点:系统复杂性增加,尤其是产品族较多时。
typescript
public interface AbstractFactory {
ProductA createProductA();
ProductB createProductB();
}
public class ConcreteFactory1 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new ConcreteProductA1();
}
@Override
public ProductB createProductB() {
return new ConcreteProductB1();
}
}
public class ConcreteFactory2 implements AbstractFactory {
@Override
public ProductA createProductA() {
return new ConcreteProductA2();
}
@Override
public ProductB createProductB() {
return new ConcreteProductB2();
}
}
使用:
ini
AbstractFactory factory = new ConcreteFactory1();
ProductA productA = factory.createProductA();
ProductB productB = factory.createProductB();
2.3 建造者模式(Builder Pattern)
建造者模式将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式通常用于创建复杂对象,特别是当对象的构造过程需要多个步骤,并且每个步骤都可能影响最终的对象表示时。
适用场景:当创建复杂对象时,需要将对象的构造过程与它的表示分离,使得同样的构造过程可以创建不同的表示。
优点:将对象的构造过程与它的表示分离,使得同样的构造过程可以创建不同的表示。
缺点:当对象构造过程较为简单时,使用建造者模式可能会增加代码的复杂性。
2.3.1 建造者模式的核心结构
- 产品(Product):表示要创建的复杂对象。
- 抽象建造者(Builder):定义创建复杂对象的各个步骤,并声明一个返回复杂对象的方法。
- 具体建造者(ConcreteBuilder):实现抽象建造者中定义的各个步骤,并返回一个复杂对象。
- 指挥者(Director):负责调用具体建造者中的各个步骤,并返回一个复杂对象。(可选,实践中常被简化)
typescript
public class Product {
private String partA;
private String partB;
private String partC;
public void setPartA(String partA) {
this.partA = partA;
}
public void setPartB(String partB) {
this.partB = partB;
}
public void setPartC(String partC) {
this.partC = partC;
}
public void show() {
System.out.println("Product: " + partA + " " + partB + " " + partC);
}
}
public abstract class Builder {
protected Product product = new Product();
public abstract void buildPartA();
public abstract void buildPartB();
public abstract void buildPartC();
public Product getResult() {
return product;
}
}
public class ConcreteBuilder extends Builder {
@Override
public void buildPartA() {
product.setPartA("PartA");
}
@Override
public void buildPartB() {
product.setPartB("PartB");
}
@Override
public void buildPartC() {
product.setPartC("PartC");
}
}
public class Director {
private Builder builder;
public Director(Builder builder) {
this.builder = builder;
}
public Product construct() {
builder.buildPartA();
builder.buildPartB();
builder.buildPartC();
return builder.getResult();
}
}
使用:
ini
Builder builder = new ConcreteBuilder();
Director director = new Director(builder);
Product product = director.construct();
product.show();
2.4 原型模式(Prototype Pattern)
原型模式通过复制现有对象来创建新对象,而不是通过实例化类。它通过实现一个克隆方法来复制对象,使得客户端代码不需要关心对象的创建细节。
适用场景:当需要创建的对象与现有对象相似,并且创建过程较为复杂时,可以使用原型模式。
优点:通过复制现有对象来创建新对象,不需要关心对象的创建细节。
缺点:当对象结构较为复杂时,复制过程可能较为耗时。深拷贝开销较大。
2.4.1 原型模式的核心结构

特点 :通过 clone() 方法复制对象,避免重复创建
2.4.2 浅拷贝与深拷贝
浅拷贝:复制对象时,只复制对象本身,不复制对象内部的引用对象。修改克隆对象的 会影响原始对象。
深拷贝:复制对象时,不仅复制对象本身,还复制对象内部的引用对象。修改克隆对象的 不会影响原始对象。
通过 Cloneable 接口和 clone() 方法实现原型模式,分为 浅拷贝 和 深拷贝 两种形式。 浅拷贝:默认实现,只复制对象本身,不复制对象内部的引用对象。
typescript
// 1. 实现 Cloneable 接口
class Document implements Cloneable {
private String title;
private String[] content;
public Document(String title, String[] content) {
this.title = title;
this.content = content;
}
// 2. 重写 clone() 方法
@Override
public Document clone() throws CloneNotSupportedException {
return (Document) super.clone(); // 默认浅拷贝
}
// Getter/Setter 略
}
使用:
ini
public static void main(String[] args) throws CloneNotSupportedException {
String[] content = {"test1", "test2"};
Document document = new Document("test", content);
Document clone = document.clone();
System.out.println(clone);
clone.getContent()[0] = "test3";
System.out.println(document);
}
打印结果:
ini
title='test', content='test1:::test2'
title='test', content='test3:::test2'
可以看到,修改克隆对象的 content 会影响原始对象,因为它们引用的是同一个数组。
深拷贝:复制对象时,不仅复制对象本身,还复制对象内部的引用对象。修改克隆对象的 不会影响原始对象。
java
// 1. 实现 Cloneable 接口
class Document implements Cloneable {
private String title;
private String[] content;
public Document(String title, String[] content) {
this.title = title;
this.content = content;
}
// 2. 重写 clone() 方法
@Override
public Document clone() throws CloneNotSupportedException {
Document clone = (Document) super.clone();
clone.content = this.content.clone(); // 深拷贝
return clone;
}
// Getter/Setter 略
}
使用:
ini
public static void main(String[] args) throws CloneNotSupportedException {
String[] content = {"test1", "test2"};
Document document = new Document("test", content);
Document clone = document.clone();
System.out.println(clone);
clone.getContent()[0] = "test3";
System.out.println(document);
}
打印结果:
ini
DeepDocument{title='test', content=[test1, test2]}
DeepDocument{title='test', content=[test1, test2]}
可以看到,修改克隆对象的 content 不会影响原始对象,因为它们引用的是不同的数组。
三、结构型模式(Structural Patterns)
结构型模式关注于如何将对象和类组合成更大的结构,以获得更好的结构化代码。它们通过继承和组合来创建灵活和可重用的代码。
3.1 适配器模式(Adapter Pattern)
适配器模式将一个类的接口转换成客户端希望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以一起工作。它通过创建一个适配器类来实现接口转换。
适用场景:当需要将一个类的接口转换成客户端希望的另一个接口时,可以使用适配器模式。
优点:通过适配器类实现接口转换,使得原本由于接口不兼容而不能一起工作的类可以一起工作。
缺点:增加了系统的复杂性,可能降低代码的可读性。
3.1.1 适配器模式的核心结构
- 目标接口 (Target):定义客户端所期待的接口。
- 适配者类 (Adaptee):需要适配的类。
- 适配器类 (Adapter):将适配者类的接口转换成目标接口。
- 客户端 (Client):使用目标接口与适配器类进行交互。
3.1.2 类适配器与对象适配器
类适配器:通过继承适配者类,实现目标接口,从而实现接口转换。
- 通过继承适配者类和实现目标接口来实现。
- 适用于适配者类只有一个需要适配的方法的情况。
typescript
// 目标接口
public interface Target {
void request();
}
// 适配者类
public class Adaptee {
public void specificRequest() {
System.out.println("Adaptee specificRequest");
}
}
// 类适配器
public class ClassAdapter extends Adaptee implements Target {
@Override
public void request() {
specificRequest();
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Target target = new ClassAdapter();
target.request();
}
}
对象适配器:通过组合适配者类,实现目标接口,从而实现接口转换。
- 通过组合适配者类和实现目标接口来实现。
- 适用于适配者类有多个需要适配的方法的情况。
csharp
// 目标接口
public interface Target {
void request();
}
// 适配者类
public class Adaptee {
public void specificRequest() {
System.out.println("Adaptee specificRequest");
}
}
// 对象适配器
public class ObjectAdapter implements Target {
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Target target = new ObjectAdapter(new Adaptee());
target.request();
}
}
3.2 装饰器模式(Decorator Pattern)
装饰器模式动态地给对象添加新的职责,而不改变其结构。它通过组合的方式实现,使得对象可以在运行时动态地添加或删除职责。
适用场景:当需要动态地给对象添加新的职责时,可以使用装饰器模式。
优点:通过装饰器类动态地添加或删除职责,使得对象可以在运行时灵活地改变行为。
缺点:增加了系统的复杂性,可能降低代码的可读性。
3.2.1 装饰器模式的类图

核心思想:Decorator 持有 Component,通过组合实现功能叠加
装饰器模式时序图:

Java I/O 装饰器真实场景:

| 层级 | 角色 | 增强功能 |
|---|---|---|
| FileInputStream | 真实组件 | 文件读取 |
| DataInputStream | 装饰器 | 数据类型解析 |
| BufferedInputStream | 装饰器 | 缓冲加速 |
typescript
// 组件接口
public interface Coffee {
double getCost(); // 获取价格
String getDesc(); // 获取描述
}
// 具体组件类
public class SimpleCoffee implements Coffee {
@Override
public double getCost() {
return 10;
}
@Override
public String getDesc() {
return "Simple Coffee";
}
}
// 装饰器抽象类
public abstract class CoffeeDecorator implements Coffee {
protected Coffee coffee;
public CoffeeDecorator(Coffee coffee) {
this.coffee = coffee;
}
@Override
public double getCost() {
return coffee.getCost();
}
@Override
public String getDesc() {
return coffee.getDesc();
}
}
// 具体装饰器类
public class MilkCoffeeDecorator extends CoffeeDecorator {
public MilkCoffeeDecorator(Coffee coffee) {
super(coffee);
}
@Override
public double getCost() {
return super.getCost() + 2;
}
@Override
public String getDesc() {
return super.getDesc() + ", with Milk";
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Coffee coffee = new SimpleCoffee();
System.out.println(coffee.getDesc() + " - ¥" + coffee.getCost());
Coffee milkCoffee = new MilkCoffeeDecorator(coffee);
System.out.println(milkCoffee.getDesc() + " - ¥" + milkCoffee.getCost());
}
}
💡 Java I/O 源码解析 :Java I/O 流大量使用装饰器模式。以
BufferedInputStream为例:// 典型使用:层层装饰 InputStream in = new BufferedInputStream( new DataInputStream( new FileInputStream("test.txt") ) ); // FileInputStream:真实组件 // DataInputStream:装饰器(增强readInt等方法) // BufferedInputStream:装饰器(增加缓冲功能)
继承
FilterInputStream的类都是装饰器,通过组合方式动态增强read()/write()功能。💡 Spring中的应用 :Spring
TransactionAwareCacheDecorator、Spring CloudDecoratingWebhook等都使用了装饰器模式。
代理模式为其他对象提供一种代理以控制对这个对象的访问。它通过创建一个代理对象来控制对原始对象的访问,从而实现一些额外的功能,如延迟初始化、权限控制、缓存等。
适用场景:当需要控制对原始对象的访问时,可以使用代理模式。
优点:通过代理对象控制对原始对象的访问,可以实现一些额外的功能,如延迟初始化、权限控制、缓存等。
缺点:增加了系统的复杂性,可能降低代码的可读性。
3.3.1 代理模式的类图

JDK 动态代理 vs CGLIB:

| 对比 | JDK动态代理 | CGLIB动态代理 |
|---|---|---|
| 原理 | 反射,Proxy.newInstance | 继承,重写方法 |
| 限制 | 只能代理接口 | 可代理类 |
| 性能 | 反射调用,较慢 | 字节码生成较快 |
Spring AOP 代理应用场景:

MyBatis 懒加载时序图:

静态代理:在编译时就已经确定了代理对象和真实对象的关系。
- 通过继承或实现主题接口来实现。
- 适用于代理对象和真实对象的关系比较稳定的情况。
typescript
// 主题接口
public interface Image {
void display();
}
// 真实对象类
public class RealImage implements Image {
private String filename;
public RealImage(String filename) {
this.filename = filename;
loadFromDisk();
}
private void loadFromDisk() {
System.out.println("Loading " + filename);
}
@Override
public void display() {
System.out.println("Displaying " + filename);
}
}
// 代理类
public class ProxyImage implements Image {
private RealImage realImage;
private String filename;
public ProxyImage(String filename) {
this.filename = filename;
}
@Override
public void display() {
if (realImage == null) {
realImage = new RealImage(filename);
}
realImage.display();
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Image image = new ProxyImage("test.jpg");
// 图像将从磁盘加载
image.display();
System.out.println("");
// 图像不需要从磁盘加载
image.display();
}
}
动态代理:在运行时动态地创建代理对象。
- 通过 Java 的反射机制实现。
- 适用于代理对象和真实对象的关系比较灵活的情况。
- 动态代理只能代理接口,不能代理类。
- 动态代理的实现方式有两种:JDK 动态代理和 CGLIB 动态代理。
- JDK 动态代理:通过实现 InvocationHandler 接口,并重写 invoke 方法来实现。
- CGLIB 动态代理:通过继承被代理类,并重写其方法来实现。
JDK 动态代理示例
typescript
// 主题接口
public interface Hello {
void sayHello();
}
// 真实对象类
public class RealHello implements Hello {
@Override
public void sayHello() {
System.out.println("Hello, World!");
}
}
// 代理类
public class DynamicProxy implements InvocationHandler {
private Object target;
public DynamicProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After method: " + method.getName());
return result;
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Hello hello = new RealHello();
DynamicProxy proxy = new DynamicProxy(hello);
Hello proxyHello = (Hello) Proxy.newProxyInstance(hello.getClass().getClassLoader(), hello.getClass().getInterfaces(), proxy);
proxyHello.sayHello();
}
}
CGLIB 动态代理示例
typescript
// 真实对象类
public class RealHello {
public void sayHello() {
System.out.println("Hello, World!");
}
}
// 代理类
public class CglibProxy implements MethodInterceptor {
private Object target;
public CglibProxy(Object target) {
this.target = target;
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before method: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After method: " + method.getName());
return result;
}
}
// 客户端
public class Client {
public static void main(String[] args) {
RealHello hello = new RealHello();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealHello.class);
enhancer.setCallback(new CglibProxy(hello));
RealHello proxyHello = (RealHello) enhancer.create();
proxyHello.sayHello();
}
}
💡 Spring 中的应用:Spring AOP 基于代理模式实现,JDK 动态代理用于实现接口的类,CGLIB 用于未实现接口的类。
3.3.3 MyBatis 懒加载原理
MyBatis 的懒加载(延迟加载)正是基于代理模式实现的。当查询用户及其关联的订单时,订单信息并不会立即加载,而是在真正访问订单属性时才发起 SQL 查询。
typescript
// MyBatis 懒加载原理
// 1. Mapper 代理对象结构
public class UserMapperProxy implements InvocationHandler {
private Object target; // 真实 Mapper
private Map<String, LazyLoader> lazyLoaderMap; // 延迟加载器
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 如果是 getter 方法且配置了懒加载
if ("getOrders".equals(method.getName())) {
return lazyLoader.load();
}
return method.invoke(target, args);
}
}
// 2. 延迟加载器:触发 SQL 查询
public class LazyLoader {
private String statementId; // SQL 映射 ID
private Object target; // 目标对象
public Object load() {
// 执行 SELECT 查询
return sqlSession.selectOne(statementId, target);
}
}
// 3. 使用示例
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.selectUserById(1); // 只查询用户
// 真正访问 orders 时才触发 SQL
List<Order> orders = user.getOrders(); // 触发延迟加载
💡 核心原理 :MyBatis 使用 Javassist 或 CGLIB 生成代理对象,重写 getter 方法,在首次调用时触发 SQL 查询并替换代理对象为真实数据。
💡 相关配置 :
<setting name="lazyLoadingEnabled" value="true"/>开启懒加载。
3.4 组合模式(Composite Pattern)
组合模式将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
适用场景:当需要表示对象的部分-整体层次结构,并且希望用户能够一致地对待单个对象和组合对象时,可以使用组合模式。
优点:使得用户可以一致地对待单个对象和组合对象,简化了用户的使用。
缺点:增加了系统的复杂性,可能降低代码的可读性。
3.4.1 组合模式的类图

树形结构示例:

特点:Leaf 和 Composite 实现同一接口,Composite 持有 Leaf 列表
java
// 组件接口
public interface Component {
void add(Component component);
void remove(Component component);
Component getChild(int index);
void operation();
}
// 叶子组件类
public class Leaf implements Component {
private String name;
public Leaf(String name) {
this.name = name;
}
@Override
public void add(Component component) {
throw new UnsupportedOperationException("Leaf node does not support add operation");
}
@Override
public void remove(Component component) {
throw new UnsupportedOperationException("Leaf node does not support remove operation");
}
@Override
public Component getChild(int index) {
throw new UnsupportedOperationException("Leaf node does not support getChild operation");
}
@Override
public void operation() {
System.out.println("Leaf node operation: " + name);
}
}
// 组合组件类
public class Composite implements Component {
private List<Component> children = new ArrayList<>();
@Override
public void add(Component component) {
children.add(component);
}
@Override
public void remove(Component component) {
children.remove(component);
}
@Override
public Component getChild(int index) {
return children.get(index);
}
@Override
public void operation() {
System.out.println("Composite node operation");
for (Component child : children) {
child.operation();
}
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Component root = new Composite();
Component leaf1 = new Leaf("Leaf 1");
Component leaf2 = new Leaf("Leaf 2");
Component composite1 = new Composite();
Component leaf3 = new Leaf("Leaf 3");
Component leaf4 = new Leaf("Leaf 4");
composite1.add(leaf3);
composite1.add(leaf4);
root.add(leaf1);
root.add(leaf2);
root.add(composite1);
root.operation();
}
}
3.5 桥接模式(Bridge Pattern)
桥接模式将抽象部分与实现部分分离,使它们可以独立变化。
适用场景:当一个类存在两个独立变化的维度,且这两个维度都需要扩展时。
优点:分离抽象和实现,支持独立扩展,符合开闭原则。
缺点:增加系统复杂度。
java
// 实现部分接口
public interface Color {
String fill();
}
// 具体实现
public class Red implements Color {
@Override
public String fill() {
return "Red";
}
}
public class Blue implements Color {
@Override
public String fill() {
return "Blue";
}
}
// 抽象部分
public abstract class Shape {
protected Color color; // 桥接
public Shape(Color color) {
this.color = color;
}
public abstract void draw();
}
// 具体抽象
public class Circle extends Shape {
public Circle(Color color) {
super(color);
}
@Override
public void draw() {
System.out.println("Drawing Circle with " + color.fill());
}
}
// 使用
Shape redCircle = new Circle(new Red());
redCircle.draw();
3.6 外观模式(Facade Pattern)
外观模式为复杂的子系统提供一个统一的接口,使子系统更易使用。
适用场景:当需要为一个复杂的子系统提供一个简单的接口时。
优点:简化客户端代码,隐藏子系统复杂性。
缺点:可能限制灵活性。
csharp
// 子系统类
public class CPU {
public void start() { System.out.println("CPU starting..."); }
public void execute() { System.out.println("CPU executing..."); }
}
public class Memory {
public void load() { System.out.println("Memory loading..."); }
}
public class Disk {
public void read() { System.out.println("Disk reading..."); }
}
// 外观类
public class ComputerFacade {
private CPU cpu;
private Memory memory;
private Disk disk;
public ComputerFacade() {
this.cpu = new CPU();
this.memory = new Memory();
this.disk = new Disk();
}
public void start() {
cpu.start();
memory.load();
disk.read();
cpu.execute();
}
}
// 使用
ComputerFacade computer = new ComputerFacade();
computer.start();
3.7 享元模式(Flyweight Pattern)
3.7.1 享元模式的类图

两种状态:
- 内部状态 (Intrinsic) :可共享,存储在 Flyweight 中
- 外部状态 (Extrinsic) :不可共享,由客户端传入
享元模式通过共享对象来减少内存使用,适用于大量细粒度对象的场景。
适用场景:需要创建大量相似对象,造成内存消耗过大。
优点:大幅减少对象数量,节省内存。
缺点:增加了系统复杂度,需要分离内部/外部状态。
arduino
// 享元工厂
public class TreeFactory {
private static Map<String, TreeType> treeTypes = new HashMap<>();
public static TreeType getTreeType(String name, String color) {
String key = name + "-" + color;
if (!treeTypes.containsKey(key)) {
treeTypes.put(key, new TreeType(name, color));
}
return treeTypes.get(key);
}
}
// 享元对象
public class TreeType {
private String name;
private String color;
public TreeType(String name, String color) {
this.name = name;
this.color = color;
}
public void draw(int x, int y) {
System.out.println("Drawing " + color + " " + name + " at (" + x + "," + y + ")");
}
}
// 外部状态(位置),内部状态(类型)
public class Tree {
private int x, y;
private TreeType type;
public Tree(int x, int y, TreeType type) {
this.x = x;
this.y = y;
this.type = type;
}
public void draw() {
type.draw(x, y);
}
}
// 使用:创建 10000 棵树,只需存储 2 种 TreeType
TreeFactory factory = new TreeFactory();
for (int i = 0; i < 10000; i++) {
Tree tree = new Tree(i, i, TreeFactory.getTreeType("松树", "绿色"));
tree.draw();
}
四、行为型模式(Behavioral Patterns)
行为型模式关注对象之间的交互和通信,以及对象如何响应外部事件。
4.1 策略模式(Strategy Pattern)
策略模式定义了一系列算法,并将每个算法封装起来,使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户。核心思想是定义算法,封装变化,委托执行。
适用场景:当算法存在多个变体,并且这些变体可以相互替换时,可以使用策略模式。
优点:算法可以独立于使用算法的客户进行变化,使得算法的变化不会影响到客户的使用。
缺点:如果算法非常多,会增加系统的复杂性。
4.1.1 策略模式的类图

策略模式时序图:

Spring 支付策略应用场景:

策略 vs 桥接对比:
| 对比 | 策略模式 | 桥接模式 |
|---|---|---|
| 目的 | 算法切换 | 抽象与实现分离 |
| 角度 | 行为角度 | 结构角度 |
| 变化 | 算法独立变化 | 两维度独立变化 |
4.1.2 策略模式示例
typescript
// 策略接口
public interface Strategy {
void execute();
}
// 具体策略类
public class ConcreteStrategyA implements Strategy {
@Override
public void execute() {
System.out.println("Executing strategy A");
}
}
public class ConcreteStrategyB implements Strategy {
@Override
public void execute() {
System.out.println("Executing strategy B");
}
}
// 上下文类
public class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public void executeStrategy() {
strategy.execute();
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Context context = new Context(new ConcreteStrategyA());
context.executeStrategy();
context.setStrategy(new ConcreteStrategyB());
context.executeStrategy();
}
}
💡 Spring 中的应用 :
Spring Retry中的RetryPolicy、ORM 的多数据源切换等场景。
4.2 观察者模式(Observer Pattern)
观察者模式定义了对象之间的一对多依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。核心思想是定义对象之间的依赖关系,委托通知和更新。
4.2.1 观察者模式的类图

核心思想:Subject 持有 Observer 列表,状态变化时通知所有观察者
观察者模式时序图:

Spring 事件机制应用场景:

观察者 vs 发布订阅:
| 对比 | 观察者模式 | 发布订阅模式 |
|---|---|---|
| 耦合度 | 观察者直接绑定主题 | 通过消息代理解耦 |
| 通信方式 | 同步调用 | 可同步/异步 |
| 典型实现 | Java Observable | Kafka/RabbitMQ |
| 扩展性 | 困难,需修改主题 | 易扩展,新增订阅者 |
适用场景:当一个对象的状态发生变化时,需要通知其他对象进行相应的更新时,可以使用观察者模式。
优点:解耦了对象之间的依赖关系,使得对象之间的通信更加灵活。
缺点:如果观察者非常多,会增加系统的复杂性。
4.2.1 观察者模式的核心结构
- 抽象主题 (Subject):定义了添加、删除和通知观察者的方法。
- 具体主题 (ConcreteSubject):实现了抽象主题,维护了一个观察者列表,并实现了添加、删除和通知观察者的方法。
- 抽象观察者 (Observer):定义了更新方法,当主题状态发生变化时,被通知的对象会调用这个方法。
- 具体观察者 (ConcreteObserver):实现了抽象观察者,维护了一个主题对象的引用,并实现了更新方法。
- 客户端 (Client):使用抽象主题和抽象观察者进行交互。
4.2.2 观察者模式示例
java
// 抽象主题
public interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
// 具体主题
public class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
private int state;
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
notifyObservers();
}
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update();
}
}
}
// 抽象观察者
public interface Observer {
void update();
}
// 具体观察者
public class ConcreteObserver implements Observer {
private String name;
private ConcreteSubject subject;
public ConcreteObserver(String name, ConcreteSubject subject) {
this.name = name;
this.subject = subject;
subject.registerObserver(this);
}
@Override
public void update() {
System.out.println(name + " received update: " + subject.getState());
}
}
// 客户端
public class Client {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
ConcreteObserver observer1 = new ConcreteObserver("Observer 1", subject);
ConcreteObserver observer2 = new ConcreteObserver("Observer 2", subject);
subject.setState(10);
subject.setState(20);
}
}
💡 Spring 中的应用 :Spring 事件机制 (
ApplicationEventPublisher) 基于观察者模式,用于组件间解耦通信。 责任链模式将请求的发送者和接收者解耦,使得多个对象都有机会处理这个请求。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。核心思想是定义请求处理链,委托处理请求。
适用场景:当一个请求需要多个对象进行处理,并且这些对象的处理顺序是可变的时,可以使用责任链模式。
优点:解耦了请求的发送者和接收者,使得请求的处理更加灵活。
缺点:如果责任链非常长,会增加系统的复杂性。
4.3.1 责任链模式的类图

链式传递流程:

责任链模式时序图:

Spring MVC 过滤器链应用场景:

责任链 vs 策略对比:
| 对比 | 责任链模式 | 策略模式 |
|---|---|---|
| 处理方式 | 链中处理器决定是否处理 | Context 决定使用哪个策略 |
| 链式传递 | 可传递或不传递 | 不传递,自主执行 |
| 典型场景 | 多级审批、日志切面 | 支付方式、排序算法 |
java
// 抽象处理者
public abstract class Handler {
protected Handler successor;
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public abstract void handleRequest(int request);
}
// 具体处理者
public class ConcreteHandlerA extends Handler {
@Override
public void handleRequest(int request) {
if (request >= 0 && request <= 10) {
System.out.println("ConcreteHandlerA handled request: " + request);
} else if (successor != null) {
successor.handleRequest(request);
}
}
}
public class ConcreteHandlerB extends Handler {
@Override
public void handleRequest(int request) {
if (request > 10 && request <= 20) {
System.out.println("ConcreteHandlerB handled request: " + request);
} else if (successor != null) {
successor.handleRequest(request);
}
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Handler handlerA = new ConcreteHandlerA();
Handler handlerB = new ConcreteHandlerB();
handlerA.setSuccessor(handlerB);
handlerA.handleRequest(5);
handlerA.handleRequest(15);
handlerA.handleRequest(25);
}
}
4.4 状态模式(State Pattern)
状态模式允许对象在其内部状态改变时改变其行为。核心思想是定义对象的状态,委托状态改变行为。
适用场景:当一个对象的行为取决于它的状态,并且状态改变时需要改变行为时,可以使用状态模式。
优点:将对象的状态和行为分离,使得对象的状态改变更加灵活。
缺点:如果状态非常多,会增加系统的复杂性。
4.4.1 状态模式的类图

核心思想:Context 委托 State 处理,State 可切换 Context 状态
状态模式时序图:

订单状态机应用场景:

状态 vs 策略对比:
| 对比 | 状态模式 | 策略模式 |
|---|---|---|
| 状态切换 | 状态对象自己切换 | 客户端主动切换 |
| 上下文感知 | 状态知道上下文 | 策略不知道上下文 |
| 适用场景 | 状态机、流程控制 | 算法切换 |
typescript
// 抽象状态
public interface State {
void handle(Context context);
}
// 具体状态
public class ConcreteStateA implements State {
@Override
public void handle(Context context) {
System.out.println("Handling state A");
context.setState(new ConcreteStateB());
}
}
public class ConcreteStateB implements State {
@Override
public void handle(Context context) {
System.out.println("Handling state B");
context.setState(new ConcreteStateA());
}
}
// 环境
public class Context {
private State state;
public Context() {
this.state = new ConcreteStateA();
}
public void setState(State state) {
this.state = state;
}
public void request() {
state.handle(this);
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Context context = new Context();
context.request();
context.request();
context.request();
}
}
4.5 模板方法模式(Template Method Pattern)
模板方法模式定义了一个算法的骨架,并将一些步骤延迟到子类中实现。核心思想是定义算法的骨架,委托子类实现算法的细节。
适用场景:当多个子类需要执行相同的算法,但算法的具体步骤有所不同时,可以使用模板方法模式。
优点:将算法的骨架和细节分离,使得算法更加灵活。
缺点:如果算法的骨架非常复杂,会增加系统的复杂性。
4.5.1 模板方法模式的类图

流程图示:

模板方法模式时序图:

JDBC 模板方法应用场景:

特点:父类定义骨架,子类实现具体步骤
csharp
// 抽象类
public abstract class AbstractClass {
public void templateMethod() {
step1();
step2();
step3();
}
protected abstract void step1();
protected abstract void step2();
protected void step3() {
System.out.println("Default implementation of step 3");
}
}
// 具体类
public class ConcreteClassA extends AbstractClass {
@Override
protected void step1() {
System.out.println("ConcreteClassA step 1");
}
@Override
protected void step2() {
System.out.println("ConcreteClassA step 2");
}
}
public class ConcreteClassB extends AbstractClass {
@Override
protected void step1() {
System.out.println("ConcreteClassB step 1");
}
@Override
protected void step2() {
System.out.println("ConcreteClassB step 2");
}
}
// 客户端
public class Client {
public static void main(String[] args) {
AbstractClass classA = new ConcreteClassA();
classA.templateMethod();
AbstractClass classB = new ConcreteClassB();
classB.templateMethod();
}
}
4.6 命令模式(Command Pattern)
💡 Spring 中的应用 :Spring MVC 的
HandlerAdapter将请求封装为命令对象;Spring JDBC 的PreparedStatement使用命令模式封装 SQL 执行。
4.6.1 命令模式的类图

命令模式时序图:

特点:请求封装为对象,支持撤销/重做
适用场景:需要支持撤销操作、日志记录、请求排队等。
csharp
// 命令接口
public interface Command {
void execute();
void undo();
}
// 具体命令
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
@Override
public void undo() {
light.off();
}
}
// 遥控器
public class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void pressButton() {
command.execute();
}
public void pressUndo() {
command.undo();
}
}
// 使用
Light light = new Light();
Command lightOn = new LightOnCommand(light);
RemoteControl remote = new RemoteControl();
remote.setCommand(lightOn);
remote.pressButton();
remote.pressUndo();
4.7 中介者模式(Mediator Pattern)
💡 Spring 中的应用 :Spring MVC 的
DispatcherServlet充当前端控制器,统一协调各组件(如 HandlerMapping、ViewResolver)的交互。
中介者模式通过一个中介对象封装对象间的交互。
适用场景:对象间交互复杂,形成网状结构时。
typescript
// 中介者接口
public interface Mediator {
void notify(Component sender, String event);
}
// 具体中介者
public class ConcreteMediator implements Mediator {
private ComponentA componentA;
private ComponentB componentB;
public void setComponents(ComponentA a, ComponentB b) {
this.componentA = a;
this.componentB = b;
}
@Override
public void notify(Component sender, String event) {
if (sender == componentA && "A".equals(event)) {
componentB.doThis();
}
}
}
// 组件
public abstract class Component {
protected Mediator mediator;
public void setMediator(Mediator mediator) {
this.mediator = mediator;
}
}
public class ComponentA extends Component {
public void doA() {
mediator.notify(this, "A");
}
}
public class ComponentB extends Component {
public void doThis() {
System.out.println("ComponentB handles");
}
}
4.8 备忘录模式(Memento Pattern)
💡 Spring 中的应用 :Spring WebFlow 的
FlowDefinition使用备忘录模式保存和恢复流程状态。
备忘录模式在不破坏封装性的前提下保存和恢复对象状态。
适用场景:需要保存对象历史状态,支持撤销功能。
typescript
// 备忘录
public class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
// 原发器
public class Originator {
private String state;
public void setState(String state) {
this.state = state;
}
public String getState() {
return state;
}
public Memento saveState() {
return new Memento(state);
}
public void restoreState(Memento memento) {
this.state = memento.getState();
}
}
// 管理者
public class Caretaker {
private List<Memento> mementos = new ArrayList<>();
public void addMemento(Memento memento) {
mementos.add(memento);
}
public Memento getMemento(int index) {
return mementos.get(index);
}
}
// 使用
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
originator.setState("State1");
caretaker.addMemento(originator.saveState());
originator.setState("State2");
caretaker.addMemento(originator.saveState());
originator.setState("State3");
originator.restoreState(caretaker.getMemento(0)); // 撤销到 State1
4.9 迭代器模式(Iterator Pattern)
💡 Spring 中的应用 :Spring 的
ResultSetExtractor、RowMapper封装了 JDBC 结果集的遍历逻辑。
4.9.1 迭代器模式的类图

核心思想:封装遍历逻辑,客户端不暴露集合内部结构
适用场景:需要统一方式遍历不同集合类型。
优点:封装集合遍历逻辑,支持多种遍历方式。
typescript
// 迭代器接口
public interface Iterator<T> {
boolean hasNext();
T next();
}
// 集合接口
public interface Container<T> {
Iterator<T> getIterator();
}
// 具体集合
public class NameRepository implements Container<String> {
private String[] names = {"Alice", "Bob", "Charlie"};
@Override
public Iterator<String> getIterator() {
return new NameIterator();
}
private class NameIterator implements Iterator<String> {
private int index = 0;
@Override
public boolean hasNext() {
return index < names.length;
}
@Override
public String next() {
return names[index++];
}
}
}
// 使用
NameRepository repository = new NameRepository();
for (Iterator<String> iter = repository.getIterator(); iter.hasNext();) {
System.out.println(iter.next());
}
4.10 访问者模式(Visitor Pattern)
💡 Spring 中的应用 :Spring Security 的
AccessDecisionVoter使用访问者模式评估访问权限;Jackson 序列化框架处理不同类型时也用到访问者思想。
4.10.1 访问者模式的类图

访问者模式时序图(双重分派) :

双重分派机制:

访问者模式特点:
- Element 稳定:添加新元素困难
- Visitor 易扩展:添加新操作容易
- 双重分派:运行时根据两个类型决定行为
适用场景:数据结构稳定,但需要经常添加新操作。
优点:添加新操作容易,符合开闭原则。
arduino
// 元素接口
public interface Element {
void accept(Visitor visitor);
}
// 具体元素
public class Circle implements Element {
private double radius;
public Circle(double radius) {
this.radius = radius;
}
public double getRadius() {
return radius;
}
@Override
public void accept(Visitor visitor) {
visitor.visitCircle(this);
}
}
public class Rectangle implements Element {
private double width, height;
public Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
public void accept(Visitor visitor) {
visitor.visitRectangle(this);
}
}
// 访问者接口
public interface Visitor {
void visitCircle(Circle circle);
void visitRectangle(Rectangle rectangle);
}
// 具体访问者:面积计算
public class AreaCalculator implements Visitor {
@Override
public void visitCircle(Circle circle) {
System.out.println("Circle area: " + Math.PI * circle.getRadius() * circle.getRadius());
}
@Override
public void visitRectangle(Rectangle rectangle) {
System.out.println("Rectangle area: " + rectangle.width * rectangle.height);
}
}
// 使用
List<Element> shapes = Arrays.asList(new Circle(5), new Rectangle(3, 4));
Visitor calculator = new AreaCalculator();
for (Element shape : shapes) {
shape.accept(calculator);
}
4.11 解释器模式(Interpreter Pattern)
💡 Spring 中的应用 :Spring Expression Language (SpEL) 使用解释器模式解析和执行表达式;
@Conditional注解的条件评估也用到解释器模式。
4.11.1 解释器模式的类图

两种表达式:
- 终结符表达式:叶子节点,直接解释
- 非终结符表达式:组合终结符,递归解释
适用场景:需要解释执行简单语法,如配置文件、正则表达式。
typescript
// 表达式接口
public interface Expression {
boolean interpret(String context);
}
// 终结符表达式
public class TerminalExpression implements Expression {
private String data;
public TerminalExpression(String data) {
this.data = data;
}
@Override
public boolean interpret(String context) {
return context.contains(data);
}
}
// 非终结符表达式:OR
public class OrExpression implements Expression {
private Expression expr1, expr2;
public OrExpression(Expression expr1, Expression expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
@Override
public boolean interpret(String context) {
return expr1.interpret(context) || expr2.interpret(context);
}
}
// 非终结符表达式:AND
public class AndExpression implements Expression {
private Expression expr1, expr2;
public AndExpression(Expression expr1, Expression expr2) {
this.expr1 = expr1;
this.expr2 = expr2;
}
@Override
public boolean interpret(String context) {
return expr1.interpret(context) && expr2.interpret(context);
}
}
// 使用
Expression isMale = new OrExpression(
new TerminalExpression("John"),
new TerminalExpression("Bob")
);
Expression isAdult = new AndExpression(
new TerminalExpression("John"),
new TerminalExpression("adult")
);
System.out.println(isMale.interpret("John")); // true
System.out.println(isAdult.interpret("John adult")); // true
五、设计模式对比总结
5.1、创建型模式对比
| 模式 | 特点 | 适用场景 |
|---|---|---|
| 单例模式 | 一个实例,全局访问 | 配置类、日志类、连接池 |
| 工厂模式 | 创建对象,封装细节 | 对象创建逻辑可能变化 |
| 建造者模式 | 分步构建,灵活组合 | 复杂对象,多配置选项 |
| 原型模式 | 克隆复制,避免重建 | 创建成本高,对象相似 |
5.2 结构型模式对比
| 模式 | 特点 | 适用场景 |
|---|---|---|
| 适配器模式 | 接口转换,兼容旧代码 | 接口不兼容 |
| 装饰器模式 | 动态增强,灵活叠加 | 功能扩展 |
| 代理模式 | 控制访问,延迟加载 | 权限控制、缓存 |
| 组合模式 | 树形结构,一致处理 | 文件系统、组织架构 |
| 桥接模式 | 分离抽象与实现 | 多维度变化 |
| 外观模式 | 简化接口,隐藏复杂度 | 复杂子系统 |
| 享元模式 | 共享对象,减少内存 | 大量相似对象 |
5.3、行为型模式对比
| 模式 | 特点 | 适用场景 |
|---|---|---|
| 策略模式 | 算法封装,随时切换 | 多算法变体 |
| 观察者模式 | 一对多通知 | 事件驱动 |
| 责任链模式 | 链式处理请求 | 多级审批 |
| 状态模式 | 状态决定行为 | 状态机 |
| 模板方法模式 | 骨架固定,步骤可变 | 算法框架 |
| 命令模式 | 请求封装,支持撤销 | 操作日志 |
| 中介者模式 | 对象交互封装 | UI 组件通信 |
| 备忘录模式 | 状态保存恢复 | 撤销功能 |
| 迭代器模式 | 顺序遍历,封装逻辑 | 统一遍历接口 |
| 访问者模式 | 数据与操作分离 | 添加新操作 |
| 解释器模式 | 语法解释执行 | 简单语言解析 |
5.4 功能维度对比
| 需求 | 推荐模式 |
|---|---|
| 只需一个实例 | 单例模式 |
| 创建复杂对象 | 建造者模式 |
| 对象创建可能变化 | 工厂模式 |
| 兼容旧接口 | 适配器模式 |
| 增强对象功能 | 装饰器模式 |
| 控制对象访问 | 代理模式 |
| 处理请求链 | 责任链模式 |
| 切换算法 | 策略模式 |
| 对象状态变化 | 状态模式 |
| 简化复杂系统 | 外观模式 |
| 减少对象数量 | 享元模式 |
| 一对多通知 | 观察者模式 |
六、设计模式实践建议
6.1、何时使用设计模式
- 识别重复问题:遇到同类问题多次出现时,考虑使用设计模式
- 保持简单:不要过度设计,简单代码优于复杂模式
- 考虑变化:如果某部分代码经常变化,设计模式可以帮助隔离变化
- 团队共识:团队成员都理解设计模式时再使用
6.2、设计原则(SOLID)
| 原则 | 含义 | 相关模式 |
|---|---|---|
| 单一职责 | 一个类只做一件事 | 组合模式 |
| 开闭原则 | 对扩展开放,对修改关闭 | 策略模式、观察者模式 |
| 里氏替换 | 子类可以替换父类 | 模板方法、策略模式 |
| 接口隔离 | 接口要小而专 | 装饰器模式 |
| 依赖倒置 | 依赖抽象而非具体 | 工厂模式、策略模式 |
6.3 面试高频考点
| 模式 | 面试常问点 |
|---|---|
| 单例模式 | 几种实现方式?线程安全?防反射?双重检查 volatile? |
| 工厂模式 | 三种工厂区别?Spring BeanFactory 的应用? |
| 代理模式 | JDK代理 vs CGLIB?Spring AOP 原理? |
| 装饰器模式 | 与代理模式的区别?Java I/O 中的应用? |
| 观察者模式 | 与发布订阅的区别?Spring 事件机制? |
| 策略模式 | 与桥接模式区别?Spring 中的应用? |
| 责任链模式 | Spring MVC HandlerInterceptor?Filter? |
| 模板方法 | 与回调的区别?JdbcTemplate 中的应用? |
6.4 常见误区
- 滥用设计模式:不要为了使用模式而使用
- 过度工程:简单场景不需要复杂设计
- 教条主义:根据实际情况调整模式
- 忽略代价:考虑模式带来的复杂度