Java 设计模式完全指南:从入门到精通

Java 设计模式完全指南:从入门到精通

📖 阅读说明:本文约 5 万字,预计阅读/讲解时间 20-30 分钟,适合零基础初学者。全文通过大量生活案例 + 完整代码示例,手把手带你掌握 Java 中 23 种经典设计模式。


目录

[Java 设计模式完全指南:从入门到精通](#Java 设计模式完全指南:从入门到精通)

[1. 什么是设计模式?为什么要学?](#1. 什么是设计模式?为什么要学?)

[1.1 一个有趣的比喻](#1.1 一个有趣的比喻)

[1.2 设计模式的来源](#1.2 设计模式的来源)

[1.3 为什么一定要学设计模式?](#1.3 为什么一定要学设计模式?)

[1.4 怎么学?](#1.4 怎么学?)

[2. 设计模式的六大原则](#2. 设计模式的六大原则)

[2.1 单一职责原则(SRP)](#2.1 单一职责原则(SRP))

[2.2 开放封闭原则(OCP)](#2.2 开放封闭原则(OCP))

[2.3 里氏替换原则(LSP)](#2.3 里氏替换原则(LSP))

[2.4 接口隔离原则(ISP)](#2.4 接口隔离原则(ISP))

[2.5 依赖倒置原则(DIP)](#2.5 依赖倒置原则(DIP))

[2.6 迪米特法则(LoD)](#2.6 迪米特法则(LoD))

[3. 创建型模式](#3. 创建型模式)

[3.1 单例模式(Singleton Pattern)](#3.1 单例模式(Singleton Pattern))

[🍕 生活例子](#🍕 生活例子)

[📋 使用场景](#📋 使用场景)

[💻 代码实现](#💻 代码实现)

[🔍 实际项目中的应用](#🔍 实际项目中的应用)

[⚠️ 单例模式的注意事项](#⚠️ 单例模式的注意事项)

[3.2 工厂方法模式(Factory Method Pattern)](#3.2 工厂方法模式(Factory Method Pattern))

[🍕 生活例子](#🍕 生活例子)

[📋 使用场景](#📋 使用场景)

[💻 代码实现](#💻 代码实现)

[🚀 简单工厂(不是GoF模式,但很常用)](#🚀 简单工厂(不是GoF模式,但很常用))

[🔍 JDK 中的工厂方法模式](#🔍 JDK 中的工厂方法模式)

[3.3 抽象工厂模式(Abstract Factory Pattern)](#3.3 抽象工厂模式(Abstract Factory Pattern))

[🍕 生活例子](#🍕 生活例子)

[📋 使用场景](#📋 使用场景)

[💻 代码实现](#💻 代码实现)

工厂模式三兄弟对比

[3.4 建造者模式(Builder Pattern)](#3.4 建造者模式(Builder Pattern))

[🍕 生活例子](#🍕 生活例子)

[📋 使用场景](#📋 使用场景)

[💻 代码实现](#💻 代码实现)

[🔍 Lombok 的 @Builder 注解](#🔍 Lombok 的 @Builder 注解)

[🔍 JDK/框架中的 Builder 模式](#🔍 JDK/框架中的 Builder 模式)

[3.5 原型模式(Prototype Pattern)](#3.5 原型模式(Prototype Pattern))

[🍕 生活例子](#🍕 生活例子)

[📋 使用场景](#📋 使用场景)

[💻 代码实现](#💻 代码实现)

[浅拷贝 vs 深拷贝](#浅拷贝 vs 深拷贝)

[4. 结构型模式](#4. 结构型模式)

[4.1 适配器模式(Adapter Pattern)](#4.1 适配器模式(Adapter Pattern))

[🍕 生活例子](#🍕 生活例子)

[📋 使用场景](#📋 使用场景)

[💻 代码实现](#💻 代码实现)

[🔍 JDK 中的适配器模式](#🔍 JDK 中的适配器模式)

[4.2 桥接模式(Bridge Pattern)](#4.2 桥接模式(Bridge Pattern))

[🍕 生活例子](#🍕 生活例子)

[💻 代码实现](#💻 代码实现)

[4.3 组合模式(Composite Pattern)](#4.3 组合模式(Composite Pattern))

[🍕 生活例子](#🍕 生活例子)

[💻 代码实现](#💻 代码实现)

[4.4 装饰器模式(Decorator Pattern)](#4.4 装饰器模式(Decorator Pattern))

[🍕 生活例子](#🍕 生活例子)

[📋 使用场景](#📋 使用场景)

[💻 代码实现](#💻 代码实现)

[🔍 Java I/O 中的装饰器模式](#🔍 Java I/O 中的装饰器模式)

[4.5 外观模式(Facade Pattern)](#4.5 外观模式(Facade Pattern))

[🍕 生活例子](#🍕 生活例子)

[📋 使用场景](#📋 使用场景)

[💻 代码实现](#💻 代码实现)

[4.6 享元模式(Flyweight Pattern)](#4.6 享元模式(Flyweight Pattern))

[🍕 生活例子](#🍕 生活例子)

[💻 代码实现](#💻 代码实现)

[🔍 JDK 中的享元模式](#🔍 JDK 中的享元模式)

[4.7 代理模式(Proxy Pattern)](#4.7 代理模式(Proxy Pattern))

[🍕 生活例子](#🍕 生活例子)

[📋 使用场景](#📋 使用场景)

[💻 静态代理](#💻 静态代理)

[💻 动态代理(JDK)](#💻 动态代理(JDK))

[💻 CGLIB 代理(Spring 用于没有接口的类)](#💻 CGLIB 代理(Spring 用于没有接口的类))

[JDK代理 vs CGLIB代理](#JDK代理 vs CGLIB代理)

[5. 行为型模式](#5. 行为型模式)

[5.1 责任链模式(Chain of Responsibility Pattern)](#5.1 责任链模式(Chain of Responsibility Pattern))

[🍕 生活例子](#🍕 生活例子)

[📋 使用场景](#📋 使用场景)

[💻 代码实现](#💻 代码实现)

[5.2 命令模式(Command Pattern)](#5.2 命令模式(Command Pattern))

[🍕 生活例子](#🍕 生活例子)

[📋 使用场景](#📋 使用场景)

[💻 代码实现](#💻 代码实现)

[5.3 迭代器模式(Iterator Pattern)](#5.3 迭代器模式(Iterator Pattern))

[🍕 生活例子](#🍕 生活例子)

[📋 使用场景](#📋 使用场景)

[💻 代码实现](#💻 代码实现)

[5.4 中介者模式(Mediator Pattern)](#5.4 中介者模式(Mediator Pattern))

[🍕 生活例子](#🍕 生活例子)

[💻 代码实现](#💻 代码实现)

[5.5 备忘录模式(Memento Pattern)](#5.5 备忘录模式(Memento Pattern))

[🍕 生活例子](#🍕 生活例子)

[💻 代码实现](#💻 代码实现)

[5.6 观察者模式(Observer Pattern)](#5.6 观察者模式(Observer Pattern))

[🍕 生活例子](#🍕 生活例子)

[📋 使用场景](#📋 使用场景)

[💻 代码实现](#💻 代码实现)

[🔍 Spring 中的观察者模式](#🔍 Spring 中的观察者模式)

[5.7 状态模式(State Pattern)](#5.7 状态模式(State Pattern))

[🍕 生活例子](#🍕 生活例子)

[💻 代码实现](#💻 代码实现)

[5.8 策略模式(Strategy Pattern)](#5.8 策略模式(Strategy Pattern))

[🍕 生活例子](#🍕 生活例子)

[📋 使用场景](#📋 使用场景)

[💻 代码实现](#💻 代码实现)

[5.9 模板方法模式(Template Method Pattern)](#5.9 模板方法模式(Template Method Pattern))

[🍕 生活例子](#🍕 生活例子)

[📋 使用场景](#📋 使用场景)

[💻 代码实现](#💻 代码实现)

[5.10 访问者模式(Visitor Pattern)](#5.10 访问者模式(Visitor Pattern))

[🍕 生活例子](#🍕 生活例子)

[💻 代码实现](#💻 代码实现)

[5.11 解释器模式(Interpreter Pattern)](#5.11 解释器模式(Interpreter Pattern))

[🍕 生活例子](#🍕 生活例子)

[💻 代码实现](#💻 代码实现)

[6. 设计模式在主流框架中的应用](#6. 设计模式在主流框架中的应用)

[6.1 Spring 框架中的设计模式](#6.1 Spring 框架中的设计模式)

[工厂模式:BeanFactory / ApplicationContext](#工厂模式:BeanFactory / ApplicationContext)

[单例模式:Spring Bean 默认作用域](#单例模式:Spring Bean 默认作用域)

[代理模式:Spring AOP](#代理模式:Spring AOP)

[观察者模式:Spring 事件机制](#观察者模式:Spring 事件机制)

模板方法:JdbcTemplate

装饰器模式:HttpServletRequestWrapper

[6.2 MyBatis 中的设计模式](#6.2 MyBatis 中的设计模式)

建造者模式:SqlSessionFactoryBuilder

[代理模式:Mapper 接口](#代理模式:Mapper 接口)

模板方法:BaseExecutor

[6.3 JDK 中的设计模式总结](#6.3 JDK 中的设计模式总结)

[7. 总结与学习建议](#7. 总结与学习建议)

[7.1 23种设计模式速查表](#7.1 23种设计模式速查表)

[7.2 哪些模式最常用?](#7.2 哪些模式最常用?)

[7.3 如何避免过度设计?](#7.3 如何避免过度设计?)

[7.4 学习路线建议](#7.4 学习路线建议)

[7.5 推荐资源](#7.5 推荐资源)

[7.6 一段代码结束全文](#7.6 一段代码结束全文)

写在最后

附录A:设计模式综合实战案例

[A.1 综合案例:设计一个简易 RPC 框架](#A.1 综合案例:设计一个简易 RPC 框架)

[A.2 综合案例:设计一个插件化系统](#A.2 综合案例:设计一个插件化系统)

附录B:常见面试题整理

[B.1 关于单例模式](#B.1 关于单例模式)

[B.2 关于工厂模式](#B.2 关于工厂模式)

[B.3 关于代理模式](#B.3 关于代理模式)

[B.4 关于观察者模式](#B.4 关于观察者模式)

[B.5 关于策略模式](#B.5 关于策略模式)

附录C:设计模式之间的关系图

[附录D:用 Java 8+ 特性简化设计模式](#附录D:用 Java 8+ 特性简化设计模式)

附录E:设计模式反面教材(反模式)

[E.1 贫血模型(Anti-Pattern)](#E.1 贫血模型(Anti-Pattern))

[E.2 万能类(God Object Anti-Pattern)](#E.2 万能类(God Object Anti-Pattern))

[E.3 过度设计(Over-Engineering Anti-Pattern)](#E.3 过度设计(Over-Engineering Anti-Pattern))

附录F:快速记忆口诀



1. 什么是设计模式?为什么要学?

1.1 一个有趣的比喻

想象一下,你是一位厨师。每次做番茄炒蛋,你都从零开始思考:"先放油还是先打蛋?要不要先把番茄切好?"这样每次都要重新想,非常低效。

但如果有一本菜谱,上面写清楚了步骤:先热锅倒油 → 炒鸡蛋盛出 → 炒番茄 → 鸡蛋回锅 → 调味出锅,那每次照着做就行了,又快又好。

设计模式就是软件开发中的"菜谱"。

它是前人(软件大师们)在长期开发实践中总结出来的、针对特定场景 下的最佳解决方案

1.2 设计模式的来源

1994年,四位软件大师(Gang of Four,简称 GoF ,四人帮)出版了一本划时代的书:《设计模式:可复用面向对象软件的基础》

书中总结了面向对象设计中最常见的 23 种设计模式,按用途分为三大类:

类别 数量 作用
创建型模式 5种 解决对象的创建问题
结构型模式 7种 解决对象的组合问题
行为型模式 11种 解决对象的通信问题

1.3 为什么一定要学设计模式?

原因一:代码质量飞跃

没学设计模式之前,你的代码可能是这样的:

复制代码
// ❌ 糟糕的代码:一个方法做了所有的事情
public void processOrder(Order order) {
    // 验证订单
    if (order == null) throw new RuntimeException("订单不能为空");
    if (order.getItems().isEmpty()) throw new RuntimeException("订单不能为空");
    
    // 计算价格
    double total = 0;
    for (Item item : order.getItems()) {
        total += item.getPrice() * item.getQuantity();
    }
    
    // 支付
    if (order.getPayType().equals("alipay")) {
        // 支付宝支付逻辑...
    } else if (order.getPayType().equals("wechat")) {
        // 微信支付逻辑...
    }
    
    // 发送通知
    // 发短信...
    // 发邮件...
    // 发APP推送...
}

学了设计模式之后,你会把它拆分成优雅的结构,每个部分各司其职。

原因二:和同事沟通更顺畅

当你说"这里用策略模式",懂设计模式的同事立刻就明白了你的意图,不需要长篇大论解释。设计模式是程序员之间的共同语言

原因三:看懂主流框架

Spring、MyBatis、JDK 源码中大量使用设计模式。学了才能看懂框架的设计思路:

  • Spring IoC → 工厂模式
  • Spring AOP → 代理模式
  • Spring 事件 → 观察者模式
  • MyBatis SqlSession → 模板方法模式
  • JDK Collections → 迭代器模式

原因四:面试必考

设计模式是中高级 Java 工程师面试的必考内容。

1.4 怎么学?

本文的学习路径:概念 → 生活例子 → 代码例子 → 实际应用

每个模式我们都会用最通俗的语言解释,配合生活中的类比,让你一看就懂。


2. 设计模式的六大原则

学设计模式之前,必须先了解面向对象设计的六大原则,因为所有设计模式都是基于这些原则来的。

2.1 单一职责原则(SRP)

一个类只负责一件事。

就像公司里,财务只管财务,人事只管人事,不能一个人包揽所有。

复制代码
// ❌ 违反单一职责:一个类既管用户数据,又管邮件发送
public class UserService {
    public void saveUser(User user) {
        // 保存用户到数据库
    }
    
    public void sendWelcomeEmail(User user) {
        // 发送欢迎邮件
    }
}

// ✅ 遵循单一职责:分开成两个类
public class UserService {
    public void saveUser(User user) {
        // 只负责保存用户
    }
}

public class EmailService {
    public void sendWelcomeEmail(User user) {
        // 只负责发邮件
    }
}

2.2 开放封闭原则(OCP)

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

意思是:当需要添加新功能时,应该通过新增代码来实现,而不是修改已有代码。

就像手机充电接口,以前用 USB-A,现在用 Type-C,你不需要拆开手机改电路,只需要换个数据线(扩展)就行。

复制代码
// ❌ 违反开放封闭:每次新增支付方式都要修改这个方法
public class PaymentService {
    public void pay(String type, double amount) {
        if (type.equals("alipay")) {
            // 支付宝支付
        } else if (type.equals("wechat")) {
            // 微信支付
        }
        // 新增银联支付?只能修改这里,风险很大!
    }
}

// ✅ 遵循开放封闭:定义接口,新增支付方式只需新增类
public interface Payment {
    void pay(double amount);
}

public class AlipayPayment implements Payment {
    public void pay(double amount) { /* 支付宝逻辑 */ }
}

public class WechatPayment implements Payment {
    public void pay(double amount) { /* 微信支付逻辑 */ }
}

// 新增银联支付,完全不用修改已有代码!
public class UnionpayPayment implements Payment {
    public void pay(double amount) { /* 银联逻辑 */ }
}

2.3 里氏替换原则(LSP)

子类必须能替换父类。

简单说:父类能做的事,子类也必须能做,而且不能破坏父类的行为。

复制代码
// ❌ 违反里氏替换
public class Rectangle {
    protected int width, height;
    public void setWidth(int w) { this.width = w; }
    public void setHeight(int h) { this.height = h; }
    public int getArea() { return width * height; }
}

// 正方形继承矩形,但破坏了矩形的行为!
public class Square extends Rectangle {
    @Override
    public void setWidth(int w) {
        this.width = w;
        this.height = w; // 正方形宽高必须相等,但这破坏了父类的行为
    }
}

// 用矩形的地方替换成正方形,行为不一样了!
Rectangle r = new Square();
r.setWidth(5);
r.setHeight(10);
System.out.println(r.getArea()); // 期望 50,实际输出 100!

2.4 接口隔离原则(ISP)

接口不应该太胖,客户端不应该依赖它不需要的方法。

就像餐厅菜单,不应该把所有菜(早餐、午餐、晚餐、饮料)塞进一张菜单,应该分开。

复制代码
// ❌ 接口太胖
public interface Worker {
    void work();
    void eat();
    void sleep();
}

// 机器人不需要吃饭和睡觉,但被迫实现这两个方法
public class Robot implements Worker {
    public void work() { /* 工作 */ }
    public void eat() { /* 机器人不吃饭,空实现 */ }
    public void sleep() { /* 机器人不睡觉,空实现 */ }
}

// ✅ 接口分离
public interface Workable { void work(); }
public interface Eatable { void eat(); }
public interface Sleepable { void sleep(); }

public class Human implements Workable, Eatable, Sleepable {
    public void work() { }
    public void eat() { }
    public void sleep() { }
}

public class Robot implements Workable {
    public void work() { } // 只实现需要的接口
}

2.5 依赖倒置原则(DIP)

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

就像电源插座,你的电器不应该直接焊死在墙上,而是通过标准插头(抽象接口)来连接。

复制代码
// ❌ 高层依赖低层(具体实现)
public class OrderService {
    private MySQLOrderDao dao = new MySQLOrderDao(); // 直接依赖MySQL实现
    
    public void saveOrder(Order order) {
        dao.save(order);
    }
}

// ✅ 依赖抽象接口
public interface OrderDao {
    void save(Order order);
}

public class MySQLOrderDao implements OrderDao {
    public void save(Order order) { /* MySQL实现 */ }
}

public class MongoOrderDao implements OrderDao {
    public void save(Order order) { /* MongoDB实现 */ }
}

public class OrderService {
    private OrderDao dao; // 依赖接口,不依赖具体实现
    
    // 通过构造器注入(Spring会帮你做这件事)
    public OrderService(OrderDao dao) {
        this.dao = dao;
    }
    
    public void saveOrder(Order order) {
        dao.save(order); // 切换数据库,这里的代码完全不用改
    }
}

2.6 迪米特法则(LoD)

一个对象应该对其他对象了解最少。

又叫"最少知识原则"。就像你去书店买书,你只需要告诉收银员"我要这本书",你不需要知道书是怎么进货的、仓库在哪里。

复制代码
// ❌ 违反迪米特:A 直接操作 C 的内部
public class A {
    public void doSomething(B b) {
        C c = b.getC();
        c.doSomethingElse(); // A 不应该直接调用 C 的方法
    }
}

// ✅ 遵循迪米特:A 只和直接朋友 B 交互
public class B {
    private C c = new C();
    
    public void doSomethingWithC() {
        c.doSomethingElse(); // B 帮 A 去调用 C
    }
}

public class A {
    public void doSomething(B b) {
        b.doSomethingWithC(); // A 只和 B 交互
    }
}

3. 创建型模式

创建型模式关注对象的创建过程,把对象的创建和使用分离,让系统更灵活。


3.1 单例模式(Singleton Pattern)

🍕 生活例子

全国只有一个国务院,不可能有两个。程序中有些对象也只需要一个,比如配置管理器、数据库连接池、日志管理器等。单例模式确保一个类只有一个实例,并提供全局访问点。

📋 使用场景
  • 线程池、数据库连接池
  • 配置文件读取器
  • 日志记录器
  • Spring 中的 Bean(默认单例)
💻 代码实现

方式1:饿汉式(类加载时就创建,线程安全)

复制代码
public class Singleton {
    // 类加载时就创建实例,"饿汉"迫不及待,一上来就准备好
    private static final Singleton INSTANCE = new Singleton();
    
    // 私有构造器,外部无法 new 对象
    private Singleton() {}
    
    // 提供全局访问点
    public static Singleton getInstance() {
        return INSTANCE;
    }
    
    public void doSomething() {
        System.out.println("单例对象做了些事情");
    }
}

// 使用
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1 == s2); // true,是同一个对象

优点 :实现简单,线程安全 缺点:无论用不用都会创建实例,浪费内存(如果对象很重)


方式2:懒汉式(用到时才创建,需要考虑线程安全)

复制代码
// ❌ 线程不安全的懒汉式
public class Singleton {
    private static Singleton instance;
    
    private Singleton() {}
    
    public static Singleton getInstance() {
        // 多线程下可能有多个线程同时进入这里,创建多个实例!
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

方式3:双重检查锁(DCL,推荐)

复制代码
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;
    }
}

为什么要两次 if 判断?

想象两个线程 A 和 B 同时进入了第一个 if,A 先拿到锁创建了实例,然后释放锁。如果没有第二个 if,B 拿到锁后会再创建一个实例!第二个 if 就是为了防止这种情况。


方式4:静态内部类(最优雅,推荐)

复制代码
public class Singleton {
    private Singleton() {}
    
    // 静态内部类:只有在第一次被使用时,JVM 才会加载它
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

为什么这个好?

  • 懒加载 :内部类只在第一次调用 getInstance() 时才加载
  • 线程安全:由 JVM 的类加载机制保证,天然线程安全
  • 代码简洁:不需要 synchronized,性能好

方式5:枚举单例(防反射攻击,最安全)

复制代码
public enum Singleton {
    INSTANCE; // 就这一行!
    
    public void doSomething() {
        System.out.println("枚举单例做了些事情");
    }
}

// 使用
Singleton.INSTANCE.doSomething();

这是《Effective Java》作者 Joshua Bloch 推荐的方式,能防止反射和序列化攻击破坏单例。

🔍 实际项目中的应用
复制代码
// 实际项目中的配置管理器单例
public class ConfigManager {
    private static volatile ConfigManager instance;
    private Properties props;
    
    private ConfigManager() {
        props = new Properties();
        try {
            // 加载配置文件
            InputStream is = getClass().getResourceAsStream("/application.properties");
            props.load(is);
        } catch (IOException e) {
            throw new RuntimeException("加载配置文件失败", e);
        }
    }
    
    public static ConfigManager getInstance() {
        if (instance == null) {
            synchronized (ConfigManager.class) {
                if (instance == null) {
                    instance = new ConfigManager();
                }
            }
        }
        return instance;
    }
    
    public String get(String key) {
        return props.getProperty(key);
    }
    
    public String get(String key, String defaultValue) {
        return props.getProperty(key, defaultValue);
    }
}

// 在任意地方使用
String dbUrl = ConfigManager.getInstance().get("db.url");
String port = ConfigManager.getInstance().get("server.port", "8080");
⚠️ 单例模式的注意事项
  1. 多线程环境:必须保证线程安全
  2. 反射攻击:可以通过反射调用私有构造器破坏单例,枚举方式能防止
  3. 序列化 :反序列化会创建新对象,需要实现 readResolve() 方法
  4. 单元测试:单例难以 Mock,影响测试,要谨慎使用

3.2 工厂方法模式(Factory Method Pattern)

🍕 生活例子

你去麦当劳,不管点什么汉堡,你都说"给我来一个汉堡",麦当劳后厨(工厂)负责制作。你不需要知道汉堡是怎么做的,直接吃就行。

工厂方法模式:定义一个创建对象的接口,让子类决定实例化哪个类。

📋 使用场景
  • 当你不知道该创建哪个具体类时
  • 当你想让子类来决定创建什么对象时
  • 日志框架(Log4j、SLF4J)的 Logger 创建
💻 代码实现

场景:开发一个支持多种数据库的 DAO 层

复制代码
// 第一步:定义产品接口
public interface UserDao {
    void save(User user);
    User findById(int id);
    void delete(int id);
}

// 第二步:实现具体产品
public class MySQLUserDao implements UserDao {
    @Override
    public void save(User user) {
        System.out.println("MySQL: 保存用户 " + user.getName());
        // 实际的MySQL操作...
    }
    
    @Override
    public User findById(int id) {
        System.out.println("MySQL: 查询用户 id=" + id);
        return new User(id, "张三"); // 模拟从MySQL查询
    }
    
    @Override
    public void delete(int id) {
        System.out.println("MySQL: 删除用户 id=" + id);
    }
}

public class OracleUserDao implements UserDao {
    @Override
    public void save(User user) {
        System.out.println("Oracle: 保存用户 " + user.getName());
    }
    
    @Override
    public User findById(int id) {
        System.out.println("Oracle: 查询用户 id=" + id);
        return new User(id, "李四");
    }
    
    @Override
    public void delete(int id) {
        System.out.println("Oracle: 删除用户 id=" + id);
    }
}

// 第三步:定义工厂接口
public interface DaoFactory {
    UserDao createUserDao();
}

// 第四步:实现具体工厂
public class MySQLDaoFactory implements DaoFactory {
    @Override
    public UserDao createUserDao() {
        // 可以在这里初始化MySQL连接、配置等
        return new MySQLUserDao();
    }
}

public class OracleDaoFactory implements DaoFactory {
    @Override
    public UserDao createUserDao() {
        return new OracleUserDao();
    }
}

// 使用
public class Main {
    public static void main(String[] args) {
        // 只需要换一行,切换整个数据库!
        DaoFactory factory = new MySQLDaoFactory();
        // DaoFactory factory = new OracleDaoFactory(); // 切换到Oracle
        
        UserDao userDao = factory.createUserDao();
        userDao.save(new User(1, "张三"));
        User user = userDao.findById(1);
        System.out.println("找到用户:" + user.getName());
    }
}
🚀 简单工厂(不是GoF模式,但很常用)
复制代码
// 简单工厂:用一个工厂类,根据参数创建不同对象
public class LoggerFactory {
    public static Logger getLogger(String type) {
        switch (type) {
            case "file":   return new FileLogger();
            case "db":     return new DatabaseLogger();
            case "console": return new ConsoleLogger();
            default: throw new IllegalArgumentException("未知的日志类型:" + type);
        }
    }
}

// 使用
Logger logger = LoggerFactory.getLogger("file");
logger.log("应用启动");

简单工厂 vs 工厂方法的区别:

简单工厂 工厂方法
违反OCP吗? 是(新增类型要改switch) 否(新增类型只需新增类)
代码复杂度 稍高
使用场景 产品类型固定、不常变化 产品类型会扩展
🔍 JDK 中的工厂方法模式
复制代码
// Calendar.getInstance() 就是工厂方法
Calendar calendar = Calendar.getInstance();

// NumberFormat.getInstance() 
NumberFormat format = NumberFormat.getInstance();

// JDBC 中的工厂方法
Connection conn = DriverManager.getConnection(url, username, password);
Statement stmt = conn.createStatement(); // Connection 是工厂,Statement 是产品

3.3 抽象工厂模式(Abstract Factory Pattern)

🍕 生活例子

你要装修房间,可以选择"中式风格"或"欧式风格"。

选了中式风格,家具(沙发、床、桌子)都是中式的;选了欧式风格,所有家具都是欧式的。你不会中式沙发配欧式床------这就是产品族的概念。

抽象工厂模式:创建一系列相关或相互依赖的对象(产品族),而不需要指定具体类。

📋 使用场景
  • 系统要独立于它的产品的创建、组合和表示
  • 系统要由多个产品系列中的一个来配置
  • 跨数据库开发(需要一套配套的DAO)
💻 代码实现

场景:开发一个UI组件库,支持Windows和Mac两种风格

复制代码
// 产品接口
public interface Button {
    void click();
    void render();
}

public interface TextBox {
    void input(String text);
    String getValue();
}

public interface Dialog {
    void show(String message);
}

// Windows 风格的具体产品
public class WindowsButton implements Button {
    @Override
    public void click() { System.out.println("Windows按钮被点击,带有Windows点击特效"); }
    @Override
    public void render() { System.out.println("渲染Windows风格按钮(矩形、灰色边框)"); }
}

public class WindowsTextBox implements TextBox {
    private String value = "";
    @Override
    public void input(String text) {
        this.value = text;
        System.out.println("Windows文本框输入: " + text);
    }
    @Override
    public String getValue() { return value; }
}

public class WindowsDialog implements Dialog {
    @Override
    public void show(String message) {
        System.out.println("Windows弹窗: [" + message + "] [确定] [取消]");
    }
}

// Mac 风格的具体产品
public class MacButton implements Button {
    @Override
    public void click() { System.out.println("Mac按钮被点击,带有Mac点击特效和声音"); }
    @Override
    public void render() { System.out.println("渲染Mac风格按钮(圆角、阴影效果)"); }
}

public class MacTextBox implements TextBox {
    private String value = "";
    @Override
    public void input(String text) {
        this.value = text;
        System.out.println("Mac文本框输入: " + text);
    }
    @Override
    public String getValue() { return value; }
}

public class MacDialog implements Dialog {
    @Override
    public void show(String message) {
        System.out.println("Mac弹窗: ○ " + message + " [好]");
    }
}

// 抽象工厂接口(创建一整套配套组件)
public interface UIFactory {
    Button createButton();
    TextBox createTextBox();
    Dialog createDialog();
}

// Windows 工厂(创建一套Windows风格组件)
public class WindowsUIFactory implements UIFactory {
    @Override
    public Button createButton() { return new WindowsButton(); }
    @Override
    public TextBox createTextBox() { return new WindowsTextBox(); }
    @Override
    public Dialog createDialog() { return new WindowsDialog(); }
}

// Mac 工厂(创建一套Mac风格组件)
public class MacUIFactory implements UIFactory {
    @Override
    public Button createButton() { return new MacButton(); }
    @Override
    public TextBox createTextBox() { return new MacTextBox(); }
    @Override
    public Dialog createDialog() { return new MacDialog(); }
}

// 应用程序使用抽象工厂,完全不知道具体是哪个平台
public class Application {
    private UIFactory factory;
    private Button button;
    private TextBox textBox;
    private Dialog dialog;
    
    public Application(UIFactory factory) {
        this.factory = factory;
    }
    
    public void createUI() {
        button = factory.createButton();
        textBox = factory.createTextBox();
        dialog = factory.createDialog();
    }
    
    public void run() {
        button.render();
        textBox.input("Hello World");
        button.click();
        dialog.show("操作成功!");
    }
}

// 主程序:根据操作系统选择工厂
public class Main {
    public static void main(String[] args) {
        UIFactory factory;
        
        String os = System.getProperty("os.name").toLowerCase();
        if (os.contains("mac")) {
            factory = new MacUIFactory();
        } else {
            factory = new WindowsUIFactory();
        }
        
        Application app = new Application(factory);
        app.createUI();
        app.run();
    }
}

运行结果(Windows系统):

复制代码
渲染Windows风格按钮(矩形、灰色边框)
Windows文本框输入: Hello World
Windows按钮被点击,带有Windows点击特效
Windows弹窗: [操作成功!] [确定] [取消]
工厂模式三兄弟对比
模式 核心思想 适用场景
简单工厂 一个工厂类,if-else 决定创建哪个 产品较少,不常变化
工厂方法 每个产品对应一个工厂 产品会扩展
抽象工厂 工厂能创建一系列相关产品 有多个产品族,需要保证产品一致性

3.4 建造者模式(Builder Pattern)

🍕 生活例子

你去汉堡店点餐,服务员问你:要什么面包?要什么肉?要什么酱料?要什么蔬菜?

同样是汉堡,可以有无数种组合。建造者(服务员)按你的要求一步步组装。

建造者模式:将一个复杂对象的构建和它的表示分离,同样的构建过程可以创建不同的表示。

📋 使用场景
  • 创建的对象有很多参数(超过4个以上的参数,建议用 Builder)
  • 对象的创建步骤很多,且步骤顺序可能不同
  • Lombok 的 @Builder 注解底层就是这个模式
  • StringBuilder、HttpClient、AlertDialog
💻 代码实现

场景:创建一个复杂的 HTTP 请求对象

复制代码
// ❌ 不用Builder模式的痛苦(构造器参数太多,根本记不住顺序)
HttpRequest request = new HttpRequest(
    "https://api.example.com",  // url
    "POST",                     // method
    "application/json",         // contentType
    30000,                      // timeout
    true,                       // followRedirects
    Map.of("Authorization", "Bearer xxx"), // headers
    "{\"name\": \"张三\"}",     // body
    "UTF-8",                    // charset
    true,                       // compress
    3                           // retryCount
);
// 谁能记住这10个参数的顺序?!

// ✅ 用Builder模式,清晰优雅
public class HttpRequest {
    // 所有字段
    private final String url;
    private final String method;
    private final String contentType;
    private final int timeout;
    private final boolean followRedirects;
    private final Map<String, String> headers;
    private final String body;
    private final String charset;
    private final boolean compress;
    private final int retryCount;
    
    // 私有构造器,只能通过Builder创建
    private HttpRequest(Builder builder) {
        this.url = builder.url;
        this.method = builder.method;
        this.contentType = builder.contentType;
        this.timeout = builder.timeout;
        this.followRedirects = builder.followRedirects;
        this.headers = builder.headers;
        this.body = builder.body;
        this.charset = builder.charset;
        this.compress = builder.compress;
        this.retryCount = builder.retryCount;
    }
    
    // getters...
    public String getUrl() { return url; }
    public String getMethod() { return method; }
    // ... 其他 getter
    
    @Override
    public String toString() {
        return String.format("HttpRequest{url='%s', method='%s', timeout=%d, charset='%s'}",
                url, method, timeout, charset);
    }
    
    // 静态内部 Builder 类
    public static class Builder {
        // 必填参数
        private final String url;
        private String method = "GET";  // 可选参数,有默认值
        
        // 可选参数(带默认值)
        private String contentType = "application/json";
        private int timeout = 5000;
        private boolean followRedirects = true;
        private Map<String, String> headers = new HashMap<>();
        private String body;
        private String charset = "UTF-8";
        private boolean compress = false;
        private int retryCount = 0;
        
        // 必填参数通过构造器传入
        public Builder(String url) {
            if (url == null || url.isEmpty()) {
                throw new IllegalArgumentException("URL不能为空");
            }
            this.url = url;
        }
        
        // 每个 setter 方法返回 Builder 自身,实现链式调用
        public Builder method(String method) {
            this.method = method;
            return this;
        }
        
        public Builder contentType(String contentType) {
            this.contentType = contentType;
            return this;
        }
        
        public Builder timeout(int timeout) {
            if (timeout <= 0) throw new IllegalArgumentException("超时时间必须大于0");
            this.timeout = timeout;
            return this;
        }
        
        public Builder followRedirects(boolean followRedirects) {
            this.followRedirects = followRedirects;
            return this;
        }
        
        public Builder header(String key, String value) {
            this.headers.put(key, value);
            return this;
        }
        
        public Builder body(String body) {
            this.body = body;
            return this;
        }
        
        public Builder charset(String charset) {
            this.charset = charset;
            return this;
        }
        
        public Builder compress(boolean compress) {
            this.compress = compress;
            return this;
        }
        
        public Builder retryCount(int retryCount) {
            this.retryCount = retryCount;
            return this;
        }
        
        // build() 方法创建最终对象
        public HttpRequest build() {
            // 可以在这里做最终校验
            if ("POST".equals(method) || "PUT".equals(method)) {
                if (body == null || body.isEmpty()) {
                    System.out.println("警告:POST/PUT请求没有请求体");
                }
            }
            return new HttpRequest(this);
        }
    }
}

// 优雅的链式调用!
HttpRequest request = new HttpRequest.Builder("https://api.example.com/users")
    .method("POST")
    .contentType("application/json")
    .timeout(30000)
    .header("Authorization", "Bearer eyJhbGci...")
    .header("X-Request-ID", "uuid-123")
    .body("{\"name\": \"张三\", \"age\": 25}")
    .retryCount(3)
    .build();

System.out.println(request);
// 输出: HttpRequest{url='https://api.example.com/users', method='POST', timeout=30000, charset='UTF-8'}
🔍 Lombok 的 @Builder 注解

在实际项目中,我们通常用 Lombok 来自动生成 Builder:

复制代码
import lombok.Builder;
import lombok.ToString;

@Builder
@ToString
public class User {
    private Long id;
    private String name;
    private String email;
    private Integer age;
    private String phone;
}

// Lombok 会自动生成 Builder 代码
User user = User.builder()
    .id(1L)
    .name("张三")
    .email("zhangsan@example.com")
    .age(25)
    .phone("138xxxxxxxx")
    .build();
    
System.out.println(user); // User(id=1, name=张三, email=zhangsan@example.com, age=25, phone=138xxxxxxxx)
🔍 JDK/框架中的 Builder 模式
复制代码
// StringBuilder
String result = new StringBuilder()
    .append("Hello")
    .append(", ")
    .append("World")
    .append("!")
    .toString();

// Java 8 Stream
List<String> result = list.stream()
    .filter(s -> s.length() > 3)
    .map(String::toUpperCase)
    .sorted()
    .collect(Collectors.toList());

// HttpClient(Java 11+)
HttpClient client = HttpClient.newBuilder()
    .version(HttpClient.Version.HTTP_2)
    .followRedirects(HttpClient.Redirect.NORMAL)
    .connectTimeout(Duration.ofSeconds(10))
    .build();

3.5 原型模式(Prototype Pattern)

🍕 生活例子

你画了一幅画,朋友看到后想要一幅一模一样的。你不会重新画一幅,而是复印(克隆)一份给他。

原型模式:通过复制(克隆)现有对象来创建新对象,而不是通过 new 关键字。

📋 使用场景
  • 对象创建成本很高(数据库查询、网络请求、复杂计算)
  • 需要创建大量相似对象
  • 游戏中复制怪物、子弹等大量相似对象
💻 代码实现
复制代码
// Java 中使用 Cloneable 接口实现原型模式
public class GameCharacter implements Cloneable {
    private String name;
    private int health;
    private int attack;
    private int defense;
    private List<String> skills; // 注意:这是引用类型,需要深拷贝
    
    public GameCharacter(String name, int health, int attack, int defense) {
        this.name = name;
        this.health = health;
        this.attack = attack;
        this.defense = defense;
        this.skills = new ArrayList<>();
    }
    
    public void addSkill(String skill) {
        skills.add(skill);
    }
    
    // 浅拷贝(Shallow Copy)
    @Override
    protected GameCharacter clone() throws CloneNotSupportedException {
        return (GameCharacter) super.clone();
        // 注意:浅拷贝中,skills 列表仍然是共享的!
    }
    
    // 深拷贝(Deep Copy):完全独立的新对象
    public GameCharacter deepClone() {
        GameCharacter clone = new GameCharacter(
            this.name, this.health, this.attack, this.defense
        );
        // 手动复制引用类型的字段
        clone.skills = new ArrayList<>(this.skills);
        return clone;
    }
    
    @Override
    public String toString() {
        return String.format("GameCharacter{name='%s', HP=%d, ATK=%d, DEF=%d, skills=%s}",
                name, health, attack, defense, skills);
    }
    
    // getters and setters
    public void setName(String name) { this.name = name; }
    public String getName() { return name; }
    public int getHealth() { return health; }
}

// 使用原型模式快速创建大量相似对象
public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        // 创建一个"精英怪"原型(假设创建过程很耗时)
        GameCharacter eliteMonster = new GameCharacter("精英哥布林", 1000, 150, 80);
        eliteMonster.addSkill("毒刺攻击");
        eliteMonster.addSkill("召唤小怪");
        
        System.out.println("原型: " + eliteMonster);
        
        // 浅拷贝演示
        GameCharacter monster1 = eliteMonster.clone();
        monster1.setName("精英哥布林-1号");
        
        // 深拷贝:技能列表是独立的
        GameCharacter monster2 = eliteMonster.deepClone();
        monster2.setName("精英哥布林-2号");
        monster2.addSkill("烈焰冲撞"); // 只给2号加新技能
        
        System.out.println("1号: " + monster1);
        System.out.println("2号: " + monster2);
        System.out.println("原型: " + eliteMonster); // 原型的技能不受影响
    }
}

输出:

复制代码
原型: GameCharacter{name='精英哥布林', HP=1000, ATK=150, DEF=80, skills=[毒刺攻击, 召唤小怪]}
1号: GameCharacter{name='精英哥布林-1号', HP=1000, ATK=150, DEF=80, skills=[毒刺攻击, 召唤小怪]}
2号: GameCharacter{name='精英哥布林-2号', HP=1000, ATK=150, DEF=80, skills=[毒刺攻击, 召唤小怪, 烈焰冲撞]}
原型: GameCharacter{name='精英哥布林', HP=1000, ATK=150, DEF=80, skills=[毒刺攻击, 召唤小怪]}
浅拷贝 vs 深拷贝
复制代码
原型对象:  [name="精英哥布林"] [health=1000] [skills → List["毒刺","召唤"]]
                                                        ↑
浅拷贝:   [name="1号怪"] [health=1000]     [skills → ](共享同一个List!)
                                                        ↑
深拷贝:   [name="2号怪"] [health=1000]     [skills → List["毒刺","召唤"]](独立的新List!)

4. 结构型模式

结构型模式关注如何将类或对象组合成更大的结构,就像搭积木一样,把小零件拼成大玩意儿。


4.1 适配器模式(Adapter Pattern)

🍕 生活例子

你买了一个美国电器,插头是两脚扁头的,而中国插座是三脚圆头的。你需要一个电源适配器才能用。

适配器模式:将一个类的接口转换成客户期望的另一个接口,让不兼容的接口可以一起工作。

📋 使用场景
  • 整合旧系统(Legacy System)
  • 使用第三方库,但接口不匹配
  • JDK 中:Arrays.asList()Collections.enumeration()、InputStreamReader
💻 代码实现

场景:你的系统用 MediaPlayer 接口,但想使用只支持 AdvancedPlayer 接口的第三方库

复制代码
// 你的系统定义的接口(目标接口)
public interface MediaPlayer {
    void play(String fileName);
}

// 第三方库的接口(被适配者)
public interface AdvancedMediaPlayer {
    void playVlc(String fileName);
    void playMp4(String fileName);
}

// 第三方库的实现(你无法修改)
public class VlcPlayer implements AdvancedMediaPlayer {
    @Override
    public void playVlc(String fileName) {
        System.out.println("用VLC播放器播放: " + fileName);
    }
    @Override
    public void playMp4(String fileName) {
        // VLC播放器不支持mp4
    }
}

public class Mp4Player implements AdvancedMediaPlayer {
    @Override
    public void playVlc(String fileName) {
        // Mp4播放器不支持vlc
    }
    @Override
    public void playMp4(String fileName) {
        System.out.println("用Mp4播放器播放: " + fileName);
    }
}

// 适配器:让 AdvancedMediaPlayer 适配 MediaPlayer 接口
public class MediaPlayerAdapter implements MediaPlayer {
    private AdvancedMediaPlayer advancedPlayer;
    
    public MediaPlayerAdapter(String audioType) {
        if (audioType.equalsIgnoreCase("vlc")) {
            advancedPlayer = new VlcPlayer();
        } else if (audioType.equalsIgnoreCase("mp4")) {
            advancedPlayer = new Mp4Player();
        }
    }
    
    @Override
    public void play(String fileName) {
        // 将 MediaPlayer.play() 转换成 AdvancedMediaPlayer 的方法
        if (advancedPlayer instanceof VlcPlayer) {
            advancedPlayer.playVlc(fileName);
        } else if (advancedPlayer instanceof Mp4Player) {
            advancedPlayer.playMp4(fileName);
        }
    }
}

// 你的播放器(使用你定义的接口)
public class AudioPlayer implements MediaPlayer {
    private MediaPlayerAdapter adapter;
    
    @Override
    public void play(String fileName) {
        // 获取文件扩展名
        String extension = fileName.substring(fileName.lastIndexOf('.') + 1);
        
        if (extension.equalsIgnoreCase("mp3")) {
            System.out.println("直接播放mp3: " + fileName);
        } else if (extension.equalsIgnoreCase("vlc") || extension.equalsIgnoreCase("mp4")) {
            // 通过适配器使用第三方播放器
            adapter = new MediaPlayerAdapter(extension);
            adapter.play(fileName);
        } else {
            System.out.println("不支持的格式:" + extension);
        }
    }
}

// 测试
public class Main {
    public static void main(String[] args) {
        AudioPlayer player = new AudioPlayer();
        
        player.play("my_song.mp3");        // 直接播放mp3
        player.play("movie.mp4");          // 通过适配器用Mp4Player播放
        player.play("documentary.vlc");    // 通过适配器用VlcPlayer播放
        player.play("audio.avi");          // 不支持
    }
}

输出:

复制代码
直接播放mp3: my_song.mp3
用Mp4播放器播放: movie.mp4
用VLC播放器播放: documentary.vlc
不支持的格式:avi
🔍 JDK 中的适配器模式
复制代码
// 1. Arrays.asList() --- 将数组适配成 List
String[] array = {"a", "b", "c"};
List<String> list = Arrays.asList(array); // 数组 → List 的适配

// 2. InputStreamReader --- 将字节流适配成字符流
InputStream inputStream = new FileInputStream("file.txt");
Reader reader = new InputStreamReader(inputStream, "UTF-8"); // 字节流 → 字符流

// 3. Collections.enumeration() --- 将 Collection 适配成 Enumeration
List<String> list2 = new ArrayList<>(Arrays.asList("a", "b", "c"));
Enumeration<String> enumeration = Collections.enumeration(list2);

4.2 桥接模式(Bridge Pattern)

🍕 生活例子

画图时有两个维度:形状 (圆形、方形)和颜色(红色、蓝色)。

如果用继承来实现,你需要:红圆、蓝圆、红方、蓝方------4个类。 如果再加一个颜色或形状,类的数量会爆炸!

桥接模式用组合代替继承,让两个维度独立变化。

桥接模式:将抽象部分与它的实现部分分离,使它们都可以独立地变化。

💻 代码实现
复制代码
// 实现层接口(颜色)
public interface Color {
    String getColor();
    void fill();
}

// 具体颜色实现
public class Red implements Color {
    @Override
    public String getColor() { return "红色"; }
    @Override
    public void fill() { System.out.println("填充红色"); }
}

public class Blue implements Color {
    @Override
    public String getColor() { return "蓝色"; }
    @Override
    public void fill() { System.out.println("填充蓝色"); }
}

public class Green implements Color {
    @Override
    public String getColor() { return "绿色"; }
    @Override
    public void fill() { System.out.println("填充绿色"); }
}

// 抽象层(形状),持有对颜色的引用(这就是"桥")
public abstract class Shape {
    // 持有颜色接口的引用,就是"桥"
    protected Color color;
    
    public Shape(Color color) {
        this.color = color;
    }
    
    public abstract void draw();
}

// 具体形状实现
public class Circle extends Shape {
    private int radius;
    
    public Circle(Color color, int radius) {
        super(color);
        this.radius = radius;
    }
    
    @Override
    public void draw() {
        System.out.println("画一个" + color.getColor() + "圆形,半径=" + radius);
        color.fill();
    }
}

public class Rectangle extends Shape {
    private int width, height;
    
    public Rectangle(Color color, int width, int height) {
        super(color);
        this.width = width;
        this.height = height;
    }
    
    @Override
    public void draw() {
        System.out.println("画一个" + color.getColor() + "矩形," + width + "×" + height);
        color.fill();
    }
}

// 测试:形状和颜色可以自由组合!
public class Main {
    public static void main(String[] args) {
        // 红色圆形
        Shape redCircle = new Circle(new Red(), 10);
        redCircle.draw();
        
        // 蓝色矩形
        Shape blueRect = new Rectangle(new Blue(), 20, 30);
        blueRect.draw();
        
        // 绿色圆形
        Shape greenCircle = new Circle(new Green(), 5);
        greenCircle.draw();
        
        // 新增一种颜色(黄色)只需新增 Yellow 类,不用改任何形状类!
        // 新增一种形状(三角形)只需新增 Triangle 类,不用改任何颜色类!
    }
}

输出:

复制代码
画一个红色圆形,半径=10
填充红色
画一个蓝色矩形,20×30
填充蓝色
画一个绿色圆形,半径=5
填充绿色

桥接模式的核心思想:

复制代码
没有桥接(继承地狱):
Shape
├── RedCircle
├── BlueCircle
├── GreenCircle
├── RedRectangle
├── BlueRectangle
└── GreenRectangle (N种形状 × M种颜色 = N×M个类)

使用桥接(组合):
Shape ─────桥────→ Color
├── Circle          ├── Red
└── Rectangle       ├── Blue
                    └── Green
(N种形状 + M种颜色 = N+M个类,自由组合!)

4.3 组合模式(Composite Pattern)

🍕 生活例子

你的文件系统:有文件夹,文件夹里有文件,文件夹里还可以有子文件夹,子文件夹里还有文件...

无论是文件还是文件夹,你都可以对它执行"计算大小"的操作。这就是组合模式。

组合模式:将对象组合成树形结构来表示"部分-整体"的层次结构,让客户端以一致的方式对待单个对象和组合对象。

💻 代码实现
复制代码
// 组件接口(文件和文件夹的共同抽象)
public abstract class FileSystemItem {
    protected String name;
    
    public FileSystemItem(String name) {
        this.name = name;
    }
    
    public String getName() { return name; }
    
    // 抽象方法,文件和文件夹都要实现
    public abstract long getSize();
    public abstract void display(int indent);
    
    // 文件夹特有操作(叶子节点默认不支持,可以抛异常或空实现)
    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 long getSize() { return size; }
    
    @Override
    public void display(int indent) {
        System.out.println(" ".repeat(indent) + "📄 " + name + " (" + size + " KB)");
    }
}

// 树枝节点:文件夹
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 long getSize() {
        // 文件夹大小 = 所有子项大小之和
        return children.stream().mapToLong(FileSystemItem::getSize).sum();
    }
    
    @Override
    public void display(int indent) {
        System.out.println(" ".repeat(indent) + "📁 " + name + 
                           " (共 " + getSize() + " KB)");
        // 递归显示子项
        for (FileSystemItem child : children) {
            child.display(indent + 4);
        }
    }
}

// 测试:构建文件系统树
public class Main {
    public static void main(String[] args) {
        // 构建文件系统结构
        Folder root = new Folder("我的文档");
        
        // 根目录下的文件
        root.add(new File("简历.docx", 50));
        root.add(new File("合同.pdf", 120));
        
        // 子文件夹:项目
        Folder projects = new Folder("项目");
        projects.add(new File("需求文档.md", 30));
        projects.add(new File("设计图.png", 500));
        
        // 项目下的子文件夹
        Folder codeFolder = new Folder("代码");
        codeFolder.add(new File("Main.java", 10));
        codeFolder.add(new File("Service.java", 25));
        codeFolder.add(new File("Dao.java", 20));
        projects.add(codeFolder);
        
        root.add(projects);
        
        // 另一个子文件夹:图片
        Folder photos = new Folder("图片");
        photos.add(new File("旅游照片.jpg", 3000));
        photos.add(new File("证件照.jpg", 200));
        root.add(photos);
        
        // 显示整个目录树
        root.display(0);
        
        System.out.println("\n总大小: " + root.getSize() + " KB");
    }
}

输出:

复制代码
📁 我的文档 (共 3955 KB)
    📄 简历.docx (50 KB)
    📄 合同.pdf (120 KB)
    📁 项目 (共 585 KB)
        📄 需求文档.md (30 KB)
        📄 设计图.png (500 KB)
        📁 代码 (共 55 KB)
            📄 Main.java (10 KB)
            📄 Service.java (25 KB)
            📄 Dao.java (20 KB)
    📁 图片 (共 3200 KB)
        📄 旅游照片.jpg (3000 KB)
        📄 证件照.jpg (200 KB)

总大小: 3955 KB

4.4 装饰器模式(Decorator Pattern)

🍕 生活例子

你点了一杯普通咖啡,然后可以加牛奶(+3元),加糖(+1元),加奶泡(+5元)......

每次"装饰"都给咖啡增加了新功能(味道、价格),但基础咖啡本身没变。

装饰器模式:动态地给对象添加额外的职责,比继承更灵活。

📋 使用场景
  • Java I/O 流(BufferedReader、GZIPInputStream 等)
  • Spring Web MVC 的 HttpServletRequestWrapper
  • 权限校验、日志记录、缓存等横切关注点
💻 代码实现
复制代码
// 组件接口
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 class Espresso implements Coffee {
    @Override
    public String getDescription() { return "浓缩咖啡"; }
    @Override
    public double getCost() { return 15.0; }
}

// 抽象装饰器(实现同一接口,持有被装饰对象的引用)
public abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;
    
    public CoffeeDecorator(Coffee coffee) {
        this.decoratedCoffee = coffee;
    }
    
    @Override
    public String getDescription() {
        return decoratedCoffee.getDescription();
    }
    
    @Override
    public double getCost() {
        return decoratedCoffee.getCost();
    }
}

// 具体装饰器:加牛奶
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;
    }
}

// 具体装饰器:加糖
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.0;
    }
}

// 具体装饰器:加奶泡
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;
    }
}

// 测试:灵活地给咖啡叠加装饰!
public class CoffeeShop {
    public static void main(String[] args) {
        // 普通咖啡
        Coffee coffee1 = new SimpleCoffee();
        System.out.printf("%-30s ¥%.1f%n", coffee1.getDescription(), coffee1.getCost());
        
        // 拿铁:咖啡 + 牛奶
        Coffee latte = new MilkDecorator(new SimpleCoffee());
        System.out.printf("%-30s ¥%.1f%n", latte.getDescription(), latte.getCost());
        
        // 甜蜜拿铁:咖啡 + 牛奶 + 糖
        Coffee sweetLatte = new SugarDecorator(new MilkDecorator(new SimpleCoffee()));
        System.out.printf("%-30s ¥%.1f%n", sweetLatte.getDescription(), sweetLatte.getCost());
        
        // 豪华咖啡:浓缩 + 牛奶 + 奶泡 + 糖
        Coffee luxuryCoffee = new SugarDecorator(
                                new WhipDecorator(
                                  new MilkDecorator(
                                    new Espresso())));
        System.out.printf("%-30s ¥%.1f%n", luxuryCoffee.getDescription(), luxuryCoffee.getCost());
    }
}

输出:

复制代码
简单咖啡                           ¥10.0
简单咖啡, 加牛奶                   ¥13.0
简单咖啡, 加牛奶, 加糖             ¥14.0
浓缩咖啡, 加牛奶, 加奶泡, 加糖     ¥24.0
🔍 Java I/O 中的装饰器模式
复制代码
// Java I/O 是装饰器模式的经典实现!

// 基础流(原始对象)
InputStream fileInput = new FileInputStream("data.txt");

// 一层一层包装(装饰)
InputStream buffered = new BufferedInputStream(fileInput);    // 加缓冲
InputStream gzip = new GZIPInputStream(buffered);            // 加解压
DataInputStream data = new DataInputStream(gzip);            // 加数据类型读取

// 使用
int value = data.readInt();
String line = new BufferedReader(new InputStreamReader(data)).readLine();

4.5 外观模式(Facade Pattern)

🍕 生活例子

你去饭店吃饭,只需要跟服务员说"我要一份红烧肉"。你不需要知道厨师怎么烧、食材从哪里采购、厨房里的流程。

服务员就是外观(Facade),屏蔽了内部复杂的系统。

外观模式:为子系统中的一组接口提供一个一致的高层接口,让子系统更容易使用。

📋 使用场景
  • 减少客户端与复杂子系统之间的依赖
  • 为子系统提供简单的接口(就像 API 网关)
  • 分层设计中的层与层之间的接口
💻 代码实现

场景:家庭影院系统,需要操作音响、屏幕、灯光、播放器

复制代码
// 子系统1:投影屏幕
public class Screen {
    public void down() {
        System.out.println("🖥️  投影屏幕:缓缓放下");
    }
    public void up() {
        System.out.println("🖥️  投影屏幕:缓缓收起");
    }
}

// 子系统2:音响系统
public class SoundSystem {
    public void on() {
        System.out.println("🔊 音响:开机");
    }
    public void setVolume(int volume) {
        System.out.println("🔊 音响:音量调到 " + volume);
    }
    public void setSurroundMode() {
        System.out.println("🔊 音响:切换到环绕立体声模式");
    }
    public void off() {
        System.out.println("🔊 音响:关机");
    }
}

// 子系统3:灯光控制
public class Lights {
    public void dim(int level) {
        System.out.println("💡 灯光:调暗到 " + level + "%");
    }
    public void on() {
        System.out.println("💡 灯光:全亮");
    }
}

// 子系统4:蓝光播放器
public class BluRayPlayer {
    public void on() {
        System.out.println("📀 蓝光播放器:开机");
    }
    public void play(String movie) {
        System.out.println("📀 蓝光播放器:播放《" + movie + "》");
    }
    public void stop() {
        System.out.println("📀 蓝光播放器:停止");
    }
    public void off() {
        System.out.println("📀 蓝光播放器:关机");
    }
}

// 子系统5:空调
public class AirConditioner {
    public void setTemperature(int temp) {
        System.out.println("❄️  空调:温度设为 " + temp + "°C");
    }
    public void off() {
        System.out.println("❄️  空调:关机");
    }
}

// 外观类:家庭影院外观
public class HomeTheaterFacade {
    private Screen screen;
    private SoundSystem sound;
    private Lights lights;
    private BluRayPlayer player;
    private AirConditioner ac;
    
    public HomeTheaterFacade() {
        this.screen = new Screen();
        this.sound = new SoundSystem();
        this.lights = new Lights();
        this.player = new BluRayPlayer();
        this.ac = new AirConditioner();
    }
    
    // 外观方法:一键开始看电影
    public void watchMovie(String movie) {
        System.out.println("=== 🎬 准备观看《" + movie + "》===");
        lights.dim(10);
        screen.down();
        ac.setTemperature(22);
        sound.on();
        sound.setSurroundMode();
        sound.setVolume(70);
        player.on();
        player.play(movie);
        System.out.println("=== 🍿 电影开始,享受吧!===\n");
    }
    
    // 外观方法:一键结束看电影
    public void endMovie() {
        System.out.println("=== 电影结束,收拾一下 ===");
        player.stop();
        player.off();
        sound.off();
        screen.up();
        lights.on();
        ac.off();
        System.out.println("=== 晚安!===");
    }
}

// 测试:使用外观,简单极了!
public class Main {
    public static void main(String[] args) {
        HomeTheaterFacade theater = new HomeTheaterFacade();
        
        // 一键开始,不需要知道内部细节!
        theater.watchMovie("阿凡达:水之道");
        
        // 看完了
        theater.endMovie();
    }
}

输出:

复制代码
=== 🎬 准备观看《阿凡达:水之道》===
💡 灯光:调暗到 10%
🖥️  投影屏幕:缓缓放下
❄️  空调:温度设为 22°C
🔊 音响:开机
🔊 音响:切换到环绕立体声模式
🔊 音响:音量调到 70
📀 蓝光播放器:开机
📀 蓝光播放器:播放《阿凡达:水之道》
=== 🍿 电影开始,享受吧!===
...
=== 电影结束,收拾一下 ===
...
=== 晚安!===

4.6 享元模式(Flyweight Pattern)

🍕 生活例子

你在玩一个森林游戏,地图上有10万棵树。如果每棵树都单独存储颜色、纹理等信息,内存会爆炸。

但实际上,大多数树是相同种类的,共享同样的颜色和纹理。只有位置不同。

享元模式:通过共享来高效支持大量细粒度的对象(状态分为内部状态/共享 和 外部状态/不共享)。

💻 代码实现
复制代码
// 享元对象:树的类型(共享的内部状态)
public class TreeType {
    private final String name;        // 树种名称(共享)
    private final String color;       // 颜色(共享)
    private final String texture;     // 纹理(共享)
    
    public TreeType(String name, String color, String texture) {
        this.name = name;
        this.color = color;
        this.texture = texture;
        System.out.println("创建树类型: " + name + " (这个对象会被共享)");
    }
    
    // 绘制树(位置是外部状态,不存储在享元中)
    public void draw(int x, int y) {
        System.out.printf("在(%d,%d)画了一棵%s(%s色)%n", x, y, name, color);
    }
}

// 享元工厂:负责管理和复用享元对象
public class TreeTypeFactory {
    // 缓存已创建的享元对象
    private static Map<String, TreeType> treeTypes = new HashMap<>();
    
    public static TreeType getTreeType(String name, String color, String texture) {
        // 生成唯一key
        String key = name + "_" + color + "_" + texture;
        
        // 如果已存在,直接返回(复用)
        if (!treeTypes.containsKey(key)) {
            treeTypes.put(key, new TreeType(name, color, texture));
        }
        
        return treeTypes.get(key);
    }
    
    public static int getTreeTypeCount() {
        return treeTypes.size();
    }
}

// 树(非共享部分:位置,是外部状态)
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);
    }
}

// 森林(管理大量树对象)
public class Forest {
    private List<Tree> 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 Tree(x, y, type));
    }
    
    public void draw() {
        for (Tree tree : trees) {
            tree.draw();
        }
    }
    
    public int getTreeCount() { return trees.size(); }
}

// 测试:种10棵树,但只有3种树类型对象
public class Main {
    public static void main(String[] args) {
        Forest forest = new Forest();
        
        // 种10棵树
        forest.plantTree(1, 2, "松树", "深绿", "松树纹理");
        forest.plantTree(5, 8, "松树", "深绿", "松树纹理"); // 复用!
        forest.plantTree(3, 6, "柳树", "浅绿", "柳树纹理");
        forest.plantTree(7, 1, "松树", "深绿", "松树纹理"); // 复用!
        forest.plantTree(2, 9, "桃树", "粉红", "桃树纹理");
        forest.plantTree(8, 4, "柳树", "浅绿", "柳树纹理"); // 复用!
        forest.plantTree(4, 7, "松树", "深绿", "松树纹理"); // 复用!
        forest.plantTree(6, 3, "桃树", "粉红", "桃树纹理"); // 复用!
        forest.plantTree(9, 5, "柳树", "浅绿", "柳树纹理"); // 复用!
        forest.plantTree(0, 0, "松树", "深绿", "松树纹理"); // 复用!
        
        System.out.println("\n--- 绘制森林 ---");
        forest.draw();
        
        System.out.println("\n--- 统计 ---");
        System.out.println("总树数量: " + forest.getTreeCount());
        System.out.println("树类型对象数量: " + TreeTypeFactory.getTreeTypeCount()); // 只有3个!
        System.out.println("节省了 " + (forest.getTreeCount() - TreeTypeFactory.getTreeTypeCount()) 
                           + " 个重量级对象的内存");
    }
}

输出:

复制代码
创建树类型: 松树 (这个对象会被共享)
创建树类型: 柳树 (这个对象会被共享)
创建树类型: 桃树 (这个对象会被共享)

--- 绘制森林 ---
在(1,2)画了一棵松树(深绿色)
在(5,8)画了一棵松树(深绿色)
...

--- 统计 ---
总树数量: 10
树类型对象数量: 3
节省了 7 个重量级对象的内存
🔍 JDK 中的享元模式
复制代码
// Integer.valueOf() 缓存了 -128 到 127 的对象(享元!)
Integer a = Integer.valueOf(100);
Integer b = Integer.valueOf(100);
System.out.println(a == b); // true,是同一个对象(共享)

Integer c = Integer.valueOf(200);
Integer d = Integer.valueOf(200);
System.out.println(c == d); // false,超出缓存范围,是不同对象

// String.intern() --- 将字符串加入字符串常量池(享元)
String s1 = new String("hello").intern();
String s2 = "hello";
System.out.println(s1 == s2); // true

4.7 代理模式(Proxy Pattern)

🍕 生活例子

你想见某个大老板,但大老板很忙,你需要先通过他的秘书(代理)预约。秘书可以过滤一些不重要的事情,记录见面时间,等等。

代理模式:为对象提供一个代理,控制对这个对象的访问。

📋 使用场景
  • 远程代理:为远程对象提供本地代表(RMI)
  • 虚拟代理:延迟创建开销大的对象
  • 保护代理:控制访问权限
  • 缓存代理:缓存请求结果
  • Spring AOP:底层就是动态代理
💻 静态代理
复制代码
// 主题接口
public interface UserService {
    User getUser(int id);
    void saveUser(User user);
}

// 真实对象
public class UserServiceImpl implements UserService {
    @Override
    public User getUser(int id) {
        System.out.println("从数据库查询用户: id=" + id);
        return new User(id, "张三");
    }
    
    @Override
    public void saveUser(User user) {
        System.out.println("保存用户到数据库: " + user.getName());
    }
}

// 静态代理:日志代理
public class UserServiceLogProxy implements UserService {
    private UserService target; // 持有真实对象
    
    public UserServiceLogProxy(UserService target) {
        this.target = target;
    }
    
    @Override
    public User getUser(int id) {
        System.out.println("[LOG] 开始执行 getUser,参数: id=" + id);
        long start = System.currentTimeMillis();
        
        User user = target.getUser(id); // 调用真实方法
        
        long end = System.currentTimeMillis();
        System.out.println("[LOG] getUser 执行完毕,耗时: " + (end - start) + "ms");
        return user;
    }
    
    @Override
    public void saveUser(User user) {
        System.out.println("[LOG] 开始执行 saveUser,参数: " + user.getName());
        long start = System.currentTimeMillis();
        
        target.saveUser(user);
        
        long end = System.currentTimeMillis();
        System.out.println("[LOG] saveUser 执行完毕,耗时: " + (end - start) + "ms");
    }
}
💻 动态代理(JDK)
复制代码
import java.lang.reflect.*;

// 通用的日志处理器(动态代理的核心)
public class LoggingHandler implements InvocationHandler {
    private Object target;
    
    public LoggingHandler(Object target) {
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("[动态代理] 方法 " + method.getName() + " 开始执行");
        System.out.println("[动态代理] 参数: " + Arrays.toString(args));
        
        long start = System.currentTimeMillis();
        Object result = method.invoke(target, args); // 调用真实方法
        long end = System.currentTimeMillis();
        
        System.out.println("[动态代理] 方法 " + method.getName() + " 执行完毕,耗时: " + (end-start) + "ms");
        System.out.println("[动态代理] 返回值: " + result);
        return result;
    }
    
    // 工厂方法:创建代理对象
    @SuppressWarnings("unchecked")
    public static <T> T createProxy(T target) {
        return (T) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),    // 类加载器
            target.getClass().getInterfaces(),      // 被代理的接口
            new LoggingHandler(target)              // 处理器
        );
    }
}

// 测试动态代理
public class Main {
    public static void main(String[] args) {
        UserService realService = new UserServiceImpl();
        
        // 动态创建代理,不需要手动写代理类!
        UserService proxy = LoggingHandler.createProxy(realService);
        
        // 使用代理(和使用真实对象完全一样)
        User user = proxy.getUser(1);
        proxy.saveUser(new User(2, "李四"));
    }
}
💻 CGLIB 代理(Spring 用于没有接口的类)
复制代码
// Spring Boot 项目中,引入 spring-core 即可使用 CGLIB
import org.springframework.cglib.proxy.*;

// 不实现任何接口的类
public class OrderService {
    public void createOrder(String productId) {
        System.out.println("创建订单: " + productId);
    }
}

// CGLIB 代理(通过继承,生成子类来代理)
public class CglibLoggingProxy implements MethodInterceptor {
    private Object target;
    
    public CglibLoggingProxy(Object target) {
        this.target = target;
    }
    
    @Override
    public Object intercept(Object obj, Method method, Object[] args, 
                            MethodProxy proxy) throws Throwable {
        System.out.println("[CGLIB] 前置处理: " + method.getName());
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("[CGLIB] 后置处理: " + method.getName());
        return result;
    }
    
    @SuppressWarnings("unchecked")
    public static <T> T createProxy(T target) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(new CglibLoggingProxy(target));
        return (T) enhancer.create();
    }
}

// 使用
OrderService proxy = CglibLoggingProxy.createProxy(new OrderService());
proxy.createOrder("PROD-001");
JDK代理 vs CGLIB代理
JDK 动态代理 CGLIB 代理
要求 必须有接口 无需接口,但类不能是 final
原理 实现接口 继承(生成子类)
性能 较低(反射) 较高(字节码)
Spring 选择 有接口时用 没接口时用

5. 行为型模式

行为型模式关注对象之间的通信和职责分配,让对象之间的交互更加灵活。


5.1 责任链模式(Chain of Responsibility Pattern)

🍕 生活例子

你在公司申请报销,金额不同,需要不同级别的审批:

  • 1000元以下:组长审批
  • 1000-5000元:经理审批
  • 5000-20000元:总监审批
  • 20000元以上:CEO审批

申请沿着链向上传递,直到有人能处理为止。

责任链模式:将多个处理者连成链,请求沿链传递,直到有处理者处理。

📋 使用场景
  • OA 审批流程
  • Spring Security 过滤器链
  • Netty Pipeline
  • Servlet 过滤器
💻 代码实现
复制代码
// 审批处理者抽象类
public abstract class Approver {
    protected String name;
    protected Approver nextApprover; // 下一个处理者
    
    public Approver(String name) {
        this.name = name;
    }
    
    // 设置下一个处理者(链式构建)
    public Approver setNext(Approver next) {
        this.nextApprover = next;
        return next; // 返回next便于链式调用
    }
    
    // 抽象处理方法
    public abstract void handle(ExpenseRequest request);
    
    // 传递给下一个处理者
    protected void passToNext(ExpenseRequest request) {
        if (nextApprover != null) {
            nextApprover.handle(request);
        } else {
            System.out.println("❌ 报销申请 [" + request.getDescription() + 
                               "] 金额: ¥" + request.getAmount() + " 无人审批,退回");
        }
    }
}

// 报销申请
public class ExpenseRequest {
    private String description;
    private double amount;
    private String applicant;
    
    public ExpenseRequest(String description, double amount, String applicant) {
        this.description = description;
        this.amount = amount;
        this.applicant = applicant;
    }
    
    public String getDescription() { return description; }
    public double getAmount() { return amount; }
    public String getApplicant() { return applicant; }
}

// 具体处理者:组长(审批1000以下)
public class TeamLeader extends Approver {
    public TeamLeader(String name) { super(name); }
    
    @Override
    public void handle(ExpenseRequest request) {
        if (request.getAmount() <= 1000) {
            System.out.printf("✅ 组长[%s]审批了[%s]的报销申请: %s,金额: ¥%.2f%n",
                    name, request.getApplicant(), request.getDescription(), request.getAmount());
        } else {
            System.out.printf("⬆️  组长[%s]: ¥%.2f 超出我的权限,转交经理%n", name, request.getAmount());
            passToNext(request);
        }
    }
}

// 具体处理者:经理(审批5000以下)
public class Manager extends Approver {
    public Manager(String name) { super(name); }
    
    @Override
    public void handle(ExpenseRequest request) {
        if (request.getAmount() <= 5000) {
            System.out.printf("✅ 经理[%s]审批了[%s]的报销申请: %s,金额: ¥%.2f%n",
                    name, request.getApplicant(), request.getDescription(), request.getAmount());
        } else {
            System.out.printf("⬆️  经理[%s]: ¥%.2f 超出我的权限,转交总监%n", name, request.getAmount());
            passToNext(request);
        }
    }
}

// 具体处理者:总监(审批20000以下)
public class Director extends Approver {
    public Director(String name) { super(name); }
    
    @Override
    public void handle(ExpenseRequest request) {
        if (request.getAmount() <= 20000) {
            System.out.printf("✅ 总监[%s]审批了[%s]的报销申请: %s,金额: ¥%.2f%n",
                    name, request.getApplicant(), request.getDescription(), request.getAmount());
        } else {
            System.out.printf("⬆️  总监[%s]: ¥%.2f 超出我的权限,转交CEO%n", name, request.getAmount());
            passToNext(request);
        }
    }
}

// 具体处理者:CEO(审批所有)
public class CEO extends Approver {
    public CEO(String name) { super(name); }
    
    @Override
    public void handle(ExpenseRequest request) {
        System.out.printf("✅ CEO[%s]审批了[%s]的报销申请: %s,金额: ¥%.2f%n",
                name, request.getApplicant(), request.getDescription(), request.getAmount());
    }
}

// 测试:构建审批链
public class Main {
    public static void main(String[] args) {
        // 构建责任链:组长 → 经理 → 总监 → CEO
        Approver teamLeader = new TeamLeader("王组长");
        Approver manager = new Manager("李经理");
        Approver director = new Director("赵总监");
        Approver ceo = new CEO("张总");
        
        // 链式设置下一个处理者
        teamLeader.setNext(manager).setNext(director).setNext(ceo);
        
        // 提交不同金额的申请
        System.out.println("=== 提交报销申请 ===");
        teamLeader.handle(new ExpenseRequest("购买办公用品", 500, "小明"));
        System.out.println();
        teamLeader.handle(new ExpenseRequest("团队建设活动", 3000, "小红"));
        System.out.println();
        teamLeader.handle(new ExpenseRequest("采购服务器", 15000, "小张"));
        System.out.println();
        teamLeader.handle(new ExpenseRequest("收购竞争对手", 100000, "小李"));
    }
}

输出:

复制代码
=== 提交报销申请 ===
✅ 组长[王组长]审批了[小明]的报销申请: 购买办公用品,金额: ¥500.00

⬆️  组长[王组长]: ¥3000.00 超出我的权限,转交经理
✅ 经理[李经理]审批了[小红]的报销申请: 团队建设活动,金额: ¥3000.00

⬆️  组长[王组长]: ¥15000.00 超出我的权限,转交经理
⬆️  经理[李经理]: ¥15000.00 超出我的权限,转交总监
✅ 总监[赵总监]审批了[小张]的报销申请: 采购服务器,金额: ¥15000.00

⬆️  (一路传递到CEO...)
✅ CEO[张总]审批了[小李]的报销申请: 收购竞争对手,金额: ¥100000.00

5.2 命令模式(Command Pattern)

🍕 生活例子

你用遥控器控制电视:按"开机"按钮、按"调高音量"按钮、按"换台"按钮......

每个按钮背后对应一个"命令"。命令可以被记录(实现撤销功能),可以被排队执行,可以被重新执行。

命令模式:将请求封装成对象,从而可以用不同的请求来参数化客户端,也可以队列或记录请求,以及支持撤销操作。

📋 使用场景
  • 撤销/重做功能(Ctrl+Z / Ctrl+Y)
  • 事务管理(一组命令要么全成功,要么全撤销)
  • 操作日志(把命令序列化存储)
  • 消息队列(命令序列化后发给消费者)
💻 代码实现
复制代码
// 命令接口
public interface Command {
    void execute();
    void undo(); // 撤销
}

// 接收者:文本编辑器
public class TextEditor {
    private StringBuilder text = new StringBuilder();
    
    public void insertText(String content, int position) {
        text.insert(position, content);
        System.out.println("插入后: [" + text.toString() + "]");
    }
    
    public void deleteText(int from, int to) {
        text.delete(from, to);
        System.out.println("删除后: [" + text.toString() + "]");
    }
    
    public String getText() { return text.toString(); }
    public void setText(String t) { text = new StringBuilder(t); }
}

// 具体命令:插入文本
public class InsertTextCommand implements Command {
    private TextEditor editor;
    private String content;
    private int position;
    
    public InsertTextCommand(TextEditor editor, String content, int position) {
        this.editor = editor;
        this.content = content;
        this.position = position;
    }
    
    @Override
    public void execute() {
        System.out.print("执行: 在位置" + position + "插入[" + content + "] → ");
        editor.insertText(content, position);
    }
    
    @Override
    public void undo() {
        System.out.print("撤销: 删除位置" + position + "的[" + content + "] → ");
        editor.deleteText(position, position + content.length());
    }
}

// 具体命令:删除文本
public class DeleteTextCommand implements Command {
    private TextEditor editor;
    private int from, to;
    private String deletedContent; // 保存被删除的内容,用于撤销
    
    public DeleteTextCommand(TextEditor editor, int from, int to) {
        this.editor = editor;
        this.from = from;
        this.to = to;
    }
    
    @Override
    public void execute() {
        deletedContent = editor.getText().substring(from, to);
        System.out.print("执行: 删除位置" + from + "-" + to + " → ");
        editor.deleteText(from, to);
    }
    
    @Override
    public void undo() {
        System.out.print("撤销: 恢复[" + deletedContent + "] → ");
        editor.insertText(deletedContent, from);
    }
}

// 调用者:命令历史管理器(支持撤销/重做)
public class CommandHistory {
    private Deque<Command> history = new ArrayDeque<>();
    private Deque<Command> redoStack = new ArrayDeque<>();
    
    public void execute(Command command) {
        command.execute();
        history.push(command);
        redoStack.clear(); // 执行新命令后清空重做栈
    }
    
    public void undo() {
        if (!history.isEmpty()) {
            Command command = history.pop();
            System.out.print("↩️  ");
            command.undo();
            redoStack.push(command);
        } else {
            System.out.println("没有可撤销的操作");
        }
    }
    
    public void redo() {
        if (!redoStack.isEmpty()) {
            Command command = redoStack.pop();
            System.out.print("↪️  ");
            command.execute();
            history.push(command);
        } else {
            System.out.println("没有可重做的操作");
        }
    }
}

// 测试
public class Main {
    public static void main(String[] args) {
        TextEditor editor = new TextEditor();
        CommandHistory history = new CommandHistory();
        
        // 执行一系列操作
        history.execute(new InsertTextCommand(editor, "Hello", 0));
        history.execute(new InsertTextCommand(editor, " World", 5));
        history.execute(new InsertTextCommand(editor, "!", 11));
        
        System.out.println("\n当前文本: " + editor.getText());
        System.out.println();
        
        // 撤销两次
        history.undo();
        history.undo();
        System.out.println("撤销后: " + editor.getText());
        System.out.println();
        
        // 重做一次
        history.redo();
        System.out.println("重做后: " + editor.getText());
    }
}

5.3 迭代器模式(Iterator Pattern)

🍕 生活例子

你有一个装满东西的书包,要一样一样地取出来检查。迭代器就像一只手,帮你一个一个地取,你不用知道书包内部是怎么存放的。

迭代器模式:提供一种方法顺序访问集合对象中的元素,而不暴露集合的内部结构。

📋 使用场景
  • Java IteratorIterable 接口
  • 增强for循环(for-each)底层就是迭代器
  • 遍历各种数据结构(数组、链表、树、图)
💻 代码实现
复制代码
// Java 内置的 Iterator 接口
public interface Iterator<T> {
    boolean hasNext();
    T next();
}

// 自定义集合
public class NumberCollection implements Iterable<Integer> {
    private int[] numbers;
    private int size;
    
    public NumberCollection(int capacity) {
        numbers = new int[capacity];
        size = 0;
    }
    
    public void add(int number) {
        if (size < numbers.length) {
            numbers[size++] = number;
        }
    }
    
    // 正向迭代器
    @Override
    public java.util.Iterator<Integer> iterator() {
        return new ForwardIterator();
    }
    
    // 反向迭代器
    public java.util.Iterator<Integer> reverseIterator() {
        return new ReverseIterator();
    }
    
    // 内部类:正向迭代器
    private class ForwardIterator implements java.util.Iterator<Integer> {
        private int index = 0;
        
        @Override
        public boolean hasNext() { return index < size; }
        
        @Override
        public Integer next() {
            if (!hasNext()) throw new java.util.NoSuchElementException();
            return numbers[index++];
        }
    }
    
    // 内部类:反向迭代器
    private class ReverseIterator implements java.util.Iterator<Integer> {
        private int index = size - 1;
        
        @Override
        public boolean hasNext() { return index >= 0; }
        
        @Override
        public Integer next() {
            if (!hasNext()) throw new java.util.NoSuchElementException();
            return numbers[index--];
        }
    }
}

// 测试
public class Main {
    public static void main(String[] args) {
        NumberCollection collection = new NumberCollection(10);
        for (int i = 1; i <= 5; i++) collection.add(i * 10);
        
        // 使用 for-each(底层用迭代器)
        System.out.print("正向遍历: ");
        for (int num : collection) {
            System.out.print(num + " ");
        }
        
        System.out.print("\n反向遍历: ");
        java.util.Iterator<Integer> it = collection.reverseIterator();
        while (it.hasNext()) {
            System.out.print(it.next() + " ");
        }
        // 输出: 反向遍历: 50 40 30 20 10
    }
}

5.4 中介者模式(Mediator Pattern)

🍕 生活例子

机场的航班调度中心:所有飞机起降都要通过塔台(中介者)协调,飞机们不直接沟通,避免混乱。

如果没有塔台,飞机们需要互相通讯,网状结构会极其复杂。

中介者模式:用中介对象来封装一系列对象之间的交互,使对象们不直接相互引用,降低耦合。

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

// 用户(同事类)
public abstract class User {
    protected ChatMediator mediator;
    protected String name;
    
    public User(ChatMediator mediator, String name) {
        this.mediator = mediator;
        this.name = name;
    }
    
    public abstract void send(String message);
    public abstract void receive(String message, String senderName);
    
    public String getName() { return name; }
}

// 具体用户
public class ChatUser extends User {
    public ChatUser(ChatMediator mediator, String name) {
        super(mediator, name);
    }
    
    @Override
    public void send(String message) {
        System.out.println(name + " 发送消息: " + message);
        mediator.sendMessage(message, this); // 通过中介者发送
    }
    
    @Override
    public void receive(String message, String senderName) {
        System.out.println("  → " + name + " 收到来自 " + senderName + " 的消息: " + message);
    }
}

// 具体中介者:聊天室
public class ChatRoom implements ChatMediator {
    private List<User> users = new ArrayList<>();
    
    @Override
    public void addUser(User user) {
        users.add(user);
        System.out.println("=== " + user.getName() + " 加入了聊天室 ===");
    }
    
    @Override
    public void sendMessage(String message, User sender) {
        // 广播给除发送者外的所有用户
        for (User user : users) {
            if (user != sender) {
                user.receive(message, sender.getName());
            }
        }
    }
}

// 测试
public class Main {
    public static void main(String[] args) {
        ChatMediator chatRoom = new ChatRoom();
        
        User alice = new ChatUser(chatRoom, "Alice");
        User bob = new ChatUser(chatRoom, "Bob");
        User charlie = new ChatUser(chatRoom, "Charlie");
        
        chatRoom.addUser(alice);
        chatRoom.addUser(bob);
        chatRoom.addUser(charlie);
        
        System.out.println();
        alice.send("大家好!");
        System.out.println();
        bob.send("你好,Alice!");
    }
}

输出:

复制代码
=== Alice 加入了聊天室 ===
=== Bob 加入了聊天室 ===
=== Charlie 加入了聊天室 ===

Alice 发送消息: 大家好!
  → Bob 收到来自 Alice 的消息: 大家好!
  → Charlie 收到来自 Alice 的消息: 大家好!

Bob 发送消息: 你好,Alice!
  → Alice 收到来自 Bob 的消息: 你好,Alice!
  → Charlie 收到来自 Bob 的消息: 你好,Alice!

5.5 备忘录模式(Memento Pattern)

🍕 生活例子

玩游戏时,在打Boss之前先存档(Save),打输了就读档(Load)回到存档时的状态。

备忘录模式:在不破坏封装的前提下,捕获并保存对象的内部状态,以便以后恢复。

💻 代码实现
复制代码
// 备忘录类(保存状态的快照)
public class GameMemento {
    private final int level;
    private final int health;
    private final int gold;
    private final String location;
    private final String saveTime;
    
    public GameMemento(int level, int health, int gold, String location) {
        this.level = level;
        this.health = health;
        this.gold = gold;
        this.location = location;
        this.saveTime = new java.util.Date().toString();
    }
    
    // 只有 get 方法,不提供 set(保护快照)
    public int getLevel() { return level; }
    public int getHealth() { return health; }
    public int getGold() { return gold; }
    public String getLocation() { return location; }
    public String getSaveTime() { return saveTime; }
    
    @Override
    public String toString() {
        return String.format("存档[Lv%d, HP%d, 金币%d, 位置:%s]", level, health, gold, location);
    }
}

// 游戏角色(原发器)
public class GameCharacter {
    private String name;
    private int level;
    private int health;
    private int gold;
    private String location;
    
    public GameCharacter(String name) {
        this.name = name;
        this.level = 1;
        this.health = 100;
        this.gold = 0;
        this.location = "新手村";
    }
    
    // 保存状态(创建备忘录)
    public GameMemento save() {
        System.out.println("💾 " + name + " 存档: " + this);
        return new GameMemento(level, health, gold, location);
    }
    
    // 恢复状态(从备忘录恢复)
    public void restore(GameMemento memento) {
        this.level = memento.getLevel();
        this.health = memento.getHealth();
        this.gold = memento.getGold();
        this.location = memento.getLocation();
        System.out.println("📂 " + name + " 读档: " + this);
    }
    
    // 游戏事件
    public void gainExp() {
        level++;
        System.out.println("⬆️  " + name + " 升级! → 当前: " + this);
    }
    
    public void takeDamage(int damage) {
        health -= damage;
        System.out.println("💥 " + name + " 受到" + damage + "点伤害 → 当前: " + this);
    }
    
    public void earnGold(int amount) {
        gold += amount;
    }
    
    public void moveTo(String place) {
        location = place;
        System.out.println("🚶 " + name + " 移动到 " + place);
    }
    
    public boolean isDead() { return health <= 0; }
    
    @Override
    public String toString() {
        return String.format("[Lv%d, HP%d, 金币%d, 位置:%s]", level, health, gold, location);
    }
}

// 存档管理器(负责人)
public class SaveManager {
    private Map<String, GameMemento> saves = new LinkedHashMap<>();
    
    public void save(String slotName, GameMemento memento) {
        saves.put(slotName, memento);
        System.out.println("   ✅ 保存到存档槽[" + slotName + "]");
    }
    
    public GameMemento load(String slotName) {
        GameMemento memento = saves.get(slotName);
        if (memento == null) throw new RuntimeException("存档槽[" + slotName + "]不存在");
        System.out.println("   📂 从存档槽[" + slotName + "]加载: " + memento);
        return memento;
    }
    
    public void listSaves() {
        System.out.println("--- 所有存档 ---");
        saves.forEach((slot, mem) -> System.out.println("  [" + slot + "]: " + mem));
    }
}

// 测试
public class Main {
    public static void main(String[] args) {
        GameCharacter hero = new GameCharacter("勇者小明");
        SaveManager saveManager = new SaveManager();
        
        // 探索新手村
        hero.earnGold(100);
        hero.gainExp();
        
        // 在进入危险地区前存档
        saveManager.save("槽位1", hero.save());
        
        // 进入危险地区
        hero.moveTo("暗黑森林");
        hero.takeDamage(60);
        hero.gainExp();
        
        // 在Boss门口存档
        saveManager.save("槽位2", hero.save());
        
        // 挑战Boss,失败了
        System.out.println("\n=== 挑战Boss ===");
        hero.takeDamage(200); // 被打死了
        System.out.println("💀 " + hero.isDead() ? "游戏结束!" : "");
        
        // 读取Boss前的存档
        System.out.println("\n=== 读取存档,重新挑战 ===");
        hero.restore(saveManager.load("槽位2"));
        
        System.out.println("\n=== 所有存档 ===");
        saveManager.listSaves();
    }
}

5.6 观察者模式(Observer Pattern)

🍕 生活例子

你关注了某个微信公众号,每当它发布新文章,你就会收到推送通知。公众号是"发布者",你是"订阅者"(观察者)。

观察者模式(发布-订阅模式):定义对象间一对多的依赖关系,当一个对象状态改变时,所有依赖它的对象都自动收到通知。

📋 使用场景
  • Spring 事件机制(ApplicationEvent)
  • GUI 事件处理(按钮点击等)
  • MQ(消息队列)的发布订阅
  • Android/iOS 的通知机制
💻 代码实现
复制代码
// 观察者接口
public interface Observer {
    void update(String eventType, Object data);
}

// 被观察者接口(发布者)
public interface Observable {
    void addObserver(String eventType, Observer observer);
    void removeObserver(String eventType, Observer observer);
    void notifyObservers(String eventType, Object data);
}

// 电商订单系统(被观察者)
public class OrderSystem implements Observable {
    // 事件类型 → 观察者列表
    private Map<String, List<Observer>> observerMap = new HashMap<>();
    
    @Override
    public void addObserver(String eventType, Observer observer) {
        observerMap.computeIfAbsent(eventType, k -> new ArrayList<>()).add(observer);
    }
    
    @Override
    public void removeObserver(String eventType, Observer observer) {
        List<Observer> observers = observerMap.get(eventType);
        if (observers != null) observers.remove(observer);
    }
    
    @Override
    public void notifyObservers(String eventType, Object data) {
        List<Observer> observers = observerMap.getOrDefault(eventType, Collections.emptyList());
        for (Observer observer : observers) {
            observer.update(eventType, data);
        }
    }
    
    // 业务方法:创建订单
    public void createOrder(Order order) {
        System.out.println("📦 订单系统: 订单 " + order.getId() + " 创建成功");
        // 通知所有"订单创建"事件的观察者
        notifyObservers("ORDER_CREATED", order);
    }
    
    // 业务方法:订单支付
    public void payOrder(Order order) {
        System.out.println("💰 订单系统: 订单 " + order.getId() + " 支付成功");
        notifyObservers("ORDER_PAID", order);
    }
    
    // 业务方法:订单发货
    public void shipOrder(Order order) {
        System.out.println("🚚 订单系统: 订单 " + order.getId() + " 已发货");
        notifyObservers("ORDER_SHIPPED", order);
    }
}

// 简单的订单类
public class Order {
    private String id;
    private String productName;
    private double amount;
    private String customerPhone;
    private String customerEmail;
    
    public Order(String id, String productName, double amount, String phone, String email) {
        this.id = id; this.productName = productName; this.amount = amount;
        this.customerPhone = phone; this.customerEmail = email;
    }
    
    public String getId() { return id; }
    public String getProductName() { return productName; }
    public double getAmount() { return amount; }
    public String getCustomerPhone() { return customerPhone; }
    public String getCustomerEmail() { return customerEmail; }
}

// 观察者1:短信通知服务
public class SmsNotificationObserver implements Observer {
    @Override
    public void update(String eventType, Object data) {
        Order order = (Order) data;
        switch (eventType) {
            case "ORDER_CREATED":
                System.out.println("  📱 短信: 发送给 " + order.getCustomerPhone() + 
                                   " - 您的订单" + order.getId() + "已创建");
                break;
            case "ORDER_PAID":
                System.out.println("  📱 短信: 发送给 " + order.getCustomerPhone() + 
                                   " - 您的订单" + order.getId() + "支付¥" + order.getAmount() + "成功");
                break;
            case "ORDER_SHIPPED":
                System.out.println("  📱 短信: 发送给 " + order.getCustomerPhone() + 
                                   " - 您的订单" + order.getId() + "已发货,请注意查收");
                break;
        }
    }
}

// 观察者2:邮件通知服务
public class EmailNotificationObserver implements Observer {
    @Override
    public void update(String eventType, Object data) {
        Order order = (Order) data;
        if ("ORDER_CREATED".equals(eventType)) {
            System.out.println("  📧 邮件: 发送给 " + order.getCustomerEmail() + 
                               " - 订单确认邮件");
        }
    }
}

// 观察者3:库存服务
public class InventoryObserver implements Observer {
    @Override
    public void update(String eventType, Object data) {
        Order order = (Order) data;
        if ("ORDER_PAID".equals(eventType)) {
            System.out.println("  📦 库存: 减少商品[" + order.getProductName() + "]库存1件");
        }
    }
}

// 观察者4:数据统计服务
public class StatisticsObserver implements Observer {
    private double totalRevenue = 0;
    
    @Override
    public void update(String eventType, Object data) {
        Order order = (Order) data;
        if ("ORDER_PAID".equals(eventType)) {
            totalRevenue += order.getAmount();
            System.out.println("  📊 统计: 累计营收 ¥" + totalRevenue);
        }
    }
}

// 测试
public class Main {
    public static void main(String[] args) {
        OrderSystem orderSystem = new OrderSystem();
        
        // 注册观察者
        Observer smsObs = new SmsNotificationObserver();
        Observer emailObs = new EmailNotificationObserver();
        Observer inventoryObs = new InventoryObserver();
        Observer statsObs = new StatisticsObserver();
        
        orderSystem.addObserver("ORDER_CREATED", smsObs);
        orderSystem.addObserver("ORDER_CREATED", emailObs);
        orderSystem.addObserver("ORDER_PAID", smsObs);
        orderSystem.addObserver("ORDER_PAID", inventoryObs);
        orderSystem.addObserver("ORDER_PAID", statsObs);
        orderSystem.addObserver("ORDER_SHIPPED", smsObs);
        
        // 模拟订单流程
        Order order1 = new Order("ORD-001", "iPhone 15", 7999.0, "138xxxx", "user@email.com");
        orderSystem.createOrder(order1);
        System.out.println();
        orderSystem.payOrder(order1);
        System.out.println();
        orderSystem.shipOrder(order1);
    }
}

输出:

复制代码
📦 订单系统: 订单 ORD-001 创建成功
  📱 短信: 发送给 138xxxx - 您的订单ORD-001已创建
  📧 邮件: 发送给 user@email.com - 订单确认邮件

💰 订单系统: 订单 ORD-001 支付成功
  📱 短信: 发送给 138xxxx - 您的订单ORD-001支付¥7999.0成功
  📦 库存: 减少商品[iPhone 15]库存1件
  📊 统计: 累计营收 ¥7999.0

🚚 订单系统: 订单 ORD-001 已发货
  📱 短信: 发送给 138xxxx - 您的订单ORD-001已发货,请注意查收
🔍 Spring 中的观察者模式
复制代码
// Spring 事件(观察者模式的高级实现)

// 定义事件
public class OrderCreatedEvent extends ApplicationEvent {
    private Order order;
    
    public OrderCreatedEvent(Object source, Order order) {
        super(source);
        this.order = order;
    }
    
    public Order getOrder() { return order; }
}

// 发布事件
@Service
public class OrderService {
    @Autowired
    private ApplicationEventPublisher publisher;
    
    public void createOrder(Order order) {
        // ... 业务逻辑
        publisher.publishEvent(new OrderCreatedEvent(this, order)); // 发布事件
    }
}

// 订阅事件(观察者)
@Component
public class SmsListener {
    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        System.out.println("发送短信: " + event.getOrder().getId());
    }
}

@Component
public class EmailListener {
    @EventListener
    public void handleOrderCreated(OrderCreatedEvent event) {
        System.out.println("发送邮件: " + event.getOrder().getId());
    }
}

5.7 状态模式(State Pattern)

🍕 生活例子

自动售货机有几种状态:待机(有货/无货)、已投币、已选择商品、正在出货。

不同状态下,按下同一个按钮会有不同的行为。状态模式让对象在内部状态改变时改变它的行为。

状态模式:允许对象在内部状态改变时改变它的行为,对象看起来好像修改了它的类。

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

// 自动售货机
public class VendingMachine {
    private VendingMachineState currentState;
    private int coins = 0;
    private Map<String, Integer> inventory = new HashMap<>();
    
    // 所有可能的状态
    private final VendingMachineState noCoinState = new NoCoinState();
    private final VendingMachineState hasCoinState = new HasCoinState();
    private final VendingMachineState soldState = new SoldState();
    private final VendingMachineState soldOutState = new SoldOutState();
    
    public VendingMachine() {
        inventory.put("可乐", 3);
        inventory.put("雪碧", 2);
        inventory.put("矿泉水", 5);
        currentState = noCoinState; // 初始状态:无硬币
    }
    
    // 状态转换方法
    public void setState(VendingMachineState state) { this.currentState = state; }
    public VendingMachineState getNoCoinState() { return noCoinState; }
    public VendingMachineState getHasCoinState() { return hasCoinState; }
    public VendingMachineState getSoldState() { return soldState; }
    public VendingMachineState getSoldOutState() { return soldOutState; }
    
    public void addCoins(int amount) { this.coins += amount; }
    public void returnCoins() { 
        System.out.println("退还硬币: " + coins + "元");
        coins = 0; 
    }
    public int getCoins() { return coins; }
    
    public boolean hasProduct(String product) {
        return inventory.getOrDefault(product, 0) > 0;
    }
    
    public void reduceInventory(String product) {
        inventory.merge(product, -1, Integer::sum);
        System.out.println("出货: " + product + ",剩余: " + inventory.get(product) + "瓶");
    }
    
    public boolean isEmptyInventory() {
        return inventory.values().stream().allMatch(v -> v == 0);
    }
    
    // 委托给当前状态处理
    public void insertCoin() { currentState.insertCoin(this); }
    public void ejectCoin() { currentState.ejectCoin(this); }
    public void selectProduct(String product) { currentState.selectProduct(this, product); }
    
    public void displayStatus() {
        System.out.println("--- 售货机状态 ---");
        System.out.println("当前状态: " + currentState.getClass().getSimpleName());
        System.out.println("投入金额: " + coins + "元");
        System.out.println("库存: " + inventory);
        System.out.println();
    }
}

// 状态1:无硬币状态
public class NoCoinState implements VendingMachineState {
    @Override
    public void insertCoin(VendingMachine machine) {
        machine.addCoins(1);
        System.out.println("✅ 投入1元,当前投入: " + machine.getCoins() + "元");
        machine.setState(machine.getHasCoinState());
    }
    
    @Override
    public void ejectCoin(VendingMachine machine) {
        System.out.println("❌ 没有投币,无法退款");
    }
    
    @Override
    public void selectProduct(VendingMachine machine, String product) {
        System.out.println("❌ 请先投币");
    }
    
    @Override
    public void dispense(VendingMachine machine) {
        System.out.println("❌ 请先投币再选商品");
    }
}

// 状态2:有硬币状态
public class HasCoinState implements VendingMachineState {
    @Override
    public void insertCoin(VendingMachine machine) {
        machine.addCoins(1);
        System.out.println("✅ 再次投入1元,当前投入: " + machine.getCoins() + "元");
    }
    
    @Override
    public void ejectCoin(VendingMachine machine) {
        machine.returnCoins();
        machine.setState(machine.getNoCoinState());
    }
    
    @Override
    public void selectProduct(VendingMachine machine, String product) {
        if (!machine.hasProduct(product)) {
            System.out.println("❌ " + product + " 已售罄");
            return;
        }
        if (machine.getCoins() < 2) { // 假设所有商品2元
            System.out.println("❌ 余额不足(需要2元,当前" + machine.getCoins() + "元)");
            return;
        }
        System.out.println("✅ 已选择: " + product);
        machine.setState(machine.getSoldState());
        machine.currentState.dispense(machine); // 触发出货
        // 实际应该在dispense中操作,这里简化
    }
    
    @Override
    public void dispense(VendingMachine machine) {
        System.out.println("❌ 请先选择商品");
    }
}

// 状态3:出货状态
public class SoldState implements VendingMachineState {
    @Override
    public void insertCoin(VendingMachine machine) {
        System.out.println("⏳ 正在出货,请稍候");
    }
    @Override
    public void ejectCoin(VendingMachine machine) {
        System.out.println("❌ 已选择商品,无法退币");
    }
    @Override
    public void selectProduct(VendingMachine machine, String product) {
        System.out.println("⏳ 正在出货,请稍候");
    }
    @Override
    public void dispense(VendingMachine machine) {
        // 实际出货逻辑在HasCoinState.selectProduct中触发
    }
}

// 状态4:售罄状态
public class SoldOutState implements VendingMachineState {
    @Override
    public void insertCoin(VendingMachine machine) {
        System.out.println("❌ 商品已售罄,不接受投币");
        machine.returnCoins();
    }
    @Override
    public void ejectCoin(VendingMachine machine) { machine.returnCoins(); }
    @Override
    public void selectProduct(VendingMachine machine, String product) {
        System.out.println("❌ 商品已售罄");
    }
    @Override
    public void dispense(VendingMachine machine) {
        System.out.println("❌ 商品已售罄");
    }
}

5.8 策略模式(Strategy Pattern)

🍕 生活例子

你去旅行,可以选择不同的出行方式:坐飞机(快但贵)、坐火车(适中)、开车(自由但慢)。

每种出行方式就是一种"策略",你可以根据情况切换。

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

📋 使用场景
  • 购物车的多种促销算法
  • 排序算法的选择
  • 支付方式的选择
  • Comparator 接口就是策略模式
💻 代码实现
复制代码
// 策略接口:优惠计算
public interface DiscountStrategy {
    double calculateDiscount(double originalPrice, int quantity);
    String getDescription();
}

// 策略1:无折扣
public class NoDiscountStrategy implements DiscountStrategy {
    @Override
    public double calculateDiscount(double originalPrice, int quantity) {
        return 0; // 没有折扣
    }
    @Override
    public String getDescription() { return "无折扣"; }
}

// 策略2:百分比折扣(打折)
public class PercentageDiscountStrategy implements DiscountStrategy {
    private double discountRate; // 折扣率,如0.2表示八折(减20%)
    
    public PercentageDiscountStrategy(double discountRate) {
        this.discountRate = discountRate;
    }
    
    @Override
    public double calculateDiscount(double originalPrice, int quantity) {
        return originalPrice * quantity * discountRate;
    }
    
    @Override
    public String getDescription() {
        return (int)((1 - discountRate) * 10) + "折优惠";
    }
}

// 策略3:满减优惠
public class FullReductionStrategy implements DiscountStrategy {
    private double threshold; // 满减门槛
    private double reduction; // 减多少
    
    public FullReductionStrategy(double threshold, double reduction) {
        this.threshold = threshold;
        this.reduction = reduction;
    }
    
    @Override
    public double calculateDiscount(double originalPrice, int quantity) {
        double total = originalPrice * quantity;
        // 可以叠加:满200减30,满400减60
        return Math.floor(total / threshold) * reduction;
    }
    
    @Override
    public String getDescription() {
        return "满" + (int)threshold + "减" + (int)reduction;
    }
}

// 策略4:买X送X(第二件半价等)
public class BuyGetFreeStrategy implements DiscountStrategy {
    private int buyCount;  // 买几件
    private int freeCount; // 送几件
    
    public BuyGetFreeStrategy(int buyCount, int freeCount) {
        this.buyCount = buyCount;
        this.freeCount = freeCount;
    }
    
    @Override
    public double calculateDiscount(double originalPrice, int quantity) {
        // 每 buyCount+freeCount 件中,有 freeCount 件免费
        int groups = quantity / (buyCount + freeCount);
        int remainBuy = Math.min(quantity % (buyCount + freeCount), buyCount);
        int freeItems = groups * freeCount;
        return freeItems * originalPrice;
    }
    
    @Override
    public String getDescription() {
        return "买" + buyCount + "送" + freeCount;
    }
}

// 购物车(使用策略的环境类)
public class ShoppingCart {
    private List<CartItem> items = new ArrayList<>();
    private DiscountStrategy discountStrategy;
    
    public ShoppingCart() {
        this.discountStrategy = new NoDiscountStrategy(); // 默认无折扣
    }
    
    public void setDiscountStrategy(DiscountStrategy strategy) {
        this.discountStrategy = strategy;
        System.out.println("🎁 当前优惠活动: " + strategy.getDescription());
    }
    
    public void addItem(String name, double price, int quantity) {
        items.add(new CartItem(name, price, quantity));
    }
    
    public void checkout() {
        System.out.println("\n=== 购物清单 ===");
        double originalTotal = 0;
        for (CartItem item : items) {
            double subtotal = item.price * item.quantity;
            originalTotal += subtotal;
            System.out.printf("  %-15s ¥%.2f × %d = ¥%.2f%n",
                    item.name, item.price, item.quantity, subtotal);
        }
        System.out.printf("原价合计: ¥%.2f%n", originalTotal);
        
        double discount = discountStrategy.calculateDiscount(
            items.isEmpty() ? 0 : items.get(0).price,
            items.stream().mapToInt(i -> i.quantity).sum()
        );
        
        // 简化:直接对总价应用折扣
        double discountAmount = 0;
        for (CartItem item : items) {
            discountAmount += discountStrategy.calculateDiscount(item.price, item.quantity);
        }
        
        double finalPrice = originalTotal - discountAmount;
        System.out.printf("优惠金额: -¥%.2f (%s)%n", discountAmount, discountStrategy.getDescription());
        System.out.printf("实付金额: ¥%.2f%n", finalPrice);
        System.out.println("===============\n");
    }
    
    static class CartItem {
        String name;
        double price;
        int quantity;
        CartItem(String name, double price, int quantity) {
            this.name = name; this.price = price; this.quantity = quantity;
        }
    }
}

// 测试:灵活切换优惠策略
public class Main {
    public static void main(String[] args) {
        ShoppingCart cart = new ShoppingCart();
        cart.addItem("T恤", 99.0, 3);
        cart.addItem("裤子", 159.0, 2);
        
        // 双十一活动:八折
        cart.setDiscountStrategy(new PercentageDiscountStrategy(0.2)); // 减去20%
        cart.checkout();
        
        // 满减活动:满200减30
        cart.setDiscountStrategy(new FullReductionStrategy(200, 30));
        cart.checkout();
        
        // 买二送一
        cart.setDiscountStrategy(new BuyGetFreeStrategy(2, 1));
        cart.checkout();
    }
}

5.9 模板方法模式(Template Method Pattern)

🍕 生活例子

做菜的步骤:准备食材 → 热锅 → 放油 → 烹饪 → 出锅

这个框架(步骤顺序)是固定的,但每道菜的具体烹饪方式不同。

模板方法模式定义了算法的框架,把具体步骤延迟到子类实现。

模板方法模式:在父类中定义算法的骨架,将某些步骤延迟到子类中实现,使得子类可以在不改变算法结构的情况下重新定义某些步骤。

📋 使用场景
  • JDK AbstractList(实现了大量通用方法,只需子类实现几个抽象方法)
  • Spring JdbcTemplate(定义了JDBC操作的模板)
  • MyBatis BaseExecutor
💻 代码实现
复制代码
// 抽象类:定义炒菜的模板
public abstract class CookingTemplate {
    
    // 模板方法(final,不允许子类修改流程)
    public final void cook() {
        System.out.println("=== 开始烹饪: " + getDishName() + " ===");
        prepareIngredients();
        heatPan();
        addOil();
        addIngredients();
        stirFry();
        addSeasoning();
        serve();
        System.out.println("=== " + getDishName() + " 大功告成!===\n");
    }
    
    // 抽象方法:子类必须实现
    protected abstract String getDishName();
    protected abstract void prepareIngredients();
    protected abstract void addIngredients();
    protected abstract void stirFry();
    
    // 具体方法:所有子类共用
    private void heatPan() {
        System.out.println("  1. 开火,热锅");
    }
    
    private void addOil() {
        System.out.println("  2. 倒入适量食用油");
    }
    
    private void serve() {
        System.out.println("  6. 装盘,上桌!");
    }
    
    // 钩子方法:子类可以选择性重写
    protected void addSeasoning() {
        System.out.println("  5. 加盐、鸡精调味");
    }
}

// 具体子类1:番茄炒蛋
public class TomatoAndEggCooking extends CookingTemplate {
    @Override
    protected String getDishName() { return "番茄炒蛋"; }
    
    @Override
    protected void prepareIngredients() {
        System.out.println("  准备食材: 番茄2个切块,鸡蛋3个打散加盐");
    }
    
    @Override
    protected void addIngredients() {
        System.out.println("  3. 先倒鸡蛋液,炒成块状后盛出备用");
        System.out.println("     再放番茄翻炒出汁");
    }
    
    @Override
    protected void stirFry() {
        System.out.println("  4. 鸡蛋回锅,与番茄一起翻炒均匀");
    }
    
    // 重写钩子方法:番茄炒蛋加糖提鲜
    @Override
    protected void addSeasoning() {
        System.out.println("  5. 加盐、少许糖(提鲜),翻炒均匀");
    }
}

// 具体子类2:青椒肉丝
public class GreenPepperAndMeatCooking extends CookingTemplate {
    @Override
    protected String getDishName() { return "青椒肉丝"; }
    
    @Override
    protected void prepareIngredients() {
        System.out.println("  准备食材: 猪肉200g切丝腌制,青椒3个切丝,大蒜切片");
    }
    
    @Override
    protected void addIngredients() {
        System.out.println("  3. 先下蒜片爆香,再放肉丝翻炒至变色");
        System.out.println("     加入青椒丝翻炒");
    }
    
    @Override
    protected void stirFry() {
        System.out.println("  4. 大火翻炒2分钟,青椒断生即可");
    }
}

// 测试
public class Main {
    public static void main(String[] args) {
        // 做番茄炒蛋
        CookingTemplate dish1 = new TomatoAndEggCooking();
        dish1.cook();
        
        // 做青椒肉丝
        CookingTemplate dish2 = new GreenPepperAndMeatCooking();
        dish2.cook();
    }
}

输出:

复制代码
=== 开始烹饪: 番茄炒蛋 ===
  准备食材: 番茄2个切块,鸡蛋3个打散加盐
  1. 开火,热锅
  2. 倒入适量食用油
  3. 先倒鸡蛋液,炒成块状后盛出备用
     再放番茄翻炒出汁
  4. 鸡蛋回锅,与番茄一起翻炒均匀
  5. 加盐、少许糖(提鲜),翻炒均匀
  6. 装盘,上桌!
=== 番茄炒蛋 大功告成!===

5.10 访问者模式(Visitor Pattern)

🍕 生活例子

医院体检:不同的检查医生(访问者)到你这里(元素)进行检查。眼科医生检查眼睛,心内科医生检查心脏,每个医生只专注自己的检查项目。

访问者模式:在不改变已有类的前提下,为这些类添加新的操作。

💻 代码实现
复制代码
// 访问者接口
public interface Visitor {
    void visit(Book book);
    void visit(Electronics electronics);
    void visit(Clothing clothing);
}

// 元素接口
public interface Product {
    void accept(Visitor visitor);
    String getName();
    double getPrice();
}

// 具体元素:书
public class Book implements Product {
    private String name;
    private double price;
    private String isbn;
    
    public Book(String name, double price, String isbn) {
        this.name = name; this.price = price; this.isbn = isbn;
    }
    
    @Override
    public void accept(Visitor visitor) { visitor.visit(this); }
    @Override
    public String getName() { return name; }
    @Override
    public double getPrice() { return price; }
    public String getIsbn() { return isbn; }
}

// 具体元素:电子产品
public class Electronics implements Product {
    private String name;
    private double price;
    private int warrantyYears;
    
    public Electronics(String name, double price, int warrantyYears) {
        this.name = name; this.price = price; this.warrantyYears = warrantyYears;
    }
    
    @Override
    public void accept(Visitor visitor) { visitor.visit(this); }
    @Override
    public String getName() { return name; }
    @Override
    public double getPrice() { return price; }
    public int getWarrantyYears() { return warrantyYears; }
}

// 具体元素:服装
public class Clothing implements Product {
    private String name;
    private double price;
    private String size;
    
    public Clothing(String name, double price, String size) {
        this.name = name; this.price = price; this.size = size;
    }
    
    @Override
    public void accept(Visitor visitor) { visitor.visit(this); }
    @Override
    public String getName() { return name; }
    @Override
    public double getPrice() { return price; }
    public String getSize() { return size; }
}

// 具体访问者1:折扣计算器
public class DiscountCalculator implements Visitor {
    @Override
    public void visit(Book book) {
        double discount = book.getPrice() * 0.1; // 书打九折
        System.out.printf("📚 书籍[%s]: 原价¥%.2f, 享9折, 优惠¥%.2f%n",
                book.getName(), book.getPrice(), discount);
    }
    
    @Override
    public void visit(Electronics electronics) {
        double discount = electronics.getPrice() * 0.05; // 电子产品九五折
        System.out.printf("💻 电子[%s]: 原价¥%.2f, 享95折, 优惠¥%.2f%n",
                electronics.getName(), electronics.getPrice(), discount);
    }
    
    @Override
    public void visit(Clothing clothing) {
        double discount = clothing.getPrice() * 0.2; // 服装打八折
        System.out.printf("👕 服装[%s]: 原价¥%.2f, 享8折, 优惠¥%.2f%n",
                clothing.getName(), clothing.getPrice(), discount);
    }
}

// 具体访问者2:税费计算器
public class TaxCalculator implements Visitor {
    @Override
    public void visit(Book book) {
        // 书籍免税
        System.out.printf("📚 书籍[%s]: 免税商品, 税费¥0.00%n", book.getName());
    }
    
    @Override
    public void visit(Electronics electronics) {
        double tax = electronics.getPrice() * 0.13; // 13%增值税
        System.out.printf("💻 电子[%s]: 增值税13%%, 税费¥%.2f%n", electronics.getName(), tax);
    }
    
    @Override
    public void visit(Clothing clothing) {
        double tax = clothing.getPrice() * 0.09; // 9%增值税
        System.out.printf("👕 服装[%s]: 增值税9%%, 税费¥%.2f%n", clothing.getName(), tax);
    }
}

// 测试:同一批商品,用不同访问者计算不同信息
public class Main {
    public static void main(String[] args) {
        List<Product> products = Arrays.asList(
            new Book("Java设计模式", 89.0, "ISBN-001"),
            new Electronics("iPhone 15", 7999.0, 2),
            new Clothing("运动T恤", 199.0, "XL"),
            new Book("深入理解JVM", 119.0, "ISBN-002")
        );
        
        System.out.println("=== 双十一优惠计算 ===");
        Visitor discountVisitor = new DiscountCalculator();
        for (Product p : products) {
            p.accept(discountVisitor);
        }
        
        System.out.println("\n=== 税费计算 ===");
        Visitor taxVisitor = new TaxCalculator();
        for (Product p : products) {
            p.accept(taxVisitor);
        }
    }
}

5.11 解释器模式(Interpreter Pattern)

🍕 生活例子

你教计算器理解 "1 + 2 × 3 - 4" 这个表达式,计算器需要"解释"每个符号的含义并计算出结果。

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

注意:解释器模式在实际项目中用的不多,了解即可。

💻 代码实现
复制代码
// 抽象表达式
public interface Expression {
    int interpret();
}

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

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

// 非终结符表达式:减法
public class SubtractExpression implements Expression {
    private Expression left, right;
    
    public SubtractExpression(Expression left, Expression right) {
        this.left = left; this.right = right;
    }
    
    @Override
    public int interpret() {
        return left.interpret() - right.interpret();
    }
    
    @Override
    public String toString() { return "(" + left + " - " + right + ")"; }
}

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

// 测试:手动构建表达式树(实际项目中会用解析器自动构建)
public class Main {
    public static void main(String[] args) {
        // 计算: 1 + 2 × 3 - 4
        // 按优先级构建: 1 + (2 × 3) - 4 = (1 + (2×3)) - 4
        
        Expression one = new NumberExpression(1);
        Expression two = new NumberExpression(2);
        Expression three = new NumberExpression(3);
        Expression four = new NumberExpression(4);
        
        Expression twoTimesThree = new MultiplyExpression(two, three); // 2×3
        Expression onePlusTwoTimesThree = new AddExpression(one, twoTimesThree); // 1+(2×3)
        Expression result = new SubtractExpression(onePlusTwoTimesThree, four); // (1+(2×3))-4
        
        System.out.println("表达式: " + result);
        System.out.println("结果: " + result.interpret());
        // 输出: 表达式: ((1 + (2 × 3)) - 4)
        //       结果: 3
    }
}

6. 设计模式在主流框架中的应用

学完23种设计模式,我们来看看它们在实际框架中是怎么用的。这部分能让你豁然开朗,"哦,原来是这个模式!"

6.1 Spring 框架中的设计模式

工厂模式:BeanFactory / ApplicationContext
复制代码
// Spring 最核心的思想就是工厂模式
// ApplicationContext 就是一个大型工厂,生产和管理所有 Bean

ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");

// getBean() 就是工厂方法
UserService userService = (UserService) context.getBean("userService");
OrderService orderService = context.getBean(OrderService.class);

// Spring 通过反射创建对象,就像工厂方法一样
// @Component, @Service, @Repository 注解的类会被Spring扫描并注册
单例模式:Spring Bean 默认作用域
复制代码
// Spring Bean 默认是单例的
@Service
public class UserService {
    // 这个类在整个应用中只有一个实例
}

// 也可以配置为原型(每次获取都是新实例)
@Service
@Scope("prototype")
public class PrototypeService {
    // 每次从容器获取都是新对象
}
代理模式:Spring AOP
复制代码
// Spring AOP 底层就是动态代理
@Aspect
@Component
public class LoggingAspect {
    
    // 环绕通知:在方法执行前后插入逻辑
    @Around("execution(* com.example.service.*.*(..))")
    public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("[AOP] 开始执行: " + methodName);
        long start = System.currentTimeMillis();
        
        Object result = joinPoint.proceed(); // 调用真实方法
        
        long end = System.currentTimeMillis();
        System.out.println("[AOP] 执行完毕: " + methodName + ", 耗时: " + (end-start) + "ms");
        return result;
    }
}

// 被AOP增强的Service(不需要写任何日志代码)
@Service
public class OrderService {
    public Order createOrder(String productId) {
        // 只写业务逻辑,日志由AOP自动添加
        return new Order(productId);
    }
}
观察者模式:Spring 事件机制
复制代码
// 已在5.6节展示,Spring的ApplicationEvent就是观察者模式

// 使用 @EventListener 注解,非常优雅
@Component
public class OrderEventListeners {
    
    @EventListener
    public void onOrderCreated(OrderCreatedEvent event) {
        // 处理订单创建事件
    }
    
    @EventListener
    @Async // 异步处理
    public void onOrderPaid(OrderPaidEvent event) {
        // 异步处理支付事件
    }
}
模板方法:JdbcTemplate
复制代码
// Spring JdbcTemplate 是模板方法模式的经典实现
// 模板(JdbcTemplate)定义了JDBC操作的骨架:获取连接→创建Statement→执行→处理结果→关闭
// 你只需要提供具体的SQL和结果映射

@Repository
public class UserDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    public User findById(int id) {
        // 你只需要写SQL和结果映射,JdbcTemplate处理其他一切
        return jdbcTemplate.queryForObject(
            "SELECT * FROM users WHERE id = ?",
            (rs, rowNum) -> new User(rs.getInt("id"), rs.getString("name")), // 结果映射
            id
        );
    }
    
    public List<User> findAll() {
        return jdbcTemplate.query(
            "SELECT * FROM users",
            (rs, rowNum) -> new User(rs.getInt("id"), rs.getString("name"))
        );
    }
    
    public int save(User user) {
        return jdbcTemplate.update(
            "INSERT INTO users(name, email) VALUES(?, ?)",
            user.getName(), user.getEmail()
        );
    }
}
装饰器模式:HttpServletRequestWrapper
复制代码
// Spring Web 中,HttpServletRequestWrapper 是装饰器模式
// 可以在不修改原始请求的情况下,增强请求功能

public class BodyCachingRequestWrapper extends HttpServletRequestWrapper {
    private byte[] body;
    
    public BodyCachingRequestWrapper(HttpServletRequest request) throws IOException {
        super(request);
        // 把请求体缓存起来(因为流只能读一次)
        body = IOUtils.toByteArray(request.getInputStream());
    }
    
    @Override
    public ServletInputStream getInputStream() {
        return new CachedBodyServletInputStream(body);
    }
    
    // 可以多次读取请求体
    public String getBody() {
        return new String(body, StandardCharsets.UTF_8);
    }
}

// 过滤器中使用
@Component
public class RequestLoggingFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) 
            throws IOException, ServletException {
        BodyCachingRequestWrapper wrappedRequest = 
            new BodyCachingRequestWrapper((HttpServletRequest) request);
        
        System.out.println("请求体: " + wrappedRequest.getBody()); // 记录日志
        
        chain.doFilter(wrappedRequest, response); // 传递包装后的请求
    }
}

6.2 MyBatis 中的设计模式

建造者模式:SqlSessionFactoryBuilder
复制代码
// MyBatis 使用建造者模式创建 SqlSessionFactory
SqlSessionFactory factory = new SqlSessionFactoryBuilder()
    .build(Resources.getResourceAsStream("mybatis-config.xml"));

// SqlSession 是工厂方法模式
SqlSession session = factory.openSession();

// Mapper 接口的实现是代理模式(JDK动态代理)
UserMapper userMapper = session.getMapper(UserMapper.class);
// userMapper 是一个代理对象!调用它的方法时,会自动执行对应的SQL
User user = userMapper.findById(1);
代理模式:Mapper 接口
复制代码
// MyBatis 的核心黑魔法:你只写接口,MyBatis 帮你生成实现类(代理)

// 你只写这个接口
@Mapper
public interface UserMapper {
    @Select("SELECT * FROM users WHERE id = #{id}")
    User findById(@Param("id") int id);
    
    @Insert("INSERT INTO users(name, email) VALUES(#{name}, #{email})")
    int insert(User user);
}

// MyBatis 通过动态代理,自动生成实现类
// 当你调用 userMapper.findById(1) 时
// 实际是代理对象在执行 SQL 并把结果映射成 User 对象
模板方法:BaseExecutor
复制代码
// MyBatis 的 Executor(SQL执行器)使用模板方法模式
// BaseExecutor 定义了查询的骨架
// SimpleExecutor, ReuseExecutor, BatchExecutor 各自实现具体步骤

// 伪代码示意
abstract class BaseExecutor {
    public <E> List<E> query(MappedStatement ms, Object parameter, ...) {
        // 模板方法:定义查询流程
        if (queryCache != null) {
            return queryFromCache(...); // 先查缓存
        }
        return queryFromDatabase(...); // 缓存没有,查数据库
    }
    
    // 抽象方法:留给子类实现
    protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, ...);
}

class SimpleExecutor extends BaseExecutor {
    @Override
    protected <E> List<E> doQuery(...) {
        // 简单执行:每次都创建新的 Statement
    }
}

class ReuseExecutor extends BaseExecutor {
    @Override
    protected <E> List<E> doQuery(...) {
        // 复用 Statement:相同SQL复用PreparedStatement
    }
}

6.3 JDK 中的设计模式总结

复制代码
// ===== 工厂方法 =====
Calendar.getInstance()           // 工厂方法,根据Locale返回不同Calendar子类
NumberFormat.getInstance()
DriverManager.getConnection()
ThreadFactory.newThread()

// ===== 单例 =====
Runtime.getRuntime()             // Runtime 是单例
System.getSecurityManager()
Desktop.getDesktop()

// ===== 建造者 =====
StringBuilder.append().append().toString()
ProcessBuilder
HttpClient.newBuilder().build()  // Java 11+

// ===== 原型 =====
Object.clone()
Arrays.copyOf()

// ===== 适配器 =====
Arrays.asList()                  // 数组 → List
Collections.enumeration()        // Collection → Enumeration
InputStreamReader                // 字节流 → 字符流

// ===== 装饰器 =====
BufferedInputStream/OutputStream
DataInputStream/OutputStream
GZIPInputStream/OutputStream

// ===== 代理 =====
java.lang.reflect.Proxy          // JDK动态代理

// ===== 迭代器 =====
Iterator/Iterable                // 所有集合类

// ===== 观察者 =====
java.util.Observable(已废弃)
EventListener(Swing事件)
PropertyChangeListener

// ===== 策略 =====
Comparator/Comparable            // 排序策略
ThreadPoolExecutor的拒绝策略

// ===== 模板方法 =====
AbstractList, AbstractSet         // 大量抽象集合类
InputStream.read()               // 定义了读取框架
HttpServlet.service()            // 定义了请求处理框架

// ===== 命令 =====
Runnable                         // 命令封装
Callable
Executor.execute(Runnable)

// ===== 责任链 =====
ClassLoader的双亲委派             // 经典责任链
javax.servlet.Filter             // Servlet过滤器链

// ===== 享元 =====
Integer.valueOf(-128~127)        // 整数缓存
String常量池                     // 字符串享元

7. 总结与学习建议

7.1 23种设计模式速查表

类别 模式 核心思想 一句话记忆
创建型 单例 全局唯一实例 全国只有一个国务院
工厂方法 子类决定创建什么 麦当劳后厨做你点的东西
抽象工厂 创建一族相关产品 宜家卖配套的整套家具
建造者 分步构建复杂对象 自定义汉堡,一样一样加料
原型 克隆现有对象 复印机复印文件
结构型 适配器 转换不兼容的接口 电源适配器
桥接 分离抽象和实现 形状和颜色独立变化
组合 树形结构统一处理 文件系统(文件和文件夹)
装饰器 动态添加功能 给咖啡加牛奶加糖
外观 提供简单接口 家庭影院一键开始
享元 共享细粒度对象 游戏中大量相同的树
代理 控制对象访问 明星的经纪人
行为型 责任链 请求沿链传递 公司逐级审批
命令 请求封装成对象 遥控器按钮
迭代器 顺序访问集合 书包一件一件取东西
中介者 减少对象间直接通信 机场塔台调度
备忘录 保存和恢复状态 游戏存档
观察者 一对多通知 微信公众号推送
状态 对象随状态改变行为 自动售货机状态
策略 可切换的算法族 出行方式选择
模板方法 固定骨架,延迟步骤 炒菜的固定步骤
访问者 不改类就加操作 医院体检各科室医生
解释器 解释语法规则 计算器计算表达式

7.2 哪些模式最常用?

日常开发中高频使用(必须掌握):

  1. 单例模式 - Spring Bean 默认就是单例
  2. 工厂模式 - Spring IoC 容器的核心
  3. 代理模式 - Spring AOP、MyBatis Mapper
  4. 观察者模式 - Spring 事件、消息队列
  5. 策略模式 - 替代大量 if-else
  6. 模板方法 - JdbcTemplate、抽象基类
  7. 装饰器模式 - Java I/O、Filter
  8. 建造者模式 - 复杂对象构建、Lombok @Builder
  9. 责任链模式 - 过滤器链、审批流程

了解概念即可(中级):

  1. 适配器 - 整合老系统时用
  2. 外观 - API网关设计
  3. 组合 - 树形结构数据

面试会问,了解就行(高级/少用):

13-23. 剩余模式

7.3 如何避免过度设计?

设计模式不是越多越好!

初学者常见错误:写个Hello World也要用几个设计模式。

判断是否该用设计模式的原则:

复制代码
❓ 问自己:
1. 这里有没有"变化点"?(会不会扩展、会不会修改)
2. 现在代码有没有问题?(重复代码、难以维护)
3. 用了模式,代码是否真的更清晰?

如果三个都是"没有/否",那就不用模式,保持简单!

记住:最好的代码不是用了最多模式的代码,
而是最清晰、最容易理解的代码。

7.4 学习路线建议

复制代码
第一阶段(1-2周):掌握基础
├── 理解六大原则(先理解,慢慢内化)
├── 掌握单例、工厂、建造者(日常用得最多)
└── 了解代理模式(理解Spring AOP)

第二阶段(2-4周):深入理解
├── 策略、观察者、装饰器(项目中高频)
├── 责任链、模板方法(框架中大量使用)
└── 读Spring源码,找对应的设计模式

第三阶段(1-2月):灵活运用
├── 在项目中刻意识别和使用设计模式
├── 代码Review时考虑设计模式
└── 读《设计模式》、《Head First设计模式》原著

第四阶段(持续):融会贯通
├── 不刻意使用,自然而然就用了正确的模式
└── 能识别和评价他人代码中的设计决策

7.5 推荐资源

书籍:

  • 《Head First 设计模式》(入门,有趣易懂)
  • 《设计模式:可复用面向对象软件的基础》(GoF经典,深入)
  • 《Effective Java》(Java最佳实践,包含大量设计模式思想)
  • 《重构:改善既有代码的设计》(配合设计模式一起看)

网站:

练习方法:

  1. 读完一个模式,自己不看代码重新写一遍
  2. 在 GitHub 上找开源项目,找对应模式的使用
  3. 看 Spring、MyBatis 源码,标注出使用了哪些模式

7.6 一段代码结束全文

最后,用一个综合案例,把常用的几个模式串起来,让你感受设计模式组合使用的魅力:

复制代码
/**
 * 综合案例:一个简易的电商下单流程
 * 用到了:策略模式、观察者模式、责任链模式、单例模式、建造者模式
 */
public class ECommerceDemo {
    
    public static void main(String[] args) {
        System.out.println("╔══════════════════════════════════╗");
        System.out.println("║    欢迎使用简易电商系统           ║");
        System.out.println("╚══════════════════════════════════╝\n");
        
        // 1. 单例模式:获取配置
        ConfigManager config = ConfigManager.getInstance();
        
        // 2. 建造者模式:构建订单
        Order order = new Order.Builder()
            .orderId("ORD-2024-001")
            .productName("MacBook Pro")
            .price(14999.0)
            .quantity(1)
            .customerId("USER-001")
            .customerName("张三")
            .phone("138xxxxxxxx")
            .build();
        
        // 3. 策略模式:选择支付方式
        PaymentStrategy paymentStrategy = new AlipayPaymentStrategy();
        
        // 4. 责任链模式:订单审核(风控)
        OrderValidator validator = buildValidationChain();
        
        // 5. 观察者模式:注册订单事件监听器
        OrderEventBus eventBus = new OrderEventBus();
        eventBus.subscribe("ORDER_PLACED", new SmsNotifier());
        eventBus.subscribe("ORDER_PLACED", new EmailNotifier());
        eventBus.subscribe("ORDER_PAID", new InventoryUpdater());
        eventBus.subscribe("ORDER_PAID", new StatisticsUpdater());
        
        // 执行下单流程
        System.out.println("【步骤1】提交订单: " + order.getOrderId());
        
        if (validator.validate(order)) {
            System.out.println("【步骤2】订单验证通过");
            eventBus.publish("ORDER_PLACED", order);
            
            System.out.println("\n【步骤3】执行支付: " + paymentStrategy.getDescription());
            boolean paySuccess = paymentStrategy.pay(order.getTotalPrice());
            
            if (paySuccess) {
                System.out.println("【步骤4】支付成功 ¥" + order.getTotalPrice());
                eventBus.publish("ORDER_PAID", order);
                System.out.println("\n✅ 下单完成!请耐心等待收货。");
            }
        }
    }
    
    private static OrderValidator buildValidationChain() {
        // 建立验证责任链
        OrderValidator stockCheck = new StockCheckValidator();
        OrderValidator riskCheck = new RiskControlValidator();
        OrderValidator limitCheck = new PurchaseLimitValidator();
        
        stockCheck.setNext(riskCheck).setNext(limitCheck);
        return stockCheck;
    }
}

写在最后

恭喜你读完了这篇文章!🎉

设计模式是通往高级程序员的必经之路,但不要急于求成。

记住一个学习法则:"先理解,后应用,再灵活"

  • 理解:先搞明白每个模式解决什么问题,不要死记代码
  • 应用:在实际项目中尝试使用,哪怕用得不完美也没关系
  • 灵活:多了解变化,有时候"正宗"的模式并不是最好的,根据实际情况取舍

祝你在 Java 开发的道路上越走越远!💪


本文约 5 万字,涵盖了 23 种 GoF 设计模式的完整讲解,配有大量代码示例和生活类比。如有疑问,欢迎在评论区交流!


附录A:设计模式综合实战案例

A.1 综合案例:设计一个简易 RPC 框架

这个案例综合运用了多个设计模式,是很好的练习题:

复制代码
/**
 * 简易 RPC 框架
 * 使用的设计模式:
 * - 代理模式:生成远程调用代理
 * - 工厂模式:创建代理对象
 * - 单例模式:注册中心
 * - 策略模式:负载均衡算法
 * - 观察者模式:服务上下线通知
 */

// 1. 服务注册中心(单例模式)
public class ServiceRegistry {
    private static volatile ServiceRegistry instance;
    private Map<String, List<String>> serviceMap = new ConcurrentHashMap<>();
    private List<RegistryObserver> observers = new CopyOnWriteArrayList<>();
    
    private ServiceRegistry() {}
    
    public static ServiceRegistry getInstance() {
        if (instance == null) {
            synchronized (ServiceRegistry.class) {
                if (instance == null) instance = new ServiceRegistry();
            }
        }
        return instance;
    }
    
    // 服务注册
    public void register(String serviceName, String address) {
        serviceMap.computeIfAbsent(serviceName, k -> new CopyOnWriteArrayList<>()).add(address);
        System.out.println("📋 注册中心: 服务[" + serviceName + "]注册 " + address);
        notifyObservers("REGISTER", serviceName, address);
    }
    
    // 服务发现
    public List<String> discover(String serviceName) {
        return serviceMap.getOrDefault(serviceName, Collections.emptyList());
    }
    
    // 观察者模式:监听服务变化
    public void addObserver(RegistryObserver observer) { observers.add(observer); }
    
    private void notifyObservers(String event, String serviceName, String address) {
        observers.forEach(o -> o.onServiceChange(event, serviceName, address));
    }
}

public interface RegistryObserver {
    void onServiceChange(String event, String serviceName, String address);
}

// 2. 负载均衡策略(策略模式)
public interface LoadBalancer {
    String select(List<String> addresses);
}

public class RoundRobinLoadBalancer implements LoadBalancer {
    private AtomicInteger index = new AtomicInteger(0);
    
    @Override
    public String select(List<String> addresses) {
        if (addresses.isEmpty()) throw new RuntimeException("没有可用服务");
        int i = index.getAndIncrement() % addresses.size();
        return addresses.get(i);
    }
}

public class RandomLoadBalancer implements LoadBalancer {
    private Random random = new Random();
    
    @Override
    public String select(List<String> addresses) {
        if (addresses.isEmpty()) throw new RuntimeException("没有可用服务");
        return addresses.get(random.nextInt(addresses.size()));
    }
}

// 3. RPC 代理工厂(工厂模式 + 代理模式)
public class RpcProxyFactory {
    private ServiceRegistry registry;
    private LoadBalancer loadBalancer;
    
    public RpcProxyFactory(ServiceRegistry registry, LoadBalancer loadBalancer) {
        this.registry = registry;
        this.loadBalancer = loadBalancer;
    }
    
    @SuppressWarnings("unchecked")
    public <T> T createProxy(Class<T> serviceInterface) {
        return (T) Proxy.newProxyInstance(
            serviceInterface.getClassLoader(),
            new Class[]{serviceInterface},
            (proxy, method, args) -> {
                String serviceName = serviceInterface.getName();
                List<String> addresses = registry.discover(serviceName);
                String address = loadBalancer.select(addresses);
                
                System.out.printf("🌐 RPC调用: %s.%s() → %s%n",
                    serviceInterface.getSimpleName(), method.getName(), address);
                
                // 模拟网络调用(实际应序列化参数,通过网络发送)
                return simulateRemoteCall(address, method, args);
            }
        );
    }
    
    private Object simulateRemoteCall(String address, Method method, Object[] args) {
        // 实际RPC框架这里会做序列化、网络传输、反序列化
        System.out.println("   └─ 模拟发送请求到 " + address);
        // 简单模拟返回
        if (method.getReturnType() == String.class) return "来自" + address + "的响应";
        if (method.getReturnType() == int.class) return 42;
        return null;
    }
}

// 4. 使用示例
public interface UserService {
    String getUser(int id);
    int getUserCount();
}

public class RpcDemo {
    public static void main(String[] args) {
        // 初始化
        ServiceRegistry registry = ServiceRegistry.getInstance();
        
        // 注册服务实例(模拟多个服务节点)
        registry.register(UserService.class.getName(), "192.168.1.1:8080");
        registry.register(UserService.class.getName(), "192.168.1.2:8080");
        registry.register(UserService.class.getName(), "192.168.1.3:8080");
        
        // 创建代理(使用轮询负载均衡)
        RpcProxyFactory factory = new RpcProxyFactory(registry, new RoundRobinLoadBalancer());
        UserService userService = factory.createProxy(UserService.class);
        
        // 透明地调用远程服务(就像调用本地方法一样)
        System.out.println("\n--- 开始RPC调用 ---");
        for (int i = 0; i < 4; i++) {
            String result = userService.getUser(i);
            System.out.println("结果: " + result);
        }
    }
}

A.2 综合案例:设计一个插件化系统

复制代码
/**
 * 插件化系统
 * 使用:工厂模式 + 策略模式 + 责任链模式 + 观察者模式
 */

// 插件接口
public interface Plugin {
    String getName();
    String getVersion();
    void onLoad();     // 插件加载时
    void onUnload();   // 插件卸载时
    void execute(Context context); // 插件执行
}

// 执行上下文(建造者模式)
public class Context {
    private final Map<String, Object> data;
    
    private Context(Builder builder) {
        this.data = Collections.unmodifiableMap(builder.data);
    }
    
    public <T> T get(String key, Class<T> type) {
        return type.cast(data.get(key));
    }
    
    public static class Builder {
        private Map<String, Object> data = new HashMap<>();
        
        public Builder put(String key, Object value) {
            data.put(key, value);
            return this;
        }
        
        public Context build() { return new Context(this); }
    }
}

// 插件管理器(单例 + 工厂 + 观察者)
public class PluginManager {
    private static PluginManager instance = new PluginManager();
    private Map<String, Plugin> plugins = new LinkedHashMap<>();
    private List<PluginListener> listeners = new ArrayList<>();
    
    private PluginManager() {}
    
    public static PluginManager getInstance() { return instance; }
    
    public void register(Plugin plugin) {
        plugins.put(plugin.getName(), plugin);
        plugin.onLoad();
        notifyListeners("LOADED", plugin);
        System.out.println("✅ 插件[" + plugin.getName() + " v" + plugin.getVersion() + "]已加载");
    }
    
    public void unregister(String pluginName) {
        Plugin plugin = plugins.remove(pluginName);
        if (plugin != null) {
            plugin.onUnload();
            notifyListeners("UNLOADED", plugin);
            System.out.println("🗑️  插件[" + pluginName + "]已卸载");
        }
    }
    
    // 执行所有插件(责任链模式)
    public void executeAll(Context context) {
        System.out.println("--- 执行所有插件 ---");
        plugins.values().forEach(p -> {
            System.out.println("▶️  执行插件: " + p.getName());
            p.execute(context);
        });
    }
    
    public void addListener(PluginListener listener) { listeners.add(listener); }
    
    private void notifyListeners(String event, Plugin plugin) {
        listeners.forEach(l -> l.onPluginEvent(event, plugin));
    }
}

public interface PluginListener {
    void onPluginEvent(String event, Plugin plugin);
}

// 具体插件示例:日志插件
public class LoggingPlugin implements Plugin {
    @Override
    public String getName() { return "logging-plugin"; }
    @Override
    public String getVersion() { return "1.0.0"; }
    @Override
    public void onLoad() { System.out.println("   日志插件初始化"); }
    @Override
    public void onUnload() { System.out.println("   日志插件清理"); }
    
    @Override
    public void execute(Context context) {
        System.out.println("   [LOG] 记录执行日志: " + context.get("action", String.class));
    }
}

// 具体插件示例:安全插件
public class SecurityPlugin implements Plugin {
    @Override
    public String getName() { return "security-plugin"; }
    @Override
    public String getVersion() { return "2.1.0"; }
    @Override
    public void onLoad() { System.out.println("   安全插件初始化,加载安全规则"); }
    @Override
    public void onUnload() { System.out.println("   安全插件清理"); }
    
    @Override
    public void execute(Context context) {
        String user = context.get("user", String.class);
        System.out.println("   [SECURITY] 检查用户权限: " + user + " → 通过");
    }
}

// 测试
public class PluginSystemDemo {
    public static void main(String[] args) {
        PluginManager pm = PluginManager.getInstance();
        
        // 添加监听器
        pm.addListener((event, plugin) -> 
            System.out.println("   🔔 监听到插件事件: " + event + " - " + plugin.getName()));
        
        // 注册插件
        pm.register(new SecurityPlugin());
        pm.register(new LoggingPlugin());
        
        System.out.println();
        
        // 执行所有插件
        Context ctx = new Context.Builder()
            .put("user", "admin")
            .put("action", "DELETE_USER")
            .build();
        pm.executeAll(ctx);
        
        System.out.println();
        
        // 卸载插件
        pm.unregister("logging-plugin");
    }
}

附录B:常见面试题整理

B.1 关于单例模式

Q: 单例模式有几种写法?各有什么优缺点?

复制代码
A: 主要有5种:
1. 饿汉式:类加载时创建,线程安全,但无论用不用都占内存
2. 懒汉式(不加锁):懒加载,但线程不安全
3. 懒汉式(同步方法):线程安全,但并发性能差
4. 双重检查锁(DCL):懒加载 + 线程安全 + 性能好,需要volatile
5. 静态内部类:最优雅,利用JVM类加载保证线程安全
6. 枚举:最安全,防止反射和序列化攻击

推荐使用:静态内部类(普通情况)或枚举(需要防攻击时)

Q: 如何破坏单例?如何防止?

复制代码
// 破坏方式1:反射
Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
constructor.setAccessible(true);
Singleton s1 = constructor.newInstance(); // 通过反射创建新实例

// 防止反射攻击:在构造器中检查
private Singleton() {
    if (instance != null) {
        throw new RuntimeException("禁止反射调用!");
    }
}

// 破坏方式2:序列化
// 防止序列化破坏:实现 readResolve
private Object readResolve() {
    return instance; // 返回已有实例
}

// 最彻底的防止方法:使用枚举(JVM层面保证不能反射和序列化)

B.2 关于工厂模式

Q: 简单工厂、工厂方法、抽象工厂的区别?

复制代码
简单工厂:
- 一个工厂类
- if-else 或 switch 判断创建哪个
- 新增产品要改工厂类(违反开闭原则)
- 适合:产品少且稳定

工厂方法:
- 每个产品有自己的工厂
- 新增产品只需新增产品类+工厂类
- 遵循开闭原则
- 适合:产品会扩展

抽象工厂:
- 工厂能创建一整族产品
- 保证同族产品的一致性
- 新增产品族容易,新增单个产品类型难
- 适合:多个产品族,产品需要配套使用

B.3 关于代理模式

Q: JDK动态代理和CGLIB代理的区别?Spring如何选择?

复制代码
/*
JDK动态代理:
- 基于接口(必须有接口)
- 使用 java.lang.reflect.Proxy
- 原理:生成实现相同接口的代理类
- 性能:调用时通过反射,稍慢

CGLIB代理:
- 基于继承(不需要接口,但类不能是final)
- 使用字节码生成技术(ASM)
- 原理:生成目标类的子类
- 性能:调用时不用反射,较快

Spring的选择逻辑(Spring 5.x开始默认用CGLIB):
- 实现了接口:可用JDK或CGLIB(Spring默认CGLIB)
- 没有接口:只能用CGLIB
- proxyTargetClass=true:强制用CGLIB
- @EnableAspectJAutoProxy(proxyTargetClass=false):优先用JDK

注意:Spring Boot 2.x 起默认使用 CGLIB!
*/

B.4 关于观察者模式

Q: 观察者模式和发布订阅模式的区别?

复制代码
观察者模式:
- 观察者直接注册到被观察者
- 两者之间有直接关联
- 同步执行(被观察者状态变化后,立即通知观察者)

发布订阅模式:
- 发布者和订阅者通过消息中间件(Event Bus/消息队列)解耦
- 两者互不知晓
- 可以异步执行

关系:发布订阅是观察者模式的进化版,更松耦合

Spring的ApplicationEvent是观察者模式(还没到消息队列那种程度)
Kafka、RabbitMQ是发布订阅模式(完全解耦)

B.5 关于策略模式

Q: 策略模式如何消除 if-else?

复制代码
// ❌ 一堆 if-else(每次新增促销都要改这里)
public double calculatePrice(String type, double price) {
    if ("NONE".equals(type)) {
        return price;
    } else if ("PERCENT_10".equals(type)) {
        return price * 0.9;
    } else if ("FULL_100_MINUS_10".equals(type)) {
        return price >= 100 ? price - 10 : price;
    }
    // 新增促销类型 → 还要继续加 else if
    throw new IllegalArgumentException("未知促销类型");
}

// ✅ 策略模式消除 if-else
public interface PricingStrategy {
    double calculate(double price);
}

// 策略注册表
Map<String, PricingStrategy> strategies = new HashMap<>();
strategies.put("NONE", price -> price);
strategies.put("PERCENT_10", price -> price * 0.9);
strategies.put("FULL_100_MINUS_10", price -> price >= 100 ? price - 10 : price);

// 新增促销:只需往map里加一条,原代码不用改!
strategies.put("BUY_2_GET_1_FREE", price -> price * 2 / 3);

// 使用
public double calculatePrice(String type, double price) {
    PricingStrategy strategy = strategies.get(type);
    if (strategy == null) throw new IllegalArgumentException("未知促销类型: " + type);
    return strategy.calculate(price);
}

附录C:设计模式之间的关系图

复制代码
                    ┌─────────────────────────────────────────────┐
                    │              创建对象                        │
                    └──────────────┬──────────────────────────────┘
                                   │
           ┌───────────────────────┼───────────────────────┐
           ▼                       ▼                       ▼
       [单例模式]              [工厂模式]               [建造者]
     保证唯一实例          封装创建过程              分步构建对象
                              /      \
                     [工厂方法]    [抽象工厂]
                      单个产品      产品族

┌────────────────────────────────────────────────────────────────┐
│                     常见组合使用                                │
├────────────────────────────────────────────────────────────────┤
│ 工厂 + 单例:工厂创建的对象是单例(Spring Bean工厂)           │
│ 代理 + 策略:代理拦截后选择不同策略执行(AOP + 策略)          │
│ 观察者 + 命令:事件系统(事件=命令,订阅者=处理器)            │
│ 装饰器 + 责任链:Servlet过滤器链(每个Filter是装饰器+链节点) │
│ 模板方法 + 策略:框架提供模板,具体步骤用策略替换             │
│ 工厂 + 原型:工厂通过克隆原型来创建对象(原型工厂)            │
└────────────────────────────────────────────────────────────────┘

┌────────────────────────────────────────────────────────────────┐
│                  容易混淆的模式对比                             │
├────────────────────────────────────────────────────────────────┤
│ 代理 vs 装饰器:                                               │
│   代理:控制访问,代理类自己决定是否/如何调用目标              │
│   装饰器:增强功能,客户端决定叠加哪些装饰                    │
│                                                                │
│ 策略 vs 状态:                                                 │
│   策略:外部传入,客户端决定用哪个算法                         │
│   状态:内部切换,对象自己根据条件切换状态                    │
│                                                                │
│ 外观 vs 中介者:                                               │
│   外观:简化子系统的接口,单向(外部→子系统)                  │
│   中介者:协调对象间通信,双向(对象↔中介者↔对象)            │
│                                                                │
│ 装饰器 vs 桥接:                                               │
│   装饰器:动态添加功能,强调"功能叠加"                         │
│   桥接:分离两个独立变化的维度,强调"维度正交"                │
└────────────────────────────────────────────────────────────────┘

附录D:用 Java 8+ 特性简化设计模式

Java 8 引入了 Lambda 表达式和函数式接口,可以让某些设计模式的代码更简洁:

复制代码
// ===== 策略模式 + Lambda =====
// 传统方式
new Thread(new Runnable() {
    @Override
    public void run() { System.out.println("线程执行"); }
}).start();

// Lambda 简化(Runnable本身就是策略接口)
new Thread(() -> System.out.println("线程执行")).start();

// 策略Map
Map<String, Function<Double, Double>> discounts = new HashMap<>();
discounts.put("VIP",    price -> price * 0.8);
discounts.put("NORMAL", price -> price * 0.9);
discounts.put("GUEST",  price -> price);

double finalPrice = discounts.get("VIP").apply(100.0); // 80.0

// ===== 命令模式 + Lambda =====
// 传统方式(需要接口和实现类)
// 现在直接用 Runnable 或自定义函数接口
List<Runnable> commands = new ArrayList<>();
commands.add(() -> System.out.println("执行命令1"));
commands.add(() -> System.out.println("执行命令2"));
commands.add(() -> System.out.println("执行命令3"));
commands.forEach(Runnable::run);

// ===== 观察者模式 + Lambda =====
// 传统方式需要定义Observer接口和实现类
// Java 8 可以直接用函数式接口
Map<String, List<Consumer<String>>> listeners = new HashMap<>();
listeners.put("click", new ArrayList<>());
listeners.get("click").add(event -> System.out.println("处理点击: " + event));
listeners.get("click").add(event -> System.out.println("记录点击日志: " + event));

// 触发
listeners.get("click").forEach(l -> l.accept("按钮被点击"));

// ===== 模板方法 + Lambda(函数回调)=====
// 传统模板方法需要继承
// Java 8 可以用函数参数代替继承
public class DataProcessor {
    // 模板方法:参数化需要变化的步骤
    public <T, R> R process(T data,
                             Function<T, T> preProcess,      // 预处理(可变)
                             Function<T, R> transform,       // 转换(可变)
                             Consumer<R> postProcess) {      // 后处理(可变)
        T preprocessed = preProcess.apply(data);
        R result = transform.apply(preprocessed);
        postProcess.accept(result);
        return result;
    }
}

// 使用
DataProcessor processor = new DataProcessor();
String result = processor.process(
    "  hello world  ",
    String::trim,                           // 预处理:去空格
    String::toUpperCase,                    // 转换:大写
    s -> System.out.println("结果: " + s)  // 后处理:打印
);

// ===== 建造者模式 + Lambda =====
// 更灵活的建造者
public class FlexibleBuilder<T> {
    private T object;
    
    public FlexibleBuilder(Supplier<T> constructor) {
        this.object = constructor.get();
    }
    
    public FlexibleBuilder<T> with(Consumer<T> setter) {
        setter.accept(object);
        return this;
    }
    
    public T build() { return object; }
}

// 使用(User不需要特殊的Builder方法)
User user = new FlexibleBuilder<>(User::new)
    .with(u -> u.setName("张三"))
    .with(u -> u.setAge(25))
    .with(u -> u.setEmail("zhangsan@example.com"))
    .build();

附录E:设计模式反面教材(反模式)

学设计模式,也要知道哪些是不好的做法(反模式)

E.1 贫血模型(Anti-Pattern)

复制代码
// ❌ 贫血模型:领域对象只有属性,没有行为
public class Order {
    private String id;
    private double totalPrice;
    private String status;
    // 只有 getter/setter,没有任何业务逻辑
}

// 业务逻辑全塞在 Service 里(过于臃肿)
public class OrderService {
    public void cancelOrder(Order order) {
        if (!"PENDING".equals(order.getStatus())) {
            throw new RuntimeException("只有待处理订单可以取消");
        }
        order.setStatus("CANCELLED");
        // ...大量逻辑
    }
}

// ✅ 充血模型:领域对象包含自己的行为
public class Order {
    private String id;
    private double totalPrice;
    private OrderStatus status;
    
    public void cancel() {
        if (status != OrderStatus.PENDING) {
            throw new IllegalStateException("只有待处理订单可以取消");
        }
        this.status = OrderStatus.CANCELLED;
        // 领域事件、状态变更逻辑
    }
    
    public boolean canShip() {
        return status == OrderStatus.PAID;
    }
}

E.2 万能类(God Object Anti-Pattern)

复制代码
// ❌ 万能类:一个类做所有事情(违反单一职责)
public class SystemManager {
    // 管理用户
    public void createUser() {}
    public void deleteUser() {}
    
    // 管理订单
    public void createOrder() {}
    public void cancelOrder() {}
    
    // 发送邮件
    public void sendEmail() {}
    
    // 操作数据库
    public void executeSQL() {}
    
    // 管理文件
    public void readFile() {}
    public void writeFile() {}
    
    // ... 100个方法
    // 这种类根本无法维护!
}

// ✅ 分而治之,每个类专注一件事
public class UserService { ... }
public class OrderService { ... }
public class EmailService { ... }
public class DatabaseHelper { ... }
public class FileManager { ... }

E.3 过度设计(Over-Engineering Anti-Pattern)

复制代码
// ❌ 过度设计:一个简单的配置读取用了5层抽象
public interface ConfigSource { }
public interface ConfigAdapter extends ConfigSource { }
public abstract class AbstractConfigFactory { }
public class ConcreteConfigFactory extends AbstractConfigFactory { }
public class ConfigManager {
    private AbstractConfigFactory factory;
    // ... 5个类就为了读一个配置文件?
}

// ✅ 够用就行
public class AppConfig {
    private static Properties props;
    static {
        props = new Properties();
        try { props.load(new FileInputStream("config.properties")); }
        catch (IOException e) { throw new RuntimeException(e); }
    }
    
    public static String get(String key) { return props.getProperty(key); }
}
// 简单、直接、够用!

附录F:快速记忆口诀

创建型5种(工单建抽原):

  • 工(工厂方法):一个产品一个工厂
  • 单(单例):全局唯一
  • 建(建造者):链式构建复杂对象
  • 抽(抽象工厂):整族产品一起创建
  • 原(原型):克隆现有对象

结构型7种(适桥组装外享代):

  • 适(适配器):接口转换
  • 桥(桥接):两维度独立变化
  • 组(组合):树形结构
  • 装(装饰器):功能叠加
  • 外(外观):简化接口
  • 享(享元):共享节省内存
  • 代(代理):控制访问

行为型11种(责命迭中备观状策模访解):

  • 责(责任链):请求沿链传递
  • 命(命令):请求封装成对象
  • 迭(迭代器):顺序访问集合
  • 中(中介者):集中协调
  • 备(备忘录):存档读档
  • 观(观察者):发布订阅
  • 状(状态):状态决定行为
  • 策(策略):算法可替换
  • 模(模板方法):固定骨架
  • 访(访问者):不改类加操作
  • 解(解释器):语法解析

全文完。感谢阅读,如果对你有帮助,请分享给更多学习 Java 的朋友!

版本:1.0 | 字数:约5万字 | 适合:Java初学者到中级开发者

相关推荐
zs宝来了2 小时前
网络篇15-网络收发包应用之iptable
开发语言·网络·php
烤麻辣烫2 小时前
算法--二分搜索
java·开发语言·学习·算法·intellij-idea
编码浪子2 小时前
《安全 Rust 的边界在哪?》— 中文解读
开发语言·安全·rust
逍遥德2 小时前
MQTT教程详解-03. 高级知识点
java·物联网·中间件·信息与通信·iot·iotdb
kyriewen112 小时前
Next.js:让你的React应用从“裸奔”到“穿衣服”
开发语言·前端·javascript·react.js·设计模式·ecmascript
Nice__J2 小时前
ISO26262功能安全——SafeOS
java·linux·安全
三品吉他手会点灯2 小时前
C语言学习笔记 - 18.C编程预备计算机专业知识 - 什么是变量
c语言·开发语言·笔记·学习
好奇龙猫2 小时前
[大学院-python-base gammer learning2: python base programming ]
开发语言·python