设计模式教程

设计模式教程

本文档系统介绍 Java 设计模式,涵盖创建型、结构型、行为型三大类共 23 种设计模式,包含完整的代码示例与应用场景说明。


💡 创作不易,请点个收藏关注!


目录


一、设计模式概述

1.1 什么是设计模式

设计模式(Design Pattern)是面向对象编程中针对常见问题的标准化解决方案,它不直接提供代码实现,而是通过定义类与对象的交互方式,提升代码的可复用性、可维护性和可扩展性。

1.2 设计模式的核心价值

  1. 避免重复造轮子:遵循业界公认的设计思路
  2. 降低代码复杂度:使设计结构更清晰
  3. 便于团队协作:开发人员可基于共同的模式语言沟通
  4. 提高代码质量:编写出更加灵活、可重用、易维护的代码

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():私有构造函数,防止外部 new
  • public 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 Cloud DecoratingWebhook 等都使用了装饰器模式。

代理模式为其他对象提供一种代理以控制对这个对象的访问。它通过创建一个代理对象来控制对原始对象的访问,从而实现一些额外的功能,如延迟初始化、权限控制、缓存等。

适用场景:当需要控制对原始对象的访问时,可以使用代理模式。

优点:通过代理对象控制对原始对象的访问,可以实现一些额外的功能,如延迟初始化、权限控制、缓存等。

缺点:增加了系统的复杂性,可能降低代码的可读性。

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 的 ResultSetExtractorRowMapper 封装了 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、何时使用设计模式

  1. 识别重复问题:遇到同类问题多次出现时,考虑使用设计模式
  2. 保持简单:不要过度设计,简单代码优于复杂模式
  3. 考虑变化:如果某部分代码经常变化,设计模式可以帮助隔离变化
  4. 团队共识:团队成员都理解设计模式时再使用

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 常见误区

  1. 滥用设计模式:不要为了使用模式而使用
  2. 过度工程:简单场景不需要复杂设计
  3. 教条主义:根据实际情况调整模式
  4. 忽略代价:考虑模式带来的复杂度
相关推荐
苏格兰黑马2 小时前
解构 OpenClaw:高度解耦的渠道层架构与 Telegram 插件实现
架构
凌览2 小时前
别再手搓 Skill 了,用这个工具 5 分钟搞定
前端·后端
guslegend2 小时前
第10节:设计高效混合检索架构,提升召回精度
人工智能·架构·大模型·rag
RestCloud3 小时前
制造业供应链实时数据集成:从T+1到T+0的架构落地实录
架构·etl·数据同步·数据集成平台
weixin_408099673 小时前
python请求文字识别ocr api
开发语言·人工智能·后端·python·ocr·api·ocr文字识别
weixin_408099673 小时前
【组合实战】OCR + 图片去水印 API:自动清洗图片再识别文字(完整方案 + 代码示例)
图像处理·后端·ocr·api·文字识别·去水印·ocr识别优化
gelald3 小时前
SpringBoot - Actuator与监控
java·spring boot·后端
用户585343788433 小时前
AI Harness Engineering:从概念、场景到落地方法
人工智能·后端