GoF 23 种设计模式完整知识总结与使用教程

GoF 23 种设计模式完整知识总结与使用教程


目录

  • 一、设计模式概述
  • 二、面向对象七大设计原则
  • 三、创建型模式(5种)
    • [1. 单例模式 Singleton](#1. 单例模式 Singleton)
    • [2. 工厂方法模式 Factory Method](#2. 工厂方法模式 Factory Method)
    • [3. 抽象工厂模式 Abstract Factory](#3. 抽象工厂模式 Abstract Factory)
    • [4. 建造者模式 Builder](#4. 建造者模式 Builder)
    • [5. 原型模式 Prototype](#5. 原型模式 Prototype)
  • 四、结构型模式(7种)
    • [6. 适配器模式 Adapter](#6. 适配器模式 Adapter)
    • [7. 桥接模式 Bridge](#7. 桥接模式 Bridge)
    • [8. 组合模式 Composite](#8. 组合模式 Composite)
    • [9. 装饰器模式 Decorator](#9. 装饰器模式 Decorator)
    • [10. 外观模式 Facade](#10. 外观模式 Facade)
    • [11. 享元模式 Flyweight](#11. 享元模式 Flyweight)
    • [12. 代理模式 Proxy](#12. 代理模式 Proxy)
  • 五、行为型模式(11种)
    • [13. 责任链模式 Chain of Responsibility](#13. 责任链模式 Chain of Responsibility)
    • [14. 命令模式 Command](#14. 命令模式 Command)
    • [15. 解释器模式 Interpreter](#15. 解释器模式 Interpreter)
    • [16. 迭代器模式 Iterator](#16. 迭代器模式 Iterator)
    • [17. 中介者模式 Mediator](#17. 中介者模式 Mediator)
    • [18. 备忘录模式 Memento](#18. 备忘录模式 Memento)
    • [19. 观察者模式 Observer](#19. 观察者模式 Observer)
    • [20. 状态模式 State](#20. 状态模式 State)
    • [21. 策略模式 Strategy](#21. 策略模式 Strategy)
    • [22. 模板方法模式 Template Method](#22. 模板方法模式 Template Method)
    • [23. 访问者模式 Visitor](#23. 访问者模式 Visitor)
  • 六、23种模式总览对比表
  • 七、设计模式选择指南
  • 八、设计模式在主流框架中的应用

一、设计模式概述

1.1 什么是设计模式

设计模式(Design Pattern)是软件工程中对代码开发经验的总结 ,是针对软件设计过程中反复出现的问题所提供的典型可复用解决方案 。它不是语法规定,也不是现成可直接使用的代码,而是一套解决特定场景下设计问题的思想方法和最佳实践

设计模式的核心价值体现在以下几点:

  • 可复用性:相似问题套用成熟模式,避免重复造轮子。
  • 可维护性:模式往往遵循良好的设计原则,代码结构清晰易于修改。
  • 可读性:团队成员共享设计词汇,提升沟通效率。
  • 可扩展性:良好的设计模式天然具备开放扩展的能力。

1.2 GoF 与设计模式的诞生

1994年,Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides 四位大师联合出版了划时代著作:

《Design Patterns: Elements of Reusable Object-Oriented Software》

(《设计模式:可复用面向对象软件的基础》)

四位作者合称 GoF(Gang of Four,四人帮) 。书中系统整理了面向对象软件设计中 23 种经典模式,这些模式被沿用至今,成为软件设计领域的"圣经"。

1.3 设计模式的三大分类

分类 数量 核心关注点 包含模式
创建型模式 5 种 对象的创建方式 单例、工厂方法、抽象工厂、建造者、原型
结构型模式 7 种 类与对象的组合结构 适配器、桥接、组合、装饰器、外观、享元、代理
行为型模式 11 种 对象之间的通信与职责分配 责任链、命令、解释器、迭代器、中介者、备忘录、观察者、状态、策略、模板方法、访问者

二、面向对象七大设计原则

设计模式是这七大原则的具体应用,理解这些原则是学习设计模式的基础。

2.1 开闭原则(OCP - Open-Closed Principle)

对扩展开放,对修改关闭。

软件实体(类、模块、函数)应当可以在不修改已有代码的前提下进行功能扩展。是设计模式的灵魂原则。

java 复制代码
// 违反OCP:新增形状需要修改已有代码
class AreaCalculator {
    double calculate(Object shape) {
        if (shape instanceof Circle) { /* ... */ }
        else if (shape instanceof Rectangle) { /* ... */ } // 每次新增都要修改这里
    }
}

// 遵守OCP:新增形状只需新增类
interface Shape { double area(); }
class Circle implements Shape { public double area() { return Math.PI * r * r; } }
class Rectangle implements Shape { public double area() { return w * h; } }

2.2 单一职责原则(SRP - Single Responsibility Principle)

一个类只负责一项职责,引起该类变化的原因只有一个。

避免一个类同时承担过多功能,降低类的复杂度,提高内聚性。

2.3 里氏替换原则(LSP - Liskov Substitution Principle)

子类对象必须能够替换父类对象,且程序行为不变。

子类可以扩展父类功能,但不能改变父类原有的功能(重写父类方法时功能只能增强不能削弱)。

2.4 依赖倒置原则(DIP - Dependency Inversion Principle)

高层模块不应依赖低层模块,两者都应依赖其抽象。

面向接口/抽象编程,而非面向具体实现编程。

2.5 接口隔离原则(ISP - Interface Segregation Principle)

客户端不应该被迫依赖它不使用的方法,接口应该尽量细化。

使用多个专门的接口,比使用一个大而全的接口要好。

2.6 迪米特法则(LoD - Law of Demeter)

一个对象应该对其他对象保持最少的了解(最少知识原则)。

只与直接朋友(成员变量、方法参数、方法返回值的类型)通信,不与陌生类通信,降低耦合。

2.7 合成复用原则(CRP - Composite Reuse Principle)

优先使用组合/聚合关系来实现代码复用,而不是继承。

继承会破坏封装性,而组合更灵活,耦合度更低。


三、创建型模式(5种)

创建型模式关注对象的创建过程,将对象的创建与使用分离,提高系统的灵活性和可扩展性。


1. 单例模式 Singleton

1.1 定义与意图

保证一个类仅有一个实例,并提供一个全局访问点来访问该实例。

1.2 核心思想
  • 将构造函数设为 private,阻止外部直接实例化。
  • 提供一个静态方法(通常命名为 getInstance()),内部负责创建并返回唯一实例。
  • 实例以静态成员变量保存。
1.3 适用场景
  • 系统中只需要一个全局配置管理器(如 ApplicationConfig)。
  • 数据库连接池、线程池只需一个实例。
  • 日志记录器(Logger)。
  • Spring 框架中默认 Bean 作用域(singleton)。
  • 操作系统中的窗口管理器、文件系统。
1.4 实现方式(Java)

① 饿汉式(线程安全,推荐简单场景)

java 复制代码
public class Singleton {
    // 类加载时就创建实例,天然线程安全
    private static final Singleton INSTANCE = new Singleton();
    
    private Singleton() {} // 私有构造函数
    
    public static Singleton getInstance() {
        return INSTANCE;
    }
}

② 懒汉式 - 双重检查锁(DCL,推荐多线程场景)

java 复制代码
public class Singleton {
    // volatile 禁止指令重排序,保证多线程可见性
    private static volatile Singleton instance;
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        if (instance == null) {                    // 第一次检查(减少同步开销)
            synchronized (Singleton.class) {
                if (instance == null) {            // 第二次检查(防止重复创建)
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

③ 静态内部类(推荐,延迟加载且线程安全)

java 复制代码
public class Singleton {
    private Singleton() {}
    
    // 静态内部类,只有在调用 getInstance() 时才加载
    private static class Holder {
        private static final Singleton INSTANCE = new Singleton();
    }
    
    public static Singleton getInstance() {
        return Holder.INSTANCE;
    }
}

④ 枚举单例(最简洁,防反射攻击,Joshua Bloch 推荐)

java 复制代码
public enum Singleton {
    INSTANCE;
    
    public void doSomething() {
        System.out.println("Singleton via Enum");
    }
}

// 使用
Singleton.INSTANCE.doSomething();
1.5 优缺点
优点 缺点
内存中只有一个实例,节省资源 违反单一职责原则(负责创建自身)
提供全局访问点,使用方便 不利于单元测试(难以 Mock)
避免频繁创建销毁对象,性能好 多线程下需要额外处理,否则线程不安全
1.6 与其他模式的关系
  • 享元模式:如果所有共享状态只有一个对象,则享元类似单例,但单例对象可变,享元不可变。
  • 抽象工厂、建造者、原型都可以用单例实现。

2. 工厂方法模式 Factory Method

2.1 定义与意图

定义一个用于创建对象的接口,让子类决定实例化哪个类。工厂方法使一个类的实例化延迟到其子类。

2.2 核心角色
角色 描述
Product(抽象产品) 定义产品的公共接口
ConcreteProduct(具体产品) 实现产品接口
Creator(抽象创建者) 声明工厂方法,可包含核心业务逻辑
ConcreteCreator(具体创建者) 实现工厂方法,返回具体产品实例
2.3 适用场景
  • 创建对象的逻辑复杂,且希望将创建与使用解耦。
  • 不确定需要使用哪个具体类(运行时决定)。
  • 希望通过子类扩展产品种类,而不修改已有代码。
  • 典型案例:java.util.Calendar.getInstance()java.nio.charset.Charset.forName()
2.4 代码实现
java 复制代码
// 抽象产品
public interface Button {
    void render();
    void onClick();
}

// 具体产品 A
public class WindowsButton implements Button {
    @Override
    public void render() { System.out.println("渲染 Windows 风格按钮"); }
    @Override
    public void onClick() { System.out.println("Windows 按钮点击事件"); }
}

// 具体产品 B
public class HtmlButton implements Button {
    @Override
    public void render() { System.out.println("渲染 HTML 按钮 <button>"); }
    @Override
    public void onClick() { System.out.println("HTML 按钮点击事件"); }
}

// 抽象创建者(包含核心业务逻辑)
public abstract class Dialog {
    // 工厂方法:由子类决定创建哪种按钮
    public abstract Button createButton();
    
    // 核心业务逻辑(使用工厂方法创建的产品)
    public void render() {
        Button button = createButton();
        button.render();
    }
}

// 具体创建者 A
public class WindowsDialog extends Dialog {
    @Override
    public Button createButton() {
        return new WindowsButton();
    }
}

// 具体创建者 B
public class HtmlDialog extends Dialog {
    @Override
    public Button createButton() {
        return new HtmlButton();
    }
}

// 客户端
public class Application {
    private Dialog dialog;
    
    public void initialize(String osName) {
        if (osName.equals("Windows")) {
            dialog = new WindowsDialog();
        } else {
            dialog = new HtmlDialog();
        }
    }
    
    public void render() {
        dialog.render(); // 通过抽象接口使用,无需关心具体类型
    }
}
2.5 简单工厂(Simple Factory)

注意:简单工厂不属于 GoF 23 种模式,但实践中很常用。

java 复制代码
// 简单工厂:通过静态方法集中创建对象
public class ButtonFactory {
    public static Button createButton(String type) {
        switch (type) {
            case "Windows": return new WindowsButton();
            case "Html":    return new HtmlButton();
            default: throw new IllegalArgumentException("未知按钮类型: " + type);
        }
    }
}

简单工厂 vs 工厂方法:简单工厂违反开闭原则(新增类型需修改工厂),工厂方法通过子类扩展,更符合 OCP。

2.6 优缺点
优点 缺点
解耦创建者和具体产品 每新增一种产品,需新增一个具体创建者类
符合开闭原则,易于扩展 类的数量增多
客户端只依赖抽象接口

3. 抽象工厂模式 Abstract Factory

3.1 定义与意图

提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。

抽象工厂可以看作是工厂的工厂 ,它创建的是一个产品族(一组相关产品),而工厂方法只创建单一产品。

3.2 核心角色
角色 描述
AbstractFactory 声明创建各种产品的抽象工厂接口
ConcreteFactory 实现创建具体产品族的工厂
AbstractProduct 每种产品的抽象接口
ConcreteProduct 具体产品实现
3.3 适用场景
  • 系统需要与多个产品族协同工作(如跨平台 UI 组件:Windows风格/Mac风格)。
  • 需要保证产品族内的产品相互兼容(同一主题的按钮、文本框、滚动条应一致)。
  • 典型案例:javax.xml.parsers.DocumentBuilderFactoryjava.awt.Toolkit
3.4 代码实现
java 复制代码
// 抽象产品接口
public interface Button { void render(); }
public interface Checkbox { void render(); }

// 具体产品:Windows 风格
public class WindowsButton implements Button {
    @Override public void render() { System.out.println("Windows 按钮"); }
}
public class WindowsCheckbox implements Checkbox {
    @Override public void render() { System.out.println("Windows 复选框"); }
}

// 具体产品:Mac 风格
public class MacButton implements Button {
    @Override public void render() { System.out.println("Mac 按钮"); }
}
public class MacCheckbox implements Checkbox {
    @Override public void render() { System.out.println("Mac 复选框"); }
}

// 抽象工厂接口
public interface GUIFactory {
    Button createButton();
    Checkbox createCheckbox();
}

// 具体工厂 A:生产 Windows 产品族
public class WindowsFactory implements GUIFactory {
    @Override public Button createButton()   { return new WindowsButton(); }
    @Override public Checkbox createCheckbox() { return new WindowsCheckbox(); }
}

// 具体工厂 B:生产 Mac 产品族
public class MacFactory implements GUIFactory {
    @Override public Button createButton()   { return new MacButton(); }
    @Override public Checkbox createCheckbox() { return new MacCheckbox(); }
}

// 应用程序(只依赖抽象工厂和抽象产品)
public class Application {
    private Button button;
    private Checkbox checkbox;
    
    public Application(GUIFactory factory) {
        this.button   = factory.createButton();
        this.checkbox = factory.createCheckbox();
    }
    
    public void render() {
        button.render();
        checkbox.render();
    }
}

// 客户端
public class Main {
    public static void main(String[] args) {
        GUIFactory factory;
        String os = System.getProperty("os.name");
        if (os.contains("Windows")) {
            factory = new WindowsFactory();
        } else {
            factory = new MacFactory();
        }
        Application app = new Application(factory);
        app.render();
    }
}
3.5 工厂方法 vs 抽象工厂
对比维度 工厂方法 抽象工厂
产品数量 单一产品 产品族(多个相关产品)
扩展方式 继承(新增子类) 组合(新增工厂实现)
复杂度 较高
典型用途 单个对象的创建 跨平台/主题的整套组件创建

4. 建造者模式 Builder

4.1 定义与意图

将一个复杂对象的构建过程与其表示相分离,使得同样的构建过程可以创建不同的表示。

4.2 核心角色
角色 描述
Builder(抽象建造者) 声明构建产品各个部件的方法
ConcreteBuilder(具体建造者) 实现建造步骤,提供获取产品的方法
Director(指挥者) 按顺序调用建造者的方法,控制建造流程
Product(产品) 最终构建的复杂对象
4.3 适用场景
  • 构建的对象有很多属性,且部分属性可选(避免构造函数参数爆炸)。
  • 同一套构建流程需要创建不同的产品表示。
  • 典型案例:StringBuilderAlertDialog.Builder(Android)、Stream.Builder(Java 8)、Lombok @Builder
4.4 代码实现

① 经典建造者模式

java 复制代码
// 产品类(复杂对象)
public class Computer {
    private String cpu;
    private String memory;
    private String storage;
    private String gpu;          // 可选
    private String cooler;       // 可选
    
    // 私有构造,只能通过 Builder 创建
    private Computer(Builder builder) {
        this.cpu     = builder.cpu;
        this.memory  = builder.memory;
        this.storage = builder.storage;
        this.gpu     = builder.gpu;
        this.cooler  = builder.cooler;
    }
    
    @Override
    public String toString() {
        return "Computer{cpu=" + cpu + ", memory=" + memory + 
               ", storage=" + storage + ", gpu=" + gpu + "}";
    }
    
    // 静态内部 Builder 类
    public static class Builder {
        // 必填参数
        private final String cpu;
        private final String memory;
        // 可选参数
        private String storage = "256GB SSD";
        private String gpu     = "集成显卡";
        private String cooler  = "原装散热";
        
        public Builder(String cpu, String memory) {
            this.cpu    = cpu;
            this.memory = memory;
        }
        
        public Builder storage(String storage) { this.storage = storage; return this; }
        public Builder gpu(String gpu)         { this.gpu = gpu; return this; }
        public Builder cooler(String cooler)   { this.cooler = cooler; return this; }
        
        public Computer build() {
            return new Computer(this);
        }
    }
}

// 客户端(链式调用)
Computer gamingPC = new Computer.Builder("Intel i9-13900K", "64GB DDR5")
        .storage("2TB NVMe SSD")
        .gpu("RTX 4090")
        .cooler("360mm AIO")
        .build();

Computer officePC = new Computer.Builder("Intel i5-13400", "16GB DDR4")
        .build(); // 使用默认值

② 经典 Director 模式

java 复制代码
// 抽象建造者
public interface HouseBuilder {
    void buildFoundation();
    void buildWalls();
    void buildRoof();
    House getResult();
}

// 具体建造者(木屋)
public class WoodenHouseBuilder implements HouseBuilder {
    private House house = new House();
    @Override public void buildFoundation() { house.setFoundation("木质地基"); }
    @Override public void buildWalls()      { house.setWalls("木质墙壁"); }
    @Override public void buildRoof()       { house.setRoof("木质屋顶"); }
    @Override public House getResult()      { return house; }
}

// 指挥者
public class Director {
    public House constructSimpleHouse(HouseBuilder builder) {
        builder.buildFoundation();
        builder.buildWalls();
        builder.buildRoof();
        return builder.getResult();
    }
}

5. 原型模式 Prototype

5.1 定义与意图

用原型实例指定创建对象的种类,并通过复制这个原型来创建新的对象。

即通过**克隆(Clone)**已有对象来创建新对象,无需通过构造函数。

5.2 适用场景
  • 对象的创建成本高昂(需要复杂初始化、数据库查询、网络请求)。
  • 需要创建与已有对象相同或相似的对象。
  • 对象的类层次与使用层次需要解耦。
  • 典型案例:Object.clone()(Java)、原型链(JavaScript)、Cloneable 接口。
5.3 浅克隆 vs 深克隆
java 复制代码
// 浅克隆(只复制基本类型和引用,引用仍指向同一对象)
public class ShallowCloneDemo implements Cloneable {
    private int id;
    private String name;
    private List<String> tags; // 引用类型:浅克隆后仍共享同一个 List
    
    @Override
    public ShallowCloneDemo clone() throws CloneNotSupportedException {
        return (ShallowCloneDemo) super.clone(); // Object.clone() 是浅克隆
    }
}

// 深克隆(所有引用类型也递归克隆)
public class DeepCloneDemo implements Cloneable {
    private int id;
    private List<String> tags;
    
    @Override
    public DeepCloneDemo clone() throws CloneNotSupportedException {
        DeepCloneDemo copy = (DeepCloneDemo) super.clone();
        copy.tags = new ArrayList<>(this.tags); // 深克隆列表
        return copy;
    }
}

// 推荐方式:通过序列化实现深克隆
public <T extends Serializable> T deepCopy(T obj) throws Exception {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    new ObjectOutputStream(bos).writeObject(obj);
    ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
    return (T) new ObjectInputStream(bis).readObject();
}
5.4 原型注册表(Prototype Registry)
java 复制代码
// 管理一组预配置的原型对象
public class PrototypeRegistry {
    private final Map<String, Prototype> prototypes = new HashMap<>();
    
    public void register(String key, Prototype prototype) {
        prototypes.put(key, prototype);
    }
    
    public Prototype get(String key) throws CloneNotSupportedException {
        return prototypes.get(key).clone();
    }
}

四、结构型模式(7种)

结构型模式关注如何将类或对象组合成更大的结构,同时保持结构的灵活性和高效性。


6. 适配器模式 Adapter

6.1 定义与意图

将一个类的接口转换成客户端希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的类能够协同工作。

就像电源适配器将不同规格的插头转换为统一接口,适配器模式充当两个不兼容接口之间的桥梁。

6.2 两种实现方式

① 对象适配器(推荐,使用组合)

java 复制代码
// 目标接口(客户端期望的接口)
public interface MediaPlayer {
    void play(String audioType, String fileName);
}

// 被适配的旧接口(第三方库,无法修改)
public class AdvancedMediaPlayer {
    public void playVlc(String fileName)  { System.out.println("Playing VLC: " + fileName); }
    public void playMp4(String fileName)  { System.out.println("Playing MP4: " + fileName); }
}

// 对象适配器(通过组合持有被适配对象)
public class MediaAdapter implements MediaPlayer {
    private final AdvancedMediaPlayer advancedPlayer = new AdvancedMediaPlayer();
    
    @Override
    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("vlc")) {
            advancedPlayer.playVlc(fileName);
        } else if (audioType.equalsIgnoreCase("mp4")) {
            advancedPlayer.playMp4(fileName);
        }
    }
}

// 具体的音乐播放器(使用适配器)
public class AudioPlayer implements MediaPlayer {
    private final MediaAdapter mediaAdapter = new MediaAdapter();
    
    @Override
    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("mp3")) {
            System.out.println("Playing MP3: " + fileName);
        } else if (audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")) {
            mediaAdapter.play(audioType, fileName); // 委托给适配器
        } else {
            System.out.println("不支持的格式: " + audioType);
        }
    }
}

// 客户端
AudioPlayer player = new AudioPlayer();
player.play("mp3", "music.mp3");   // 直接播放
player.play("vlc", "movie.vlc");   // 通过适配器播放
player.play("mp4", "video.mp4");   // 通过适配器播放

② 类适配器(使用多重继承,Java 中用 interface 实现)

java 复制代码
// 类适配器(同时继承目标类和被适配类,Java中较少用)
public class ClassAdapter extends AdvancedMediaPlayer implements MediaPlayer {
    @Override
    public void play(String audioType, String fileName) {
        if (audioType.equalsIgnoreCase("vlc")) {
            this.playVlc(fileName);
        }
    }
}
6.3 适用场景
  • 需要使用现有类,但其接口与系统其他部分不兼容。
  • 对接第三方库,使其符合系统内部接口标准。
  • 典型案例:java.io.InputStreamReader(将 InputStream 适配为 Reader)、Arrays.asList()

7. 桥接模式 Bridge

7.1 定义与意图

将抽象部分与其实现部分分离,使它们都可以独立变化。

通过组合代替继承,将两个维度的变化独立抽象,避免多维继承导致的类数量爆炸。

7.2 适用场景
  • 一个类存在两个或多个独立变化的维度(如形状×颜色、设备×软件)。
  • 希望在运行时切换实现(如切换数据库驱动)。
  • 典型案例:JDBC 驱动(DriverManager 桥接不同数据库实现)、java.util.logging 的 Handler 与 Formatter。
7.3 代码实现
java 复制代码
// 实现层接口(颜色维度)
public interface Color {
    String fill();
}
public class Red   implements Color { public String fill() { return "红色"; } }
public class Blue  implements Color { public String fill() { return "蓝色"; } }
public class Green implements Color { public String fill() { return "绿色"; } }

// 抽象层(形状维度,持有实现层引用)
public abstract class Shape {
    protected Color color; // 桥接:持有实现接口的引用
    
    public Shape(Color color) { this.color = color; }
    
    public abstract String draw();
}

// 具体形状(扩展抽象层)
public class Circle extends Shape {
    public Circle(Color color) { super(color); }
    
    @Override
    public String draw() {
        return "绘制" + color.fill() + "圆形";
    }
}

public class Rectangle extends Shape {
    public Rectangle(Color color) { super(color); }
    
    @Override
    public String draw() {
        return "绘制" + color.fill() + "矩形";
    }
}

// 客户端:形状和颜色可以独立变化,无需创建 形状×颜色 数量的子类
Shape redCircle    = new Circle(new Red());    // 红色圆形
Shape blueRect     = new Rectangle(new Blue()); // 蓝色矩形
Shape greenCircle  = new Circle(new Green());  // 绿色圆形(无需新类)

System.out.println(redCircle.draw());   // 绘制红色圆形
System.out.println(blueRect.draw());    // 绘制蓝色矩形

8. 组合模式 Composite

8.1 定义与意图

将对象组合成树形结构以表示"部分-整体"的层次结构,使客户端对单个对象和组合对象的使用具有一致性。

8.2 核心角色
角色 描述
Component(抽象组件) 叶子节点和容器节点的公共接口
Leaf(叶子节点) 没有子节点的基本元素
Composite(容器节点) 包含子节点,实现与子节点相关的操作
8.3 代码实现
java 复制代码
// 抽象组件
public abstract class FileSystemItem {
    protected String name;
    
    public FileSystemItem(String name) { this.name = name; }
    
    public abstract void display(int depth);
    public abstract long getSize();
    
    // 组合节点才有意义的操作,叶子节点抛异常或空实现
    public void add(FileSystemItem item)    { throw new UnsupportedOperationException(); }
    public void remove(FileSystemItem item) { throw new UnsupportedOperationException(); }
}

// 叶子节点:文件
public class File extends FileSystemItem {
    private long size;
    
    public File(String name, long size) { super(name); this.size = size; }
    
    @Override public void display(int depth) {
        System.out.println("  ".repeat(depth) + "📄 " + name + " (" + size + "KB)");
    }
    @Override public long getSize() { return size; }
}

// 容器节点:文件夹
public class Folder extends FileSystemItem {
    private List<FileSystemItem> children = new ArrayList<>();
    
    public Folder(String name) { super(name); }
    
    @Override public void add(FileSystemItem item)    { children.add(item); }
    @Override public void remove(FileSystemItem item) { children.remove(item); }
    
    @Override
    public void display(int depth) {
        System.out.println("  ".repeat(depth) + "📁 " + name);
        for (FileSystemItem child : children) {
            child.display(depth + 1); // 递归展示
        }
    }
    
    @Override
    public long getSize() {
        return children.stream().mapToLong(FileSystemItem::getSize).sum();
    }
}

// 客户端
Folder root = new Folder("根目录");
Folder docs = new Folder("文档");
docs.add(new File("报告.docx", 128));
docs.add(new File("合同.pdf", 256));
root.add(docs);
root.add(new File("README.md", 4));
root.display(0);
System.out.println("总大小: " + root.getSize() + "KB");
8.4 适用场景
  • 需要以树形结构表示层次关系(文件系统、组织架构、菜单、XML DOM)。
  • 希望客户端统一处理单个对象和组合对象。
  • 典型案例:Java Swing 组件树、java.awt.Container

9. 装饰器模式 Decorator

9.1 定义与意图

动态地向一个对象添加额外的功能,而不改变其接口。装饰器提供了比子类继承更灵活的功能扩展方式。

9.2 核心思想

装饰器实现与被装饰对象相同的接口 ,同时持有被装饰对象的引用 ,在调用前后增加额外行为。可以多层嵌套叠加

9.3 代码实现
java 复制代码
// 抽象组件接口
public interface Coffee {
    String getDescription();
    double getCost();
}

// 具体组件(基础咖啡)
public class SimpleCoffee implements Coffee {
    @Override public String getDescription() { return "简单咖啡"; }
    @Override public double getCost()        { return 10.0; }
}

// 抽象装饰器基类
public abstract class CoffeeDecorator implements Coffee {
    protected final Coffee decoratedCoffee;
    
    public CoffeeDecorator(Coffee coffee) {
        this.decoratedCoffee = coffee;
    }
    
    @Override public String getDescription() { return decoratedCoffee.getDescription(); }
    @Override public double getCost()        { return decoratedCoffee.getCost(); }
}

// 具体装饰器 A:加牛奶
public class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) { super(coffee); }
    
    @Override public String getDescription() { return decoratedCoffee.getDescription() + ", 牛奶"; }
    @Override public double getCost()        { return decoratedCoffee.getCost() + 3.0; }
}

// 具体装饰器 B:加糖
public class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee coffee) { super(coffee); }
    
    @Override public String getDescription() { return decoratedCoffee.getDescription() + ", 糖"; }
    @Override public double getCost()        { return decoratedCoffee.getCost() + 1.5; }
}

// 具体装饰器 C:加奶油
public class WhipDecorator extends CoffeeDecorator {
    public WhipDecorator(Coffee coffee) { super(coffee); }
    
    @Override public String getDescription() { return decoratedCoffee.getDescription() + ", 奶油"; }
    @Override public double getCost()        { return decoratedCoffee.getCost() + 5.0; }
}

// 客户端(动态叠加装饰)
Coffee coffee = new SimpleCoffee();
System.out.println(coffee.getDescription() + " - ¥" + coffee.getCost());
// 简单咖啡 - ¥10.0

coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);
coffee = new WhipDecorator(coffee);
System.out.println(coffee.getDescription() + " - ¥" + coffee.getCost());
// 简单咖啡, 牛奶, 糖, 奶油 - ¥19.5
9.4 适用场景
  • 需要在不修改原有类的情况下动态添加功能。
  • 功能可以任意组合叠加,继承无法应对组合爆炸。
  • 典型案例:java.io.BufferedInputStreamjava.io.DataInputStream(Java I/O 流体系是装饰器模式的经典实现)。

Java I/O 装饰器链示例:

java 复制代码
// InputStream → FileInputStream(具体组件)
//             → BufferedInputStream(装饰:添加缓冲)
//             → DataInputStream(装饰:添加数据类型读写)
InputStream in = new DataInputStream(
                    new BufferedInputStream(
                        new FileInputStream("data.bin")));

10. 外观模式 Facade

10.1 定义与意图

为子系统中的一组接口提供一个统一的高层接口,使子系统更易于使用。

外观模式隐藏了子系统内部的复杂性,提供一个简单易用的接口给客户端。

10.2 代码实现
java 复制代码
// 复杂的子系统组件
public class CPU {
    public void freeze() { System.out.println("CPU 冻结"); }
    public void jump(long position) { System.out.println("CPU 跳转到: " + position); }
    public void execute() { System.out.println("CPU 执行指令"); }
}

public class Memory {
    public void load(long position, byte[] data) {
        System.out.println("内存加载数据到位置: " + position);
    }
}

public class HardDrive {
    public byte[] read(long lba, int size) {
        System.out.println("硬盘读取数据: lba=" + lba);
        return new byte[size];
    }
}

// 外观类(统一入口)
public class ComputerFacade {
    private final CPU cpu;
    private final Memory memory;
    private final HardDrive hardDrive;
    
    public ComputerFacade() {
        this.cpu       = new CPU();
        this.memory    = new Memory();
        this.hardDrive = new HardDrive();
    }
    
    // 将复杂的启动流程封装为一个简单方法
    public void start() {
        System.out.println("=== 计算机启动开始 ===");
        cpu.freeze();
        byte[] bootData = hardDrive.read(0, 1024);
        memory.load(0, bootData);
        cpu.jump(0);
        cpu.execute();
        System.out.println("=== 计算机启动完成 ===");
    }
}

// 客户端(无需了解内部子系统)
ComputerFacade computer = new ComputerFacade();
computer.start(); // 一键启动
10.3 适用场景
  • 为复杂子系统提供简单接口(API 网关、SDK 封装)。
  • 对子系统进行分层,用外观定义各层入口。
  • 典型案例:javax.faces.context.FacesContextSLF4J 日志门面、Spring 的 JdbcTemplate

11. 享元模式 Flyweight

11.1 定义与意图

运用共享技术有效地支持大量细粒度对象,将对象的内部状态(可共享)与外部状态(不可共享)分离。

11.2 内部状态 vs 外部状态
  • 内部状态(Intrinsic State):存储在享元对象内部,不随环境变化,可以共享。
  • 外部状态(Extrinsic State):随环境变化,不可共享,由客户端传入。
11.3 代码实现
java 复制代码
// 享元接口
public interface TreeType {
    void draw(int x, int y); // x,y 是外部状态
}

// 具体享元(存储内部状态:树的种类、颜色、纹理)
public class ConcreteTreeType implements TreeType {
    private final String name;    // 内部状态(可共享)
    private final String color;   // 内部状态(可共享)
    private final String texture; // 内部状态(可共享)
    
    public ConcreteTreeType(String name, String color, String texture) {
        this.name = name; this.color = color; this.texture = texture;
    }
    
    @Override
    public void draw(int x, int y) { // x,y 外部状态由外部传入
        System.out.println("在(" + x + "," + y + ")绘制" + color + name + "(纹理:" + texture + ")");
    }
}

// 享元工厂(管理享元对象的缓存池)
public class TreeTypeFactory {
    private static final Map<String, TreeType> cache = new HashMap<>();
    
    public static TreeType getTreeType(String name, String color, String texture) {
        String key = name + "_" + color + "_" + texture;
        return cache.computeIfAbsent(key, k -> {
            System.out.println("创建新享元对象: " + key);
            return new ConcreteTreeType(name, color, texture);
        });
    }
    
    public static int getCacheSize() { return cache.size(); }
}

// 客户端(绘制 100 万棵树,但只有少数几种享元对象)
public class Forest {
    private List<int[]> trees = new ArrayList<>(); // 存储位置(外部状态)
    
    public void plantTree(int x, int y, String name, String color, String texture) {
        TreeType type = TreeTypeFactory.getTreeType(name, color, texture);
        trees.add(new int[]{x, y, System.identityHashCode(type)});
        type.draw(x, y);
    }
}

// 运行结果:绘制大量树,但享元对象只创建极少数
// 创建新享元对象: 橡树_绿色_粗糙
// 在(10,20)绘制绿色橡树(纹理:粗糙)
// 在(30,40)绘制绿色橡树(纹理:粗糙)  ← 复用已有享元,未创建新对象
11.4 适用场景
  • 系统中存在大量相似对象,消耗大量内存。
  • 对象的大部分状态可以转为外部状态。
  • 典型案例:String 字符串常量池、Integer.valueOf(-128~127) 缓存、游戏中大量相同类型的粒子/子弹/树木。

12. 代理模式 Proxy

12.1 定义与意图

为某对象提供一种代理以控制对该对象的访问,客户端通过代理间接访问目标对象,从而限制、增强或修改其特性。

12.2 代理类型
代理类型 用途 典型案例
虚拟代理 延迟创建开销大的对象 图片懒加载、大对象按需初始化
保护代理 控制对目标的访问权限 权限校验、用户角色控制
远程代理 表示远程对象的本地代理 RPC、RMI
日志代理 记录对目标对象的操作日志 AOP 日志切面
缓存代理 缓存结果避免重复计算 HTTP 缓存、计算结果缓存
智能代理 访问时附加额外操作 引用计数、事务管理
12.3 静态代理实现
java 复制代码
// 服务接口
public interface ImageService {
    void display(String imagePath);
}

// 真实对象(加载耗时)
public class RealImageService implements ImageService {
    private String imagePath;
    private byte[] imageData;
    
    public RealImageService(String imagePath) {
        this.imagePath = imagePath;
        loadFromDisk(); // 模拟耗时加载
    }
    
    private void loadFromDisk() {
        System.out.println("🔄 从磁盘加载图片: " + imagePath);
        // 模拟耗时操作...
    }
    
    @Override
    public void display(String imagePath) {
        System.out.println("📷 显示图片: " + imagePath);
    }
}

// 虚拟代理(延迟加载)
public class LazyImageProxy implements ImageService {
    private final String imagePath;
    private RealImageService realService; // 延迟初始化
    
    public LazyImageProxy(String imagePath) {
        this.imagePath = imagePath;
        // 此时不加载,只保存路径
    }
    
    @Override
    public void display(String imagePath) {
        if (realService == null) {
            realService = new RealImageService(this.imagePath); // 首次访问才加载
        }
        realService.display(imagePath);
    }
}
12.4 动态代理实现(Java JDK)
java 复制代码
// 基于 JDK 动态代理的日志代理(Spring AOP 底层原理)
public class LoggingProxy implements InvocationHandler {
    private final Object target;
    
    public LoggingProxy(Object target) { this.target = target; }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("📝 调用前日志: " + method.getName() + "()");
        long start = System.currentTimeMillis();
        
        Object result = method.invoke(target, args); // 调用真实方法
        
        long elapsed = System.currentTimeMillis() - start;
        System.out.println("📝 调用后日志: " + method.getName() + "() 耗时 " + elapsed + "ms");
        return result;
    }
    
    // 创建代理对象的静态工厂方法
    @SuppressWarnings("unchecked")
    public static <T> T createProxy(T target) {
        return (T) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            new LoggingProxy(target)
        );
    }
}

// 使用
ImageService service = LoggingProxy.createProxy(new RealImageService("photo.jpg"));
service.display("photo.jpg");
// 📝 调用前日志: display()
// 📷 显示图片: photo.jpg
// 📝 调用后日志: display() 耗时 2ms

五、行为型模式(11种)

行为型模式关注对象之间的通信方式,以及如何在它们之间分配职责,强调算法与对象职责的分离。


13. 责任链模式 Chain of Responsibility

13.1 定义与意图

将请求的发送者和接收者解耦,让多个对象都有机会处理请求,将这些对象连成一条链,并沿着链传递请求,直到有对象处理它为止。

13.2 代码实现
java 复制代码
// 抽象处理者
public abstract class SupportHandler {
    protected SupportHandler nextHandler; // 链中的下一个处理者
    
    public SupportHandler setNext(SupportHandler handler) {
        this.nextHandler = handler;
        return handler; // 支持链式调用
    }
    
    public abstract void handle(SupportRequest request);
    
    protected void passToNext(SupportRequest request) {
        if (nextHandler != null) {
            nextHandler.handle(request);
        } else {
            System.out.println("⚠️ 请求无法被处理: " + request.getIssue());
        }
    }
}

// 请求对象
public class SupportRequest {
    private String issue;
    private int level; // 1=普通, 2=技术, 3=管理
    public SupportRequest(String issue, int level) { this.issue = issue; this.level = level; }
    public String getIssue() { return issue; }
    public int getLevel()    { return level; }
}

// 具体处理者:一线客服(处理 level 1)
public class FrontlineSupport extends SupportHandler {
    @Override
    public void handle(SupportRequest request) {
        if (request.getLevel() == 1) {
            System.out.println("👨‍💼 一线客服处理: " + request.getIssue());
        } else {
            System.out.println("↗️ 升级到技术支持...");
            passToNext(request);
        }
    }
}

// 具体处理者:技术支持(处理 level 2)
public class TechnicalSupport extends SupportHandler {
    @Override
    public void handle(SupportRequest request) {
        if (request.getLevel() == 2) {
            System.out.println("👨‍💻 技术工程师处理: " + request.getIssue());
        } else {
            System.out.println("↗️ 升级到管理层...");
            passToNext(request);
        }
    }
}

// 具体处理者:管理层(处理 level 3)
public class ManagementSupport extends SupportHandler {
    @Override
    public void handle(SupportRequest request) {
        System.out.println("👔 管理层处理: " + request.getIssue());
    }
}

// 客户端(构建责任链)
SupportHandler frontline  = new FrontlineSupport();
SupportHandler technical  = new TechnicalSupport();
SupportHandler management = new ManagementSupport();

frontline.setNext(technical).setNext(management); // 链式配置

frontline.handle(new SupportRequest("账号登录问题", 1));   // 一线处理
frontline.handle(new SupportRequest("系统 Bug 复现", 2));  // 技术处理
frontline.handle(new SupportRequest("数据安全事件", 3));   // 管理层处理
13.3 适用场景
  • 请求需要被多个对象之一处理,具体处理者运行时才确定。
  • 希望动态指定处理请求的对象集合。
  • 典型案例:Java Servlet Filter 链、Spring HandlerInterceptor、日志框架的 Appender 链。

14. 命令模式 Command

14.1 定义与意图

将一个请求封装成一个对象,从而可用不同的请求对客户参数化,支持请求的排队、日志记录、撤销/重做操作。

14.2 核心角色
角色 描述
Command(命令接口) 声明 execute()undo() 方法
ConcreteCommand(具体命令) 持有接收者引用,实现具体操作
Receiver(接收者) 实际执行操作的对象
Invoker(调用者) 存储并触发命令,不关心命令具体内容
14.3 代码实现(文本编辑器撤销/重做)
java 复制代码
// 命令接口
public interface Command {
    void execute();
    void undo();
}

// 接收者(文本编辑器)
public class TextEditor {
    private StringBuilder text = new StringBuilder();
    
    public void insertText(String str) { text.append(str); }
    public void deleteText(int length) {
        int len = text.length();
        if (length <= len) text.delete(len - length, len);
    }
    public String getText() { return text.toString(); }
}

// 具体命令:插入文本
public class InsertTextCommand implements Command {
    private final TextEditor editor;
    private final String textToInsert;
    
    public InsertTextCommand(TextEditor editor, String text) {
        this.editor = editor;
        this.textToInsert = text;
    }
    
    @Override public void execute() { editor.insertText(textToInsert); }
    @Override public void undo()    { editor.deleteText(textToInsert.length()); }
}

// 调用者(命令历史管理器)
public class CommandHistory {
    private final Deque<Command> history = new ArrayDeque<>();
    private final Deque<Command> redoStack = new ArrayDeque<>();
    
    public void execute(Command cmd) {
        cmd.execute();
        history.push(cmd);
        redoStack.clear(); // 执行新命令时清空重做栈
    }
    
    public void undo() {
        if (!history.isEmpty()) {
            Command cmd = history.pop();
            cmd.undo();
            redoStack.push(cmd);
        }
    }
    
    public void redo() {
        if (!redoStack.isEmpty()) {
            Command cmd = redoStack.pop();
            cmd.execute();
            history.push(cmd);
        }
    }
}

// 客户端
TextEditor editor = new TextEditor();
CommandHistory history = new CommandHistory();

history.execute(new InsertTextCommand(editor, "Hello"));
history.execute(new InsertTextCommand(editor, " World"));
System.out.println(editor.getText()); // Hello World

history.undo();
System.out.println(editor.getText()); // Hello

history.redo();
System.out.println(editor.getText()); // Hello World
14.4 适用场景
  • 需要支持操作的撤销/重做(Ctrl+Z)。
  • 需要将操作参数化传递、延迟执行或排队(消息队列、任务调度)。
  • 需要记录操作日志以便事务回滚。
  • 典型案例:GUI 菜单/按钮操作、数据库事务、java.lang.Runnable

15. 解释器模式 Interpreter

15.1 定义与意图

给定一种语言,定义它的文法表示,并定义一个解释器,用来解释语言中的句子。

解释器模式适用于频繁使用的、可以用简单语言表达的问题领域,通过构建语法树来解释表达式。

15.2 适用场景
  • 简单的领域特定语言(DSL)解析(SQL、正则表达式、数学表达式)。
  • 编译器/解释器的表达式计算。
  • 典型案例:java.util.regex.Pattern(正则引擎)、SQL 解析器、Spring EL 表达式。
15.3 代码实现(算术表达式解释器)
java 复制代码
// 抽象表达式
public interface Expression {
    int interpret();
}

// 终结符表达式:数字
public class NumberExpression implements Expression {
    private final int number;
    public NumberExpression(int number) { this.number = number; }
    @Override public int interpret() { return number; }
}

// 非终结符表达式:加法
public class AddExpression implements Expression {
    private final Expression left, right;
    public AddExpression(Expression left, Expression right) {
        this.left = left; this.right = right;
    }
    @Override public int interpret() { return left.interpret() + right.interpret(); }
}

// 非终结符表达式:乘法
public class MultiplyExpression implements Expression {
    private final Expression left, right;
    public MultiplyExpression(Expression left, Expression right) {
        this.left = left; this.right = right;
    }
    @Override public int interpret() { return left.interpret() * right.interpret(); }
}

// 客户端(构建语法树并解释)
// 表达式:(3 + 5) * 2 = 16
Expression expr = new MultiplyExpression(
    new AddExpression(new NumberExpression(3), new NumberExpression(5)),
    new NumberExpression(2)
);
System.out.println("结果: " + expr.interpret()); // 结果: 16

16. 迭代器模式 Iterator

16.1 定义与意图

提供一种方法顺序访问一个聚合对象中的各个元素,而不暴露该对象的内部表示。

16.2 代码实现
java 复制代码
// 迭代器接口
public interface Iterator<T> {
    boolean hasNext();
    T next();
}

// 聚合接口
public interface Iterable<T> {
    Iterator<T> createIterator();
}

// 具体聚合:书架
public class BookShelf implements Iterable<String> {
    private final List<String> books = new ArrayList<>();
    
    public void addBook(String book) { books.add(book); }
    
    @Override
    public Iterator<String> createIterator() {
        return new BookShelfIterator();
    }
    
    // 具体迭代器(内部类)
    private class BookShelfIterator implements Iterator<String> {
        private int index = 0;
        
        @Override public boolean hasNext() { return index < books.size(); }
        @Override public String next()     { return books.get(index++); }
    }
}

// Java 内置 Iterable 实现(实现 java.lang.Iterable 支持 for-each)
public class NumberRange implements java.lang.Iterable<Integer> {
    private final int start, end;
    
    public NumberRange(int start, int end) { this.start = start; this.end = end; }
    
    @Override
    public java.util.Iterator<Integer> iterator() {
        return new java.util.Iterator<Integer>() {
            private int current = start;
            public boolean hasNext() { return current <= end; }
            public Integer next()    { return current++; }
        };
    }
}

// 使用
for (int n : new NumberRange(1, 5)) {
    System.out.print(n + " "); // 1 2 3 4 5
}
16.3 适用场景
  • 需要以统一方式遍历不同集合(数组、链表、树等)。
  • 隐藏集合内部结构,只暴露遍历接口。
  • 典型案例:Java java.util.Iterator、Python __iter__、C# IEnumerable

17. 中介者模式 Mediator

17.1 定义与意图

用一个中介对象来封装一系列对象之间的交互,使各对象不需要显式互相引用,从而降低耦合,并且可以独立地改变它们之间的交互。

就像航空管制员(中介者)协调所有飞机(同事对象)而不让飞机直接通信。

17.2 代码实现
java 复制代码
// 中介者接口
public interface ChatMediator {
    void sendMessage(String message, User sender);
    void addUser(User user);
}

// 具体中介者:聊天室
public class ChatRoom implements ChatMediator {
    private final List<User> users = new ArrayList<>();
    
    @Override
    public void addUser(User user) { users.add(user); }
    
    @Override
    public void sendMessage(String message, User sender) {
        for (User user : users) {
            if (user != sender) { // 不发给自己
                user.receive(message, sender.getName());
            }
        }
    }
}

// 同事类(用户)
public class User {
    private final String name;
    private final ChatMediator mediator;
    
    public User(String name, ChatMediator mediator) {
        this.name = name;
        this.mediator = mediator;
        mediator.addUser(this);
    }
    
    public String getName() { return name; }
    
    public void send(String message) {
        System.out.println("[" + name + "] 发送: " + message);
        mediator.sendMessage(message, this); // 通过中介者发送
    }
    
    public void receive(String message, String from) {
        System.out.println("[" + name + "] 收到来自 " + from + " 的消息: " + message);
    }
}

// 客户端
ChatMediator room = new ChatRoom();
User alice = new User("Alice", room);
User bob   = new User("Bob", room);
User carol = new User("Carol", room);

alice.send("大家好!");
// [Alice] 发送: 大家好!
// [Bob] 收到来自 Alice 的消息: 大家好!
// [Carol] 收到来自 Alice 的消息: 大家好!
17.3 适用场景
  • 多个对象之间存在复杂的多对多依赖关系(网状结构变星形结构)。
  • 需要在不修改各个类的前提下改变对象间的交互方式。
  • 典型案例:聊天室、航空管制系统、MVC 中的 Controller、Java GUI 的 EventBus

18. 备忘录模式 Memento

18.1 定义与意图

在不破坏封装性的前提下,捕获并保存一个对象的内部状态,以便日后可以将该对象恢复到保存时的状态。

18.2 代码实现
java 复制代码
// 备忘录(存储状态的不可变对象)
public class EditorMemento {
    private final String content;
    private final int cursorPosition;
    private final long timestamp;
    
    public EditorMemento(String content, int cursorPosition) {
        this.content        = content;
        this.cursorPosition = cursorPosition;
        this.timestamp      = System.currentTimeMillis();
    }
    
    // 只有 Originator 能访问状态(包级访问或内部类)
    String getContent()        { return content; }
    int getCursorPosition()    { return cursorPosition; }
    public long getTimestamp() { return timestamp; }
}

// 原发器(被恢复状态的对象)
public class TextEditor {
    private String content = "";
    private int cursorPosition = 0;
    
    public void type(String text) {
        content += text;
        cursorPosition = content.length();
    }
    
    // 保存当前状态到备忘录
    public EditorMemento save() {
        return new EditorMemento(content, cursorPosition);
    }
    
    // 从备忘录恢复状态
    public void restore(EditorMemento memento) {
        this.content        = memento.getContent();
        this.cursorPosition = memento.getCursorPosition();
    }
    
    public String getContent() { return content; }
}

// 管理者(存储备忘录历史)
public class UndoManager {
    private final Deque<EditorMemento> history = new ArrayDeque<>();
    
    public void saveState(TextEditor editor) {
        history.push(editor.save());
    }
    
    public void undo(TextEditor editor) {
        if (!history.isEmpty()) {
            editor.restore(history.pop());
        }
    }
}

// 客户端
TextEditor editor  = new TextEditor();
UndoManager undo   = new UndoManager();

undo.saveState(editor); // 保存初始状态
editor.type("Hello");
undo.saveState(editor); // 保存 "Hello"
editor.type(" World");
System.out.println(editor.getContent()); // Hello World

undo.undo(editor);
System.out.println(editor.getContent()); // Hello

undo.undo(editor);
System.out.println(editor.getContent()); // (空字符串)
18.3 适用场景
  • 需要提供撤销功能(游戏存档、文本编辑器)。
  • 需要保存对象历史快照以便后续检查。
  • 典型案例:git commit、游戏存档、Ctrl+Z

19. 观察者模式 Observer

19.1 定义与意图

定义对象间一种一对多的依赖关系,当一个对象(Subject/被观察者)的状态发生改变时,所有依赖它的对象(Observer/观察者)都会自动收到通知并更新。

也称为发布-订阅模式(Pub-Sub)(严格来讲二者略有不同)。

19.2 核心角色
角色 描述
Subject(被观察者/发布者) 维护观察者列表,状态变化时通知所有观察者
Observer(观察者/订阅者) 定义 update() 方法,接收通知
ConcreteSubject 实现被观察者,存储具体状态
ConcreteObserver 实现观察者,响应通知执行具体动作
19.3 代码实现
java 复制代码
// 观察者接口
public interface EventListener {
    void update(String eventType, Object data);
}

// 被观察者(事件管理器)
public class EventManager {
    private final Map<String, List<EventListener>> listeners = new HashMap<>();
    
    public void subscribe(String eventType, EventListener listener) {
        listeners.computeIfAbsent(eventType, k -> new ArrayList<>()).add(listener);
    }
    
    public void unsubscribe(String eventType, EventListener listener) {
        List<EventListener> users = listeners.get(eventType);
        if (users != null) users.remove(listener);
    }
    
    public void notify(String eventType, Object data) {
        List<EventListener> users = listeners.getOrDefault(eventType, Collections.emptyList());
        for (EventListener listener : users) {
            listener.update(eventType, data);
        }
    }
}

// 具体被观察者:股票价格
public class StockMarket {
    private final EventManager events = new EventManager();
    private String stockSymbol;
    private double price;
    
    public void addListener(String event, EventListener listener) {
        events.subscribe(event, listener);
    }
    
    public void setPrice(String symbol, double newPrice) {
        String type = newPrice > price ? "上涨" : "下跌";
        this.stockSymbol = symbol;
        this.price = newPrice;
        events.notify(type, symbol + ": ¥" + newPrice);
        events.notify("价格变动", symbol + " 变动为 ¥" + newPrice);
    }
}

// 具体观察者 A:投资者
public class Investor implements EventListener {
    private final String name;
    public Investor(String name) { this.name = name; }
    
    @Override
    public void update(String eventType, Object data) {
        System.out.println("💰 投资者 " + name + " 收到通知 [" + eventType + "]: " + data);
    }
}

// 具体观察者 B:新闻推送
public class NewsAlert implements EventListener {
    @Override
    public void update(String eventType, Object data) {
        System.out.println("📰 财经新闻推送 [" + eventType + "]: " + data);
    }
}

// 客户端
StockMarket market = new StockMarket();
market.addListener("上涨", new Investor("张三"));
market.addListener("下跌", new Investor("李四"));
market.addListener("价格变动", new NewsAlert());

market.setPrice("AAPL", 185.50);
// 💰 投资者 张三 收到通知 [上涨]: AAPL: ¥185.5
// 📰 财经新闻推送 [价格变动]: AAPL 变动为 ¥185.5
19.4 Java 内置支持
java 复制代码
// 使用 java.util.Observable(已废弃,了解即可)
// 推荐使用:java.beans.PropertyChangeSupport
// 或 RxJava、Reactor 等响应式库

// Spring 框架中的应用
@Component
public class MyEventListener {
    @EventListener
    public void handleEvent(ApplicationReadyEvent event) {
        System.out.println("Spring 应用启动完成!");
    }
}
19.5 适用场景
  • 一个对象的变化需要同时更新多个其他对象,且不知道有多少对象需要更新。
  • 实现事件驱动架构、MVC 中 Model 变化通知 View。
  • 典型案例:java.util.EventListener(Swing 所有 UI 事件)、Spring ApplicationEvent、RxJava、前端 Vue/React 响应式系统。

20. 状态模式 State

20.1 定义与意图

允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。

将状态相关的行为抽取到单独的类中,消除大量 if-elseswitch 状态判断代码。

20.2 代码实现
java 复制代码
// 状态接口
public interface VendingMachineState {
    void insertCoin(VendingMachine machine);
    void selectProduct(VendingMachine machine);
    void dispense(VendingMachine machine);
    void ejectCoin(VendingMachine machine);
}

// 上下文类(自动贩卖机)
public class VendingMachine {
    private VendingMachineState currentState;
    private int coins = 0;
    private int stock = 5;
    
    public VendingMachine() {
        currentState = new IdleState(); // 初始状态:空闲
    }
    
    public void setState(VendingMachineState state) { this.currentState = state; }
    public int getCoins()    { return coins; }
    public int getStock()    { return stock; }
    public void addCoin()    { coins++; }
    public void returnCoin() { coins = 0; }
    public void dispenseItem() { if (stock > 0) { stock--; coins = 0; } }
    
    // 委托给当前状态处理
    public void insertCoin()    { currentState.insertCoin(this); }
    public void selectProduct() { currentState.selectProduct(this); }
    public void dispense()      { currentState.dispense(this); }
    public void ejectCoin()     { currentState.ejectCoin(this); }
}

// 具体状态:空闲
public class IdleState implements VendingMachineState {
    @Override public void insertCoin(VendingMachine m) {
        m.addCoin();
        System.out.println("✅ 已投入硬币,请选择商品");
        m.setState(new HasCoinState());
    }
    @Override public void selectProduct(VendingMachine m) { System.out.println("❌ 请先投入硬币"); }
    @Override public void dispense(VendingMachine m)      { System.out.println("❌ 请先投入硬币"); }
    @Override public void ejectCoin(VendingMachine m)     { System.out.println("❌ 没有硬币可退"); }
}

// 具体状态:已投币
public class HasCoinState implements VendingMachineState {
    @Override public void insertCoin(VendingMachine m) { System.out.println("⚠️ 已有硬币,请选择商品"); }
    @Override public void selectProduct(VendingMachine m) {
        System.out.println("✅ 已选择商品,出货中...");
        m.setState(new DispensingState());
        m.dispense();
    }
    @Override public void dispense(VendingMachine m) { System.out.println("❌ 请先选择商品"); }
    @Override public void ejectCoin(VendingMachine m) {
        m.returnCoin();
        System.out.println("💰 已退回硬币");
        m.setState(new IdleState());
    }
}

// 具体状态:出货中
public class DispensingState implements VendingMachineState {
    @Override public void insertCoin(VendingMachine m)    { System.out.println("⚠️ 正在出货,请稍候"); }
    @Override public void selectProduct(VendingMachine m) { System.out.println("⚠️ 正在出货,请稍候"); }
    @Override public void dispense(VendingMachine m) {
        m.dispenseItem();
        System.out.println("🎉 商品已出货!");
        m.setState(m.getStock() > 0 ? new IdleState() : new SoldOutState());
    }
    @Override public void ejectCoin(VendingMachine m) { System.out.println("❌ 正在出货,无法退币"); }
}

// 具体状态:售罄
public class SoldOutState implements VendingMachineState {
    @Override public void insertCoin(VendingMachine m) {
        System.out.println("❌ 商品已售罄,无法投币");
    }
    @Override public void selectProduct(VendingMachine m) { System.out.println("❌ 商品已售罄"); }
    @Override public void dispense(VendingMachine m)      { System.out.println("❌ 商品已售罄"); }
    @Override public void ejectCoin(VendingMachine m)     { System.out.println("❌ 没有硬币可退"); }
}
20.3 适用场景
  • 对象的行为依赖其状态,且状态会频繁切换。
  • 包含大量与状态相关的 if-elseswitch 代码。
  • 典型案例:交通灯、电商订单状态机、游戏角色状态、工作流引擎。

21. 策略模式 Strategy

21.1 定义与意图

定义一系列算法,把它们一个个封装起来,并且使它们可以相互替换。策略模式使算法可独立于使用它的客户端而变化。

21.2 代码实现
java 复制代码
// 策略接口(排序算法)
public interface SortStrategy {
    void sort(int[] array);
}

// 具体策略 A:冒泡排序
public class BubbleSortStrategy implements SortStrategy {
    @Override
    public void sort(int[] array) {
        System.out.println("📊 使用冒泡排序...");
        for (int i = 0; i < array.length - 1; i++) {
            for (int j = 0; j < array.length - 1 - i; j++) {
                if (array[j] > array[j + 1]) {
                    int temp = array[j];
                    array[j] = array[j + 1];
                    array[j + 1] = temp;
                }
            }
        }
    }
}

// 具体策略 B:快速排序
public class QuickSortStrategy implements SortStrategy {
    @Override
    public void sort(int[] array) {
        System.out.println("⚡ 使用快速排序...");
        quickSort(array, 0, array.length - 1);
    }
    
    private void quickSort(int[] arr, int low, int high) {
        if (low < high) {
            int pi = partition(arr, low, high);
            quickSort(arr, low, pi - 1);
            quickSort(arr, pi + 1, high);
        }
    }
    
    private int partition(int[] arr, int low, int high) {
        int pivot = arr[high];
        int i = low - 1;
        for (int j = low; j < high; j++) {
            if (arr[j] < pivot) {
                i++;
                int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp;
            }
        }
        int temp = arr[i + 1]; arr[i + 1] = arr[high]; arr[high] = temp;
        return i + 1;
    }
}

// 上下文类(使用策略的排序器)
public class Sorter {
    private SortStrategy strategy;
    
    public Sorter(SortStrategy strategy) {
        this.strategy = strategy;
    }
    
    // 运行时切换策略
    public void setStrategy(SortStrategy strategy) {
        this.strategy = strategy;
    }
    
    public void sort(int[] array) {
        strategy.sort(array);
    }
}

// 支付场景(更贴近实际)
public interface PaymentStrategy {
    void pay(double amount);
}

public class AlipayStrategy implements PaymentStrategy {
    private final String accountId;
    public AlipayStrategy(String accountId) { this.accountId = accountId; }
    @Override public void pay(double amount) {
        System.out.printf("💳 支付宝 [%s] 支付 ¥%.2f%n", accountId, amount);
    }
}

public class WeChatPayStrategy implements PaymentStrategy {
    @Override public void pay(double amount) {
        System.out.printf("🟢 微信支付 ¥%.2f%n", amount);
    }
}

public class CreditCardStrategy implements PaymentStrategy {
    private final String cardNumber;
    public CreditCardStrategy(String cardNumber) { this.cardNumber = cardNumber; }
    @Override public void pay(double amount) {
        System.out.printf("💳 信用卡 [****%s] 支付 ¥%.2f%n", 
            cardNumber.substring(cardNumber.length() - 4), amount);
    }
}

// 购物车(上下文,可以切换支付策略)
public class ShoppingCart {
    private PaymentStrategy paymentStrategy;
    private double total;
    
    public void setPaymentStrategy(PaymentStrategy strategy) {
        this.paymentStrategy = strategy;
    }
    
    public void addItem(double price) { total += price; }
    
    public void checkout() {
        if (paymentStrategy == null) throw new RuntimeException("请先选择支付方式");
        paymentStrategy.pay(total);
        total = 0;
    }
}

// 客户端
ShoppingCart cart = new ShoppingCart();
cart.addItem(99.9);
cart.addItem(45.0);

cart.setPaymentStrategy(new AlipayStrategy("user@example.com"));
cart.checkout(); // 💳 支付宝 [user@example.com] 支付 ¥144.90

cart.addItem(200.0);
cart.setPaymentStrategy(new WeChatPayStrategy()); // 切换策略
cart.checkout(); // 🟢 微信支付 ¥200.00
21.3 策略 vs 状态
对比维度 策略模式 状态模式
切换时机 客户端主动选择 对象内部自动切换
是否知道彼此 策略类相互独立 状态类可能相互知道(状态转换)
关注点 算法的变化 行为随状态的变化
21.4 适用场景
  • 需要在运行时选择算法(支付方式、排序算法、压缩算法)。
  • 避免多重 if-else 判断不同行为。
  • 典型案例:java.util.Comparator(排序策略)、java.util.Comparator.comparing()、Spring Resource 加载策略。

22. 模板方法模式 Template Method

22.1 定义与意图

在父类中定义一个操作中的算法骨架(模板),而将某些步骤的具体实现延迟到子类中,让子类在不改变算法整体结构的前提下重新定义某些步骤。

22.2 代码实现
java 复制代码
// 抽象类(定义算法骨架)
public abstract class DataMiner {
    
    // 模板方法(定义算法步骤,final 防止子类改变骨架)
    public final void mine(String path) {
        String rawData   = extractData(path);     // 步骤1(可重写)
        String parsedData = parseData(rawData);   // 步骤2(可重写)
        String analysis   = analyzeData(parsedData); // 步骤3(可重写)
        sendReport(analysis);                     // 步骤4(固定实现)
    }
    
    // 抽象步骤(必须由子类实现)
    protected abstract String extractData(String path);
    protected abstract String parseData(String rawData);
    
    // 钩子方法(可选重写,默认空实现)
    protected String analyzeData(String data) {
        return "默认分析结果: " + data.length() + " 字符";
    }
    
    // 固定步骤(不可重写)
    private void sendReport(String report) {
        System.out.println("📧 发送报告: " + report);
    }
}

// 具体类 A:PDF 数据挖掘
public class PDFDataMiner extends DataMiner {
    @Override
    protected String extractData(String path) {
        System.out.println("📄 从 PDF 提取数据: " + path);
        return "PDF 原始数据";
    }
    
    @Override
    protected String parseData(String rawData) {
        System.out.println("🔄 解析 PDF 数据");
        return "已解析的 PDF 数据";
    }
}

// 具体类 B:CSV 数据挖掘(覆写了分析步骤)
public class CSVDataMiner extends DataMiner {
    @Override
    protected String extractData(String path) {
        System.out.println("📊 从 CSV 提取数据: " + path);
        return "CSV 原始数据";
    }
    
    @Override
    protected String parseData(String rawData) {
        System.out.println("🔄 解析 CSV 表格数据");
        return "已解析的 CSV 数据";
    }
    
    @Override
    protected String analyzeData(String data) {
        return "CSV 专项分析: 共 " + data.split(",").length + " 列";
    }
}

// 客户端
DataMiner pdfMiner = new PDFDataMiner();
pdfMiner.mine("report.pdf");

DataMiner csvMiner = new CSVDataMiner();
csvMiner.mine("data.csv");
22.3 适用场景
  • 多个子类有相同的算法骨架,只是某些步骤实现不同。
  • 控制子类扩展点,只允许修改特定步骤。
  • 典型案例:java.io.InputStream.read(byte[])、Junit setUp()/tearDown()、Spring JdbcTemplate(execute 是模板方法)、Servlet doGet()/doPost()

23. 访问者模式 Visitor

23.1 定义与意图

表示一个作用于某对象结构中各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。

23.2 核心角色
角色 描述
Visitor(访问者接口) 为每个具体元素类声明访问方法
ConcreteVisitor(具体访问者) 实现对各元素的具体操作
Element(元素接口) 声明 accept(visitor) 方法
ConcreteElement(具体元素) 实现 accept() 方法,调用 visitor.visit(this)
23.3 代码实现
java 复制代码
// 访问者接口
public interface ShapeVisitor {
    void visit(Circle circle);
    void visit(Rectangle rectangle);
    void visit(Triangle triangle);
}

// 元素接口
public interface Shape {
    void accept(ShapeVisitor visitor);
    String getName();
}

// 具体元素:圆形
public class Circle implements Shape {
    private final double radius;
    public Circle(double radius) { this.radius = radius; }
    public double getRadius()    { return radius; }
    @Override public String getName() { return "圆形"; }
    @Override public void accept(ShapeVisitor visitor) { visitor.visit(this); } // 双分派
}

// 具体元素:矩形
public class Rectangle implements Shape {
    private final double width, height;
    public Rectangle(double width, double height) { this.width = width; this.height = height; }
    public double getWidth()  { return width; }
    public double getHeight() { return height; }
    @Override public String getName() { return "矩形"; }
    @Override public void accept(ShapeVisitor visitor) { visitor.visit(this); }
}

// 具体元素:三角形
public class Triangle implements Shape {
    private final double base, height;
    public Triangle(double base, double height) { this.base = base; this.height = height; }
    public double getBase()   { return base; }
    public double getHeight() { return height; }
    @Override public String getName() { return "三角形"; }
    @Override public void accept(ShapeVisitor visitor) { visitor.visit(this); }
}

// 具体访问者 A:计算面积
public class AreaCalculatorVisitor implements ShapeVisitor {
    private double totalArea = 0;
    
    @Override public void visit(Circle c)    { totalArea += Math.PI * c.getRadius() * c.getRadius(); }
    @Override public void visit(Rectangle r) { totalArea += r.getWidth() * r.getHeight(); }
    @Override public void visit(Triangle t)  { totalArea += 0.5 * t.getBase() * t.getHeight(); }
    
    public double getTotalArea() { return totalArea; }
}

// 具体访问者 B:导出为 XML(无需修改 Shape 类即可新增功能)
public class XmlExportVisitor implements ShapeVisitor {
    private final StringBuilder xml = new StringBuilder("<shapes>\n");
    
    @Override public void visit(Circle c) {
        xml.append("  <circle radius=\"").append(c.getRadius()).append("\"/>\n");
    }
    @Override public void visit(Rectangle r) {
        xml.append("  <rectangle width=\"").append(r.getWidth())
           .append("\" height=\"").append(r.getHeight()).append("\"/>\n");
    }
    @Override public void visit(Triangle t) {
        xml.append("  <triangle base=\"").append(t.getBase())
           .append("\" height=\"").append(t.getHeight()).append("\"/>\n");
    }
    
    public String getXml() { return xml.append("</shapes>").toString(); }
}

// 客户端
List<Shape> shapes = Arrays.asList(
    new Circle(5),
    new Rectangle(4, 6),
    new Triangle(3, 8)
);

AreaCalculatorVisitor areaCalc = new AreaCalculatorVisitor();
XmlExportVisitor xmlExporter   = new XmlExportVisitor();

for (Shape shape : shapes) {
    shape.accept(areaCalc);
    shape.accept(xmlExporter);
}

System.out.printf("总面积: %.2f%n", areaCalc.getTotalArea());
System.out.println(xmlExporter.getXml());
23.4 适用场景
  • 需要对一个对象结构中的对象执行许多不同的、不相关的操作,且不想污染这些对象的类。
  • 对象结构稳定,但操作类型经常变化(新增操作只需新增访问者)。
  • 典型案例:编译器 AST 遍历、XML/JSON 序列化、报表导出。

注意:访问者模式的缺点是难以新增具体元素类型(需要修改所有访问者)。


六、23种模式总览对比表

# 模式名称 分类 核心意图 关键词 常见应用
1 单例 Singleton 创建型 保证全局唯一实例 唯一实例、全局访问 Spring Bean、连接池、Logger
2 工厂方法 Factory Method 创建型 子类决定创建哪种产品 延迟实例化、子类工厂 Calendar、URLConnection
3 抽象工厂 Abstract Factory 创建型 创建一族相关对象 产品族、跨平台 GUI工具包、JDBC
4 建造者 Builder 创建型 分步构建复杂对象 链式调用、可选参数 StringBuilder、AlertDialog
5 原型 Prototype 创建型 克隆已有对象 clone、原型注册表 Object.clone()、JS原型链
6 适配器 Adapter 结构型 接口转换,兼容不兼容接口 包装、转换 InputStreamReader、Arrays.asList
7 桥接 Bridge 结构型 抽象与实现分离,独立变化 组合代继承、两个维度 JDBC Driver、日志Handler
8 组合 Composite 结构型 树形结构统一处理 树形、部分-整体 文件系统、菜单、DOM
9 装饰器 Decorator 结构型 动态增强对象功能 包装、动态叠加 Java I/O流、中间件
10 外观 Facade 结构型 简化复杂子系统接口 门面、统一入口 SLF4J、API网关、SDK
11 享元 Flyweight 结构型 共享大量细粒度对象 内部/外部状态、缓存池 字符串常量池、Integer缓存
12 代理 Proxy 结构型 控制对对象的访问 访问控制、延迟加载 Spring AOP、JDK动态代理
13 责任链 Chain of Responsibility 行为型 请求沿链传递,直到被处理 链、过滤器 Servlet Filter、Spring拦截器
14 命令 Command 行为型 将请求封装为对象 撤销/重做、队列 Runnable、事务、Ctrl+Z
15 解释器 Interpreter 行为型 解释特定语言的语法 语法树、DSL 正则引擎、SQL解析、EL表达式
16 迭代器 Iterator 行为型 统一遍历集合元素 hasNext/next、遍历 java.util.Iterator、for-each
17 中介者 Mediator 行为型 对象通过中介者通信,降低耦合 中心化协调、解耦 聊天室、EventBus、MVC Controller
18 备忘录 Memento 行为型 保存与恢复对象状态 快照、撤销 游戏存档、编辑器历史、git
19 观察者 Observer 行为型 一对多状态通知 订阅/发布、事件 Spring事件、Swing、RxJava
20 状态 State 行为型 状态变化驱动行为变化 状态机、消除if-else 自动贩卖机、订单状态、交通灯
21 策略 Strategy 行为型 封装算法族,可互相替换 算法替换、运行时选择 Comparator、支付方式、排序
22 模板方法 Template Method 行为型 定义算法骨架,子类填充步骤 骨架、钩子方法 JdbcTemplate、Servlet、JUnit
23 访问者 Visitor 行为型 不修改类,添加新操作 双分派、操作分离 AST遍历、编译器、报表导出

七、设计模式选择指南

7.1 按问题场景选择模式

🏗️ 对象创建相关
复制代码
问题:需要控制实例数量(只需1个)         → 单例模式
问题:创建对象逻辑复杂,需要与使用解耦    → 工厂方法 / 抽象工厂
问题:需要创建一族相互兼容的对象          → 抽象工厂
问题:对象有很多可选参数,构造函数臃肿    → 建造者模式
问题:创建成本高,需要快速复制相似对象    → 原型模式
🔧 结构组合相关
复制代码
问题:使用的接口不兼容(旧系统对接)      → 适配器模式
问题:类有多个独立变化维度,继承爆炸      → 桥接模式
问题:需要统一处理单个对象和对象集合       → 组合模式
问题:需要动态给对象添加功能,且可叠加    → 装饰器模式
问题:子系统太复杂,需要简单入口          → 外观模式
问题:需要创建大量相似对象,内存紧张      → 享元模式
问题:需要控制访问、延迟加载、日志、权限  → 代理模式
🔄 行为交互相关
复制代码
问题:请求不知道由谁处理,需逐级传递      → 责任链模式
问题:需要将操作参数化、支持撤销/重做     → 命令模式
问题:需要解释/执行某种简单语言           → 解释器模式
问题:需要统一遍历不同类型的集合          → 迭代器模式
问题:多对象间依赖复杂,需要解耦通信      → 中介者模式
问题:需要保存和恢复对象历史状态          → 备忘录模式
问题:一个对象变化需通知多个其他对象      → 观察者模式
问题:对象行为随内部状态改变,大量if-else → 状态模式
问题:需要在运行时切换算法/行为           → 策略模式
问题:多个子类有相同流程,部分步骤不同    → 模板方法模式
问题:对稳定结构的对象增加新操作          → 访问者模式

7.2 设计模式的误区

  1. 模式不是银弹:简单问题不要强行套用模式,会增加不必要的复杂度。
  2. 过度设计:不要为了"将来可能的扩展"提前引入复杂模式,遵循 YAGNI 原则(You Aren't Gonna Need It)。
  3. 滥用单例:单例是"反模式"的候选者,全局状态难以测试,应谨慎使用。
  4. 混淆相似模式:装饰器 vs 代理、策略 vs 状态、组合 vs 装饰器,需结合意图区分。

7.3 模式之间的关联

复制代码
创建型模式相互关系:
  抽象工厂 ──通常实现为──> 工厂方法
  抽象工厂 ──可以是──>     单例
  建造者   ──可结合──>     复合模式

结构型模式相互关系:
  装饰器   ──类似──>      代理(但意图不同)
  适配器   ──改变接口──>   外观(外观简化,适配器转换)
  组合     ──可结合──>     迭代器(遍历树结构)

行为型模式相互关系:
  策略     ──类似──>      状态(切换方式不同)
  命令     ──可使用──>    备忘录(保存命令历史用于撤销)
  观察者   ──可使用──>    中介者(解耦通信路径)
  模板方法 ──基于继承──>  策略(基于组合)

八、设计模式在主流框架中的应用

8.1 Spring Framework

模式 Spring 中的应用 具体示例
单例 Bean 默认作用域 @Component 默认单例 Bean
工厂方法 Bean 工厂 BeanFactory.getBean()@Bean 工厂方法
抽象工厂 Bean 工厂体系 ApplicationContext 是抽象工厂
代理 AOP 切面 @Transactional@Cacheable(JDK动态代理/CGLIB)
模板方法 数据访问模板 JdbcTemplateRestTemplateRedisTemplate
观察者 事件机制 ApplicationEvent@EventListener
责任链 拦截器链 HandlerInterceptorFilter
装饰器 Bean 后处理器 BeanPostProcessor
策略 资源加载 ResourceLoader、各种 Strategy 接口
外观 简化API JdbcTemplate(外观+模板方法)

8.2 Java 标准库

模式 Java 标准库中的应用
单例 Runtime.getRuntime()System
工厂方法 Calendar.getInstance()NumberFormat.getInstance()
抽象工厂 javax.xml.parsers.DocumentBuilderFactory
建造者 StringBuilderStream.BuilderLocale.Builder
原型 Object.clone()Cloneable 接口
适配器 Arrays.asList()Collections.list()InputStreamReader
装饰器 java.io 流体系(BufferedInputStream 等)
享元 String 常量池、Integer.valueOf(-128~127)
代理 java.lang.reflect.Proxyjava.rmi.*
迭代器 java.util.Iteratorjava.util.Enumeration
观察者 java.util.EventListenerjava.beans.PropertyChangeListener
命令 java.lang.Runnablejava.util.concurrent.Callable
策略 java.util.Comparatorjavax.servlet.http.HttpServlet
模板方法 java.io.InputStream(read 系列方法)、java.util.AbstractList

8.3 前端框架

模式 前端应用
观察者 Vue 响应式系统(Dep/Watcher)、React useState Hook、RxJS
装饰器 ES Decorator(@Component@Injectable in Angular)
代理 Vue 3 使用 Proxy 实现响应式、ES6 Proxy 对象
策略 表单验证策略、路由导航守卫
组合 React 组件树、Vue 组件系统
命令 Redux Action、Event Sourcing
外观 Axios HTTP 客户端封装、各种 SDK

九、一句话总结 23 种模式

模式 一句话记忆
单例 我只有一个,全世界都用我这一个
工厂方法 我定规矩,孩子决定生产什么
抽象工厂 我生产一整套,配件保证兼容
建造者 一步一步来,按需组装复杂对象
原型 复制我自己比新建更高效
适配器 两个不兼容的接口,我来翻译
桥接 形状和颜色分开变,用组合代替继承
组合 树叶和树干统一对待
装饰器 动态穿衣服,可以叠加多件
外观 复杂的事情由我统一对外说一句话
享元 大量相似对象,共享相同部分
代理 我替你挡着,顺便加点料
责任链 一个传一个,直到有人接手
命令 把请求变成对象,想怎么传就怎么传
解释器 我来解读这门小语言
迭代器 不管你是什么集合,我都能遍历你
中介者 别直接找他,通过我联系
备忘录 存档读档,时光倒流
观察者 你有变化,我第一时间通知所有人
状态 我的行为随我的状态而变
策略 算法可以换,接口不变
模板方法 骨架我定,细节你填
访问者 新操作加在外面,不动对象本身

附录:推荐学习资源

经典书籍

  • 📖 《设计模式:可复用面向对象软件的基础》(GoF 原著,Erich Gamma 等著)
  • 📖 《Head First 设计模式》(Freeman 等著,入门首选,图文并茂)
  • 📖 《大话设计模式》(程杰著,中文书,贴近实际场景)
  • 📖 《深入浅出设计模式》(Alexander Shvets 著)

在线资源

实践建议

  1. 先理解意图:每个模式都有特定适用场景,先搞清楚"解决什么问题"。
  2. 看真实案例:通过 JDK/Spring 源码理解模式在真实项目中的用法。
  3. 动手实践:每个模式自己写一遍,比看十遍更有效。
  4. 组合运用:真实项目中往往多个模式组合使用,要善于识别和拆解。
  5. 适度应用:不要为了用模式而用模式,代码的可读性永远是第一位的。

相关推荐
UrSpecial2 小时前
设计模式:模板方法模式
设计模式·模板方法模式
玖釉-2 小时前
告别 Shared Memory 瓶颈:Vulkan Subgroup 架构解析与硬核实战指南
开发语言·c++·windows·图形渲染
枫叶林FYL2 小时前
【Python高级工程与架构实战】项目五:生产级LLM Agent框架:基于PydanticAI的类型安全企业级实现
python·安全·架构
ths5122 小时前
Python 正则表达式学习笔记(小白超详细版)(一)
python·正则表达式
lly2024062 小时前
SQL UPDATE 语句详解
开发语言
君以思为故2 小时前
认识Linux -- 线程同步与互斥
java·开发语言
吴梓穆2 小时前
UE5 C++ 两种枚举
开发语言·c++·ue5
飞Link2 小时前
pprint 全量技术手册:复杂数据结构的结构化输出引擎
开发语言·前端·python
意疏2 小时前
【C语言】解决VScode中文乱码问题
c语言·开发语言·vscode