万字书籍速通之《Java设计模式》

📖 阅读提示:本文是对《Java设计模式》(刘伟 著)一书的万字精华速通,涵盖 4大板块、26种经典设计模式、7大设计原则。全文约15000字,建议阅读时间40分钟。无论你是初学设计模式的新手,还是需要系统复习的老手,这篇文章都能帮你快速建立完整的知识体系。
🏷️ 标签:Java 设计模式 GoF 面向对象 架构设计 面试
📑 目录
- 第一部分:设计模式基础
- 第二部分:创建型设计模式
- 第三部分:结构型设计模式
- 第四部分:行为型设计模式
- 附录:UML类图
- 全文总结
第一部分:设计模式基础
第1章 设计模式概述
1.1 设计模式的诞生与发展
模式的诞生
“模式”(Pattern)的概念最早由建筑大师 Christopher Alexander 在1977年的著作《A Pattern Language》中提出:
“每个模式都描述了一个在我们的环境中不断出现的问题,然后描述了该问题的解决方案的核心。”
这一思想后来被引入软件工程领域,彻底改变了软件开发的方式。
软件模式概述
软件模式按抽象层次可分为三级:
| 高 | 架构模式 | 系统级别的组织策略(如MVC、微服务) |
| 中 | 设计模式 | 类与对象级别的通用解决方案 |
| 低 | 惯用法(Idiom) | 特定语言的编程技巧 |
设计模式的发展
1995年,Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides 四人(被尊称为 “GoF”,Gang of Four)出版了划时代著作《Design Patterns: Elements of Reusable Object-Oriented Software》,系统化地提出了 23种经典设计模式,奠定了现代软件设计的基石。
1.2 设计模式的定义与分类
设计模式的基本要素
每个设计模式通常包含四个核心要素:
| 模式名称 | 用一两个词描述模式的问题、解决方案和效果 |
| 问题 | 描述应该在何时使用该模式 |
| 解决方案 | 描述设计的组成成分及它们之间的关系 |
| 效果 | 描述模式应用的效果及应权衡的问题 |
设计模式的三大分类
设计模式
├── 创建型模式(Creational)── 5种 ── 关注"怎么创建对象"
├── 结构型模式(Structural)── 7种 ── 关注"怎么组合类/对象"
└── 行为型模式(Behavioral)── 11种 ── 关注"对象间怎么通信"
1.3 GoF设计模式简介
GoF 23种设计模式全景图:
| 创建型 | 工厂方法、抽象工厂、建造者、原型、单例 |
| 结构型 | 适配器、桥接、组合、装饰、外观、享元、代理 |
| 行为型 | 职责链、命令、解释器、迭代器、中介者、备忘录、观察者、状态、策略、模板方法、访问者 |
⚠️ 注意:简单工厂模式不属于GoF 23种,但使用极为广泛,本书将其作为工厂方法的前置知识讲解。
1.4 设计模式的优点
第2章 面向对象设计原则
2.1 面向对象设计原则概述
设计原则是设计模式的灵魂与基石。掌握七大原则(SOLID + 合成复用 + 迪米特),是理解和运用设计模式的前提。
SOLID 原则
├── S ── 单一职责原则(SRP)
├── O ── 开闭原则(OCP)
├── L ── 里氏代换原则(LSP)
├── I ── 接口隔离原则(ISP)
└── D ── 依赖倒转原则(DIP)
补充原则
├── 合成复用原则(CRP)
└── 迪米特法则(LoD)
2.2 单一职责原则(SRP)
🎯 一个类应该只有一个引起它变化的原因。
// ❌ 违反SRP:一个类承担多种职责
public class Employee {
public void calculatePay() { /* 计算薪资 */ }
public void saveToDatabase() { /* 持久化 */ }
public void generateReport() { /* 生成报表 */ }
}
// ✅ 遵循SRP:职责分离
public class Employee { /* 员工基本信息 */ }
public class PayCalculator { public void calculate(Employee e) { } }
public class EmployeeRepository { public void save(Employee e) { } }
public class ReportGenerator { public void generate(Employee e) { } }
2.3 开闭原则(OCP)
🎯 软件实体应该对扩展开放,对修改关闭。
这是设计模式中最重要的原则,几乎所有模式都在践行它。
// ✅ 对扩展开放,对修改关闭
public interface Shape {
double area();
}
public class Circle implements Shape {
private double r;
public double area() { return Math.PI * r * r; }
}
// 新增三角形,无需修改任何已有代码
public class Triangle implements Shape {
private double base, height;
public double area() { return 0.5 * base * height; }
}
2.4 里氏代换原则(LSP)
🎯 所有引用基类的地方必须能透明地使用其子类的对象。
子类可以扩展父类的功能,但不能改变父类原有的功能。
💡 经典反例:“正方形继承长方形”——正方形修改宽度时高度也变,违反了长方形的行为契约。
2.5 依赖倒转原则(DIP)
🎯 高层模块不应该依赖低层模块,两者都应该依赖其抽象。
// ❌ 高层直接依赖低层具体类
public class Computer {
private KingstonRAM ram = new KingstonRAM();
}
// ✅ 依赖抽象接口
public class Computer {
private RAM ram; // 依赖抽象
public Computer(RAM ram) { this.ram = ram; }
}
2.6 接口隔离原则(ISP)
🎯 客户端不应该被迫依赖于它不使用的方法。
将臃肿接口拆分为更小、更具体的接口:
// ❌ 胖接口
public interface Worker {
void work();
void eat();
void sleep();
}
// ✅ 接口隔离
public interface Workable { void work(); }
public interface Eatable { void eat(); }
public interface Sleepable { void sleep(); }
2.7 合成复用原则(CRP)
🎯 优先使用对象组合(has-a),而非继承(is-a)来达到复用目的。
| 继承(is-a) | 实现简单 | 白箱复用,暴露父类细节,耦合度高 |
| 组合(has-a) | 封装性好,运行时可替换 | 需要额外的委托代码 |
2.8 迪米特法则(LoD)
🎯 一个对象应该对其他对象保持最少的了解,只与"直接朋友"通信。
又称"最少知识原则",核心思想是降低耦合。
// ❌ 陌生人通信
person.getWallet().getMoney().getAmount();
// ✅ 只与直接朋友通信
person.getBalance();
第二部分:创建型设计模式
🏭 核心思想:将对象的创建与使用分离,降低系统耦合度。
第3章 简单工厂模式
3.1 创建型模式概述
创建型模式关注"怎样创建对象",主要包括:简单工厂、工厂方法、抽象工厂、建造者、原型、单例。
3.2 简单工厂模式概述
简单工厂模式(Simple Factory Pattern)又称静态工厂方法模式,由一个工厂对象决定创建出哪一种产品类的实例。
⚠️ 它不属于GoF 23种模式,但使用极为广泛,是学习工厂方法的前置知识。
3.3 简单工厂模式结构与实现
角色组成:
┌─────────────────────────────────────────┐
│ SimpleFactory(工厂) │
│ + createProduct(type): Product │
└─────────────────┬───────────────────────┘
│ 创建
┌─────────┴─────────┐
▼ ▼
┌──────────────┐ ┌──────────────┐
│ ProductA │ │ ProductB │
└──────────────┘ └──────────────┘
▲ ▲
└─────────┬─────────┘
│
┌─────────┴─────────┐
│ Product(抽象) │
│ + use(): void │
└───────────────────┘
// 抽象产品
public abstract class Product {
public abstract void use();
}
// 具体产品
public class ProductA extends Product {
@Override
public void use() { System.out.println("使用产品A"); }
}
public class ProductB extends Product {
@Override
public void use() { System.out.println("使用产品B"); }
}
// 工厂类
public class SimpleFactory {
public static Product createProduct(String type) {
switch (type) {
case "A": return new ProductA();
case "B": return new ProductB();
default: throw new IllegalArgumentException("未知类型:" + type);
}
}
}
// 客户端
public class Client {
public static void main(String[] args) {
Product product = SimpleFactory.createProduct("A");
product.use(); // 输出:使用产品A
}
}
3.4 简单工厂模式应用实例
以图表库为例:根据类型字符串创建柱状图(HistogramChart)、饼图(PieChart)、折线图(LineChart)。
3.5 关于创建对象与使用对象
将创建逻辑集中到工厂中,客户端无需关心具体类的实例化过程,实现了创建与使用的分离。
3.6 简单工厂模式的简化
可以使用反射机制简化if-else判断:
public class SimpleFactory {
public static Product createProduct(String className) {
try {
return (Product) Class.forName(className).newInstance();
} catch (Exception e) {
throw new RuntimeException("创建失败", e);
}
}
}
配合配置文件,更换产品无需修改代码。
3.7 简单工厂模式优缺点与适用环境
| 实现创建与使用分离 | 工厂类职责过重,违反SRP |
| 客户端无需知道具体类名 | 新增产品需修改工厂代码,违反OCP |
| 通过配置文件提高灵活性 | 使用静态方法,无法形成继承结构 |
适用环境:产品种类较少、不经常变化的场景。
第4章 工厂方法模式
4.1 工厂方法模式概述
工厂方法模式(Factory Method Pattern)定义一个用于创建对象的接口,让子类决定将哪一个类实例化。它是对简单工厂的进一步抽象,完美解决了OCP问题。
4.2 工厂方法模式结构与实现
// 抽象工厂
public abstract class Factory {
public abstract Product createProduct();
}
// 具体工厂A
public class ConcreteFactoryA extends Factory {
@Override
public Product createProduct() { return new ProductA(); }
}
// 具体工厂B
public class ConcreteFactoryB extends Factory {
@Override
public Product createProduct() { return new ProductB(); }
}
// 客户端:面向抽象编程
public class Client {
public static void main(String[] args) {
Factory factory = new ConcreteFactoryA(); // 可替换为B
Product product = factory.createProduct();
product.use();
}
}
4.3 工厂方法模式应用实例
以日志记录器为例:
- FileLoggerFactory → 创建文件日志记录器
- DatabaseLoggerFactory → 创建数据库日志记录器
4.4 反射机制与配置文件
结合Java反射和配置文件,可以在不修改代码的情况下更换具体工厂:
# config.properties
factoryClass=com.example.ConcreteFactoryA
// 读取配置并反射创建
Properties props = new Properties();
props.load(new FileInputStream("config.properties"));
String className = props.getProperty("factoryClass");
Factory factory = (Factory) Class.forName(className).getDeclaredConstructor().newInstance();
4.5 工厂方法的重载
工厂方法可以提供多个重载版本,支持不同参数的创建方式:
public abstract class LoggerFactory {
public abstract Logger createLogger();
public Logger createLogger(String name) { /* 带名称 */ }
public Logger createLogger(Object obj) { /* 带配置对象 */ }
}
4.6 工厂方法的隐藏
将工厂方法设为protected,通过模板方法模式对外提供统一接口。
4.7 工厂方法模式优缺点与适用环境
| 符合OCP,新增产品无需修改已有代码 | 每增加一个产品就需增加一个工厂类 |
| 符合SRP,每个工厂只负责一种产品 | 增加了系统抽象性和理解难度 |
| 支持多态,客户端面向抽象编程 | 只能创建同一等级的产品 |
第5章 抽象工厂模式
5.1 产品等级结构与产品族
理解两个核心概念:
产品等级结构(纵向) 产品族(横向)
Button(抽象) Mac工厂 Win工厂
├── MacButton ┌────────┐ ┌────────┐
└── WinButton │MacBtn │ │WinBtn │
│MacWin │ │WinWin │
Window(抽象) │MacMenu │ │WinMenu │
├── MacWindow └────────┘ └────────┘
└── WinWindow
- 产品等级结构:产品的继承结构(如 Shape → Circle, Rectangle)
- 产品族:同一工厂生产的位于不同等级的一组产品
5.2 抽象工厂模式概述
抽象工厂模式(Abstract Factory Pattern)提供一个创建一系列相关或相互依赖对象的接口,无需指定具体类。
5.3 抽象工厂模式结构与实现
// 抽象工厂:创建一族产品
public abstract class UIFactory {
public abstract Button createButton();
public abstract Window createWindow();
}
// 具体工厂:Mac风格
public class MacFactory extends UIFactory {
public Button createButton() { return new MacButton(); }
public Window createWindow() { return new MacWindow(); }
}
// 具体工厂:Windows风格
public class WinFactory extends UIFactory {
public Button createButton() { return new WinButton(); }
public Window createWindow() { return new WinWindow(); }
}
// 客户端
public class Client {
public static void main(String[] args) {
UIFactory factory = new MacFactory(); // 切换工厂即可切换整套UI
Button btn = factory.createButton();
Window win = factory.createWindow();
}
}
5.4 抽象工厂模式应用实例
- 跨平台UI工具包(Mac/Windows/Linux风格)
- 多数据库访问层(MySQL工厂、Oracle工厂各自创建Connection、Command、DataReader)
5.5 开闭原则的倾斜性
⚠️ 抽象工厂模式对"增加产品族"符合OCP,但对"增加产品等级结构"违反OCP(需修改所有工厂接口和实现)。
这就是所谓的 “开闭原则的倾斜性”。
5.6 抽象工厂模式优缺点与适用环境
| 保证同族产品的一致性 | 增加新产品等级结构困难 |
| 切换产品族非常方便 | 系统扩展性受限 |
| 客户端与具体类解耦 | 类数量增长较快 |
适用环境:系统中有多个产品族,且每次只使用其中一族产品。
第6章 建造者模式
6.1 建造者模式概述
建造者模式(Builder Pattern)将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
💡 核心场景:创建步骤固定,但各步骤的具体实现不同。
6.2 建造者模式结构与实现
// 产品类
public class Computer {
private String cpu;
private String ram;
private String storage;
// getters & setters & toString
}
// 抽象建造者
public abstract class ComputerBuilder {
protected Computer computer = new Computer();
public abstract void buildCPU();
public abstract void buildRAM();
public abstract void buildStorage();
public Computer getResult() { return computer; }
}
// 具体建造者
public class GamingPCBuilder extends ComputerBuilder {
public void buildCPU() { computer.setCpu("Intel i9"); }
public void buildRAM() { computer.setRam("64GB DDR5"); }
public void buildStorage() { computer.setStorage("2TB NVMe SSD"); }
}
// 指挥者:控制构建顺序
public class Director {
private ComputerBuilder builder;
public Director(ComputerBuilder builder) { this.builder = builder; }
public Computer construct() {
builder.buildCPU();
builder.buildRAM();
builder.buildStorage();
return builder.getResult();
}
}
6.3 建造者模式应用实例
- 游戏角色创建(种族、职业、外观、技能)
- 复杂文档生成(PDF报告)
- SQL查询构建器(MyBatis的SqlSessionFactoryBuilder)
6.4 指挥者类的深入讨论
指挥者(Director)控制构建顺序。在实际开发中,指挥者常被省略,由客户端直接控制构建过程(如 Lombok的@Builder、StringBuilder)。
6.5 建造者模式优缺点与适用环境
| 构建与表示分离 | 产品差异大时复用性差 |
| 可精细控制构建过程 | 增加了建造者类的数量 |
| 易于扩展新建造者 | 不适合产品变化频繁的场景 |
适用环境:创建复杂对象,构建步骤固定但各步骤具体实现可变。
第7章 原型模式
7.1 原型模式概述
原型模式(Prototype Pattern)用原型实例指定创建对象的种类,并通过拷贝这些原型创建新的对象。避免了重复的初始化过程。
7.2 原型模式结构与实现(浅克隆与深克隆)
// ========== 浅克隆 ==========
public class ShallowClone implements Cloneable {
private String name;
private List<String> hobbies; // 引用类型
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); // ⚠️ hobbies引用同一对象!
}
}
// ========== 深克隆(序列化方式) ==========
public class DeepClone implements Serializable {
private String name;
private List<String> hobbies;
public DeepClone deepClone() {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (DeepClone) ois.readObject();
} catch (Exception e) {
throw new RuntimeException("深克隆失败", e);
}
}
}
⚠️ 浅克隆:只复制基本类型字段,引用类型字段共享同一对象。 深克隆:递归复制所有引用对象,完全独立。
7.3 原型模式应用实例
- 邮件群发时复制邮件对象(只改收件人)
- 棋盘状态快照(保存/恢复)
- 对象池中的对象复用
7.4 原型管理器
原型管理器(Prototype Manager)维护一个原型注册表,客户端通过键值获取克隆对象:
public class PrototypeManager {
private static Map<String, Prototype> registry = new HashMap<>();
public static void register(String key, Prototype proto) {
registry.put(key, proto);
}
public static Prototype getClone(String key) {
return registry.get(key).clone();
}
}
7.5 原型模式优缺点与适用环境
| 避免重复初始化,性能更优 | 深克隆实现复杂 |
| 动态增加/删除产品 | 需要为每个类配备clone方法 |
| 简化对象创建 | 对已有类改造需实现Cloneable |
适用环境:创建新对象成本较大;需要避免使用分层次的工厂类。
第8章 单例模式
8.1 单例模式概述
单例模式(Singleton Pattern)确保一个类只有一个实例,并提供一个全局访问点。
8.2 单例模式结构与实现
三个核心要点:
8.3 单例模式应用实例
数据库连接池、线程池、配置管理器、日志记录器、Spring中的Bean(默认单例)。
8.4 饿汉式单例与懒汉式单例
// ========== 饿汉式:类加载时创建,天然线程安全 ==========
public class HungrySingleton {
private static final HungrySingleton INSTANCE = new HungrySingleton();
private HungrySingleton() {}
public static HungrySingleton getInstance() { return INSTANCE; }
}
// ========== 懒汉式(双重检查锁DCL):延迟创建 ==========
public class LazySingleton {
private static volatile LazySingleton instance; // volatile防止指令重排
private LazySingleton() {}
public static LazySingleton getInstance() {
if (instance == null) { // 第一次检查(无锁)
synchronized (LazySingleton.class) {
if (instance == null) { // 第二次检查(有锁)
instance = new LazySingleton();
}
}
}
return instance;
}
}
8.5 使用静态内部类和枚举实现单例模式
// ========== 静态内部类:延迟加载 + 线程安全(推荐) ==========
public class InnerClassSingleton {
private InnerClassSingleton() {}
private static class Holder {
private static final InnerClassSingleton INSTANCE = new InnerClassSingleton();
}
public static InnerClassSingleton getInstance() {
return Holder.INSTANCE;
}
}
// ========== 枚举单例:最简洁,天然防反射和序列化破坏(最佳) ==========
public enum EnumSingleton {
INSTANCE;
public void doSomething() {
System.out.println("执行业务逻辑");
}
}
8.6 单例模式优缺点与适用环境
| 节约系统资源 | 扩展困难,不利于子类化 |
| 全局唯一,避免冲突 | 违反SRP(既管创建又管业务) |
| 延迟加载节省内存 | 对并发测试不友好 |
适用环境:系统中某个类只需要一个实例(如配置、连接池、日志)。
第三部分:结构型设计模式
🧩 核心思想:将类或对象按照某种布局组合成更大的结构,实现新的功能。
第9章 适配器模式
9.1 结构型模式概述
结构型模式关注如何将类或对象组合成更大的结构。包括:适配器、桥接、组合、装饰、外观、享元、代理。
9.2 适配器模式概述
适配器模式(Adapter Pattern)将一个类的接口转换成客户希望的另一个接口。使得原本因接口不兼容而不能一起工作的类可以一起工作。
💡 生活类比:电源转换插头(中国插头 → 欧标插座)
9.3 适配器模式结构与实现
// 目标接口(客户端期望的接口)
public interface Target {
void request();
}
// 被适配者(已有的、不兼容的接口)
public class Adaptee {
public void specificRequest() {
System.out.println("被适配者的特殊请求");
}
}
// 对象适配器(组合方式,推荐)
public class ObjectAdapter implements Target {
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest(); // 委托调用
}
}
// 类适配器(继承方式)
public class ClassAdapter extends Adaptee implements Target {
@Override
public void request() {
this.specificRequest(); // 直接调用父类方法
}
}
9.4 适配器模式应用实例
- 电压转换器(220V → 110V)
- 旧系统接口兼容
- 第三方SDK封装(如支付SDK统一接口)
- Arrays.asList() 将数组适配为List
9.5 缺省适配器模式
当不需要实现接口中所有方法时,提供一个抽象类实现接口的所有方法(空实现):
// JDK中的经典案例
public abstract class WindowAdapter implements WindowListener {
public void windowOpened(WindowEvent e) {}
public void windowClosing(WindowEvent e) {}
public void windowClosed(WindowEvent e) {}
// … 其他空实现
}
// 只需覆盖关心的方法
public class MyListener extends WindowAdapter {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
}
9.6 双向适配器
同时适配两个接口,使得两个不兼容的系统可以互相调用。
9.7 适配器模式优缺点与适用环境
| 复用现有类,无需修改源码 | 过多使用会使系统结构混乱 |
| 增加了类的透明性和复用性 | 一次只能适配一个被适配者 |
| 灵活性好,可随时替换 | 类适配器受单继承限制 |
适用环境:系统需要使用现有类,但其接口不符合需求。
第10章 桥接模式
10.1 桥接模式概述
桥接模式(Bridge Pattern)将抽象部分与它的实现部分分离,使它们都可以独立地变化。解决多维度变化导致的类爆炸问题。
💡 经典场景:形状(圆、方)× 颜色(红、蓝)→ 不用桥接需要4个类,3个维度就需要8个类!
10.2 桥接模式结构与实现
// ===== 实现化维度:颜色 =====
public interface Color {
String getColor();
}
public class Red implements Color {
public String getColor() { return "红色"; }
}
public class Blue implements Color {
public String getColor() { return "蓝色"; }
}
// ===== 抽象化维度:形状 =====
public abstract class Shape {
protected Color color; // 桥接点
public Shape(Color color) { this.color = color; }
public abstract void draw();
}
public class Circle extends Shape {
public Circle(Color color) { super(color); }
@Override
public void draw() {
System.out.println("画一个" + color.getColor() + "的圆形");
}
}
// 客户端:自由组合
Shape redCircle = new Circle(new Red());
Shape blueCircle = new Circle(new Blue());
10.3 桥接模式应用实例
- 跨平台图形库(形状 × 颜色 × 平台)
- JDBC驱动架构(抽象API × 具体数据库驱动)
- 遥控器 × 电视机(不同品牌电视用同一遥控器)
10.4 桥接模式与适配器模式的联用
| 使用时机 | 设计阶段(预防性) | 实现阶段(补救性) |
| 目的 | 分离多维度变化 | 兼容已有接口 |
两者可以联合使用。
10.5 桥接模式优缺点与适用环境
适用环境:一个类存在两个(或多个)独立变化的维度;不希望使用继承导致类数量急剧增加。
第11章 组合模式
11.1 组合模式概述
组合模式(Composite Pattern)将对象组合成树形结构以表示"部分-整体"的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
11.2 组合模式结构与实现
// 抽象构件
public abstract class Component {
protected String name;
public Component(String name) { this.name = name; }
public abstract void operation(int depth);
}
// 叶子构件
public class Leaf extends Component {
public Leaf(String name) { super(name); }
@Override
public void operation(int depth) {
System.out.println("-".repeat(depth) + name);
}
}
// 容器构件
public class Composite extends Component {
private List<Component> children = new ArrayList<>();
public Composite(String name) { super(name); }
public void add(Component c) { children.add(c); }
public void remove(Component c) { children.remove(c); }
@Override
public void operation(int depth) {
System.out.println("-".repeat(depth) + name);
for (Component child : children) {
child.operation(depth + 2);
}
}
}
11.3 组合模式应用实例
- 文件系统(文件/文件夹)
- 组织架构(公司→部门→小组→员工)
- XML/HTML文档解析
- GUI容器(Window→Panel→Button)
11.4 透明组合模式与安全组合模式
| 透明式 | Component中声明add/remove | 叶子节点调用add无意义,需抛异常 |
| 安全式 | 只在Composite中声明管理方法 | 客户端需区分叶子和容器 |
11.5 组合模式优缺点与适用环境
适用环境:表示对象的部分-整体层次结构;希望用户忽略组合对象与单个对象的不同。
第12章 装饰模式
12.1 装饰模式概述
装饰模式(Decorator Pattern)动态地给一个对象添加一些额外的职责,比继承更灵活。是"组合优于继承"原则的典型体现。
💡 Java I/O流就是装饰模式的教科书级应用!
12.2 装饰模式结构与实现
// 抽象构件
public interface DataSource {
void writeData(String data);
String readData();
}
// 具体构件
public class FileDataSource implements DataSource {
private String filename;
public FileDataSource(String filename) { this.filename = filename; }
public void writeData(String data) { /* 写文件 */ }
public String readData() { return "文件内容"; }
}
// 装饰器基类(关键:实现同一接口 + 持有构件引用)
public class DataSourceDecorator implements DataSource {
protected DataSource wrappee;
public DataSourceDecorator(DataSource source) { this.wrappee = source; }
public void writeData(String data) { wrappee.writeData(data); }
public String readData() { return wrappee.readData(); }
}
// 具体装饰器:加密
public class EncryptionDecorator extends DataSourceDecorator {
public EncryptionDecorator(DataSource source) { super(source); }
@Override
public void writeData(String data) {
super.writeData(encrypt(data)); // 增强后再写入
}
private String encrypt(String data) { return "ENC(" + data + ")"; }
}
// 客户端:像套娃一样自由组合
DataSource source = new FileDataSource("data.txt");
source = new EncryptionDecorator(source); // 加加密
source = new CompressionDecorator(source); // 再加压缩
source.writeData("Hello");
12.3 装饰模式应用实例
- Java I/O流:new BufferedInputStream(new FileInputStream("a.txt"))
- Web开发中的Filter/Interceptor
- Spring中的BeanPostProcessor
12.4 透明装饰模式与半透明装饰模式
- 透明式:装饰器与构件实现同一接口,可递归装饰(如Java I/O)
- 半透明式:装饰器增加新方法,客户端需知道具体装饰器类型
12.5 装饰模式优缺点与适用环境
| 比继承更灵活,动态扩展 | 产生大量小对象 |
| 可自由组合多个装饰器 | 调试困难(层层嵌套) |
| 符合OCP | 初始化代码复杂 |
第13章 外观模式
13.1 外观模式概述
外观模式(Facade Pattern)为子系统中的一组接口提供一个一致的高层界面,使得子系统更加容易使用。
💡 生活类比:酒店前台(一个入口搞定住宿、餐饮、交通)
13.2 外观模式结构与实现
// 子系统组件
public class CPU {
public void start() { System.out.println("CPU启动"); }
public void shutdown() { System.out.println("CPU关闭"); }
}
public class Memory {
public void load() { System.out.println("内存加载"); }
public void free() { System.out.println("内存释放"); }
}
public class HardDrive {
public void read() { System.out.println("硬盘读取"); }
}
// 外观类:一键操作
public class ComputerFacade {
private CPU cpu = new CPU();
private Memory memory = new Memory();
private HardDrive hd = new HardDrive();
public void start() {
cpu.start();
memory.load();
hd.read();
System.out.println("✅ 电脑启动完成");
}
public void shutdown() {
memory.free();
cpu.shutdown();
System.out.println("✅ 电脑已关闭");
}
}
// 客户端:极简调用
new ComputerFacade().start();
13.3 外观模式应用实例
- 编译器(将词法分析、语法分析、语义分析、代码生成封装为compile())
- MVC中的Service层
- 电商下单流程(库存、支付、物流、通知)
13.4 抽象外观类
引入抽象外观类,支持更换具体外观实现,符合OCP。
13.5 外观模式优缺点与适用环境
适用环境:为复杂子系统提供简单接口;客户程序与多个子系统间存在很大依赖性。
第14章 享元模式
14.1 享元模式概述
享元模式(Flyweight Pattern)运用共享技术有效地支持大量细粒度对象的复用。通过共享已存在的对象来减少内存开销。
14.2 享元模式结构与实现
// 抽象享元
public abstract class ChessPiece {
protected String color; // 内部状态(可共享)
public ChessPiece(String color) { this.color = color; }
public abstract void display(int x, int y); // 外部状态作为参数传入
}
// 具体享元
public class ConcreteChess extends ChessPiece {
public ConcreteChess(String color) { super(color); }
@Override
public void display(int x, int y) {
System.out.println(color + "棋子,位置:(" + x + "," + y + ")");
}
}
// 享元工厂(核心:池化管理)
public class ChessFactory {
private static Map<String, ChessPiece> pool = new HashMap<>();
public static ChessPiece getPiece(String color) {
return pool.computeIfAbsent(color, ConcreteChess::new);
}
}
14.3 享元模式应用实例
- 围棋/象棋棋子(颜色共享,位置不同)
- 文本编辑器中的字符对象
- 数据库连接池、线程池
14.4 有外部状态的享元模式
- 内部状态(Intrinsic State):可共享,存储在享元内部
- 外部状态(Extrinsic State):不可共享,由客户端传入
14.5 单纯享元模式与复合享元模式
- 单纯享元:所有享元对象都可共享
- 复合享元:由单纯享元组合而成,本身不可共享
14.6 享元模式与String类
Java的String常量池就是享元模式的典型应用:
String s1 = "hello";
String s2 = "hello";
System.out.println(s1 == s2); // true!共享同一对象
14.7 享元模式优缺点与适用环境
适用环境:系统中存在大量相似对象;需要缓冲池;对象的大部分状态可以外部化。
第15章 代理模式
15.1 代理模式概述
代理模式(Proxy Pattern)给某一个对象提供一个代理,并由代理对象控制对原对象的引用。
15.2 代理模式结构与实现
// 抽象主题
public interface Subject {
void request();
}
// 真实主题
public class RealSubject implements Subject {
@Override
public void request() {
System.out.println("真实请求处理");
}
}
// 静态代理
public class Proxy implements Subject {
private RealSubject realSubject;
@Override
public void request() {
System.out.println("【前置处理】权限校验");
if (realSubject == null) {
realSubject = new RealSubject(); // 延迟创建
}
realSubject.request();
System.out.println("【后置处理】记录日志");
}
}
15.3 代理模式应用实例
- Spring AOP(面向切面编程)
- RPC远程调用(Dubbo、gRPC)
- MyBatis的Mapper接口
- 图片懒加载
15.4 远程代理
为不同地址空间的对象提供局部代表,如RMI、Dubbo、gRPC。
15.5 虚拟代理
延迟创建开销大的对象,如大图片的懒加载、Hibernate的延迟加载。
15.6 动态代理
// JDK动态代理
public class DynamicProxyHandler implements InvocationHandler {
private Object target;
public DynamicProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("【Before】" + method.getName());
Object result = method.invoke(target, args);
System.out.println("【After】" + method.getName());
return result;
}
}
// 创建代理实例
Subject proxy = (Subject) Proxy.newProxyInstance(
Subject.class.getClassLoader(),
new Class[]{Subject.class},
new DynamicProxyHandler(new RealSubject())
);
proxy.request();
15.7 代理模式优缺点与适用环境
| 远程代理 | RPC、分布式系统 |
| 虚拟代理 | 大对象延迟加载 |
| 保护代理 | 权限控制 |
| 缓冲代理 | 缓存(如Redis) |
| 智能引用代理 | 引用计数、COW |
第四部分:行为型设计模式
🔄 核心思想:关注对象之间的通信、职责分配和算法封装。
第16章 职责链模式
16.1 行为型模式概述
行为型模式关注对象之间的通信和职责分配,共11种模式。
16.2 职责链模式概述
职责链模式(Chain of Responsibility Pattern)将请求的发送者和接收者解耦,使多个对象都有机会处理请求,将这些对象连成一条链。
16.3 职责链模式结构与实现
// 抽象处理者
public abstract class Handler {
protected Handler successor;
public void setSuccessor(Handler successor) {
this.successor = successor;
}
public abstract void handleRequest(int amount);
}
// 具体处理者:经理(≤5000)
public class Manager extends Handler {
@Override
public void handleRequest(int amount) {
if (amount <= 5000) {
System.out.println("经理审批通过:" + amount + "元");
} else if (successor != null) {
successor.handleRequest(amount);
}
}
}
// 具体处理者:总监(≤50000)
public class Director extends Handler {
@Override
public void handleRequest(int amount) {
if (amount <= 50000) {
System.out.println("总监审批通过:" + amount + "元");
} else if (successor != null) {
successor.handleRequest(amount);
}
}
}
// 具体处理者:CEO(无上限)
public class CEO extends Handler {
@Override
public void handleRequest(int amount) {
System.out.println("CEO审批通过:" + amount + "元");
}
}
// 客户端:构建链
Handler manager = new Manager();
Handler director = new Director();
Handler ceo = new CEO();
manager.setSuccessor(director);
director.setSuccessor(ceo);
manager.handleRequest(3000); // 经理审批
manager.handleRequest(30000); // 总监审批
manager.handleRequest(100000); // CEO审批
16.4 职责链模式应用实例
- 审批流程(OA系统)
- Servlet Filter链
- Netty ChannelPipeline
- 异常处理链(try-catch)
- Spring Security过滤器链
16.5 纯与不纯的职责链模式
- 纯职责链:请求必须被某个处理者处理或明确拒绝
- 不纯职责链:请求可以被部分处理,也可以不被任何处理者处理
16.6 职责链模式优缺点与适用环境
适用环境:有多个对象可以处理同一请求;需要动态指定处理者集合。
第17章 命令模式
17.1 命令模式概述
命令模式(Command Pattern)将一个请求封装为一个对象,从而可以用不同的请求对客户进行参数化,支持请求的排队、记录日志、撤销操作。
17.2 命令模式结构与实现
// 接收者
public class Light {
public void on() { System.out.println("💡 灯亮了"); }
public void off() { System.out.println("🌑 灯灭了"); }
}
// 抽象命令
public interface Command {
void execute();
void undo();
}
// 具体命令
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) { this.light = light; }
public void execute() { light.on(); }
public void undo() { light.off(); }
}
// 调用者(遥控器)
public class RemoteControl {
private Command command;
private Stack<Command> history = new Stack<>();
public void setCommand(Command cmd) { this.command = cmd; }
public void pressButton() {
command.execute();
history.push(command);
}
public void pressUndo() {
if (!history.isEmpty()) {
history.pop().undo();
}
}
}
17.3 命令模式应用实例
- GUI按钮操作
- 文本编辑器的撤销/重做(Ctrl+Z / Ctrl+Y)
- 任务调度系统
- 数据库事务日志
17.4 实现命令队列
将命令对象放入队列,实现异步执行和批量处理。
17.5 记录请求日志
将执行过的命令序列化存储,系统崩溃后可恢复重放。
17.6 实现撤销操作
通过undo()方法或命令历史栈实现多级撤销。
17.7 宏命令
组合多个命令为一个宏命令,一次执行多个操作(如"一键下班":关灯+关空调+锁门)。
17.8 命令模式优缺点与适用环境
适用环境:需要将请求调用者和接收者解耦;需要支持撤销和恢复;需要支持事务。
第18章 解释器模式
18.1 解释器模式概述
解释器模式(Interpreter Pattern)给定一个语言,定义它的文法的一种表示,并定义一个解释器来解释该语言中的句子。
18.2 文法规则和抽象语法树
通过文法规则(BNF)描述语言结构,构建抽象语法树(AST) 进行解析。
表达式文法示例:
expression ::= term (('+' | '-') term)*
term ::= number
18.3 解释器模式结构与实现
// 抽象表达式
public abstract class Expression {
public abstract int interpret();
}
// 终结符表达式(数字)
public class NumberExpression extends Expression {
private int number;
public NumberExpression(int number) { this.number = number; }
public int interpret() { return number; }
}
// 非终结符表达式(加法)
public class AddExpression extends Expression {
private Expression left, right;
public AddExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
public int interpret() {
return left.interpret() + right.interpret();
}
}
// 使用:解释 3 + 5
Expression expr = new AddExpression(
new NumberExpression(3),
new NumberExpression(5)
);
System.out.println(expr.interpret()); // 输出:8
18.4 解释器模式应用实例
- 正则表达式引擎
- SQL解析器
- 数学表达式计算器
- 编译器(javac)
18.5 解释器模式优缺点与适用环境
| 易于扩展文法规则 | 复杂文法难以维护 |
| 每条文法规则对应一个类 | 执行效率较低 |
| 实现简单 | 实际开发中使用极少 |
适用环境:文法简单、效率不是关键问题的场景。
第19章 迭代器模式
19.1 迭代器模式概述
迭代器模式(Iterator Pattern)提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
19.2 迭代器模式结构与实现
// 抽象迭代器
public interface Iterator<T> {
boolean hasNext();
T next();
}
// 具体迭代器
public class ConcreteIterator<T> implements Iterator<T> {
private List<T> list;
private int index = 0;
public ConcreteIterator(List<T> list) { this.list = list; }
public boolean hasNext() { return index < list.size(); }
public T next() { return list.get(index++); }
}
// 抽象聚合
public interface Aggregate<T> {
Iterator<T> createIterator();
}
19.3 迭代器模式应用实例
- 集合框架遍历
- 数据库结果集遍历
- 文件系统目录遍历
19.4 使用内部类实现迭代器
Java中常用内部类实现迭代器,可以访问外部类的私有成员,同时对外隐藏实现细节。
19.5 Java内置迭代器
// java.util.Iterator + java.lang.Iterable
List<String> list = Arrays.asList("A", "B", "C");
// 方式一:Iterator
Iterator<String> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
// 方式二:for-each(语法糖,底层就是Iterator)
for (String s : list) {
System.out.println(s);
}
19.6 迭代器模式优缺点与适用环境
适用环境:需要为聚合对象提供多种遍历方式;需要为不同的聚合结构提供统一的遍历接口。
第20章 中介者模式
20.1 中介者模式概述
中介者模式(Mediator Pattern)用一个中介对象来封装一系列对象的交互,使各对象不需要显式地相互引用,从而松耦合。
💡 类比:机场调度塔(飞机之间不直接通信,都通过塔台协调)
20.2 中介者模式结构与实现
// 抽象中介者
public abstract class Mediator {
public abstract void send(String message, Colleague colleague);
}
// 具体中介者(聊天室)
public class ChatRoom extends Mediator {
private List<Colleague> colleagues = new ArrayList<>();
public void register(Colleague c) { colleagues.add(c); }
@Override
public void send(String message, Colleague from) {
for (Colleague c : colleagues) {
if (c != from) {
c.receive(message);
}
}
}
}
// 同事类
public class User extends Colleague {
private String name;
public User(String name, Mediator mediator) {
super(mediator);
this.name = name;
}
public void send(String msg) {
System.out.println(name + " 发送:" + msg);
mediator.send(msg, this);
}
public void receive(String msg) {
System.out.println(name + " 收到:" + msg);
}
}
20.3 中介者模式应用实例
- 聊天室
- MVC中的Controller
- 机场调度塔
- GUI中对话框协调多个控件
20.4 扩展中介者与同事类
通过抽象中介者和抽象同事类,支持新增交互方式而无需修改已有代码。
20.5 中介者模式优缺点与适用环境
| 降低同事类间的耦合 | 中介者类可能变得非常复杂 |
| 交互逻辑集中管理 | 新增同事类需修改中介者 |
| 减少子类生成 | 过度集中可能成为瓶颈 |
第21章 备忘录模式
21.1 备忘录模式概述
备忘录模式(Memento Pattern)在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后恢复。
21.2 备忘录模式结构与实现
// 备忘录(存储状态)
public class Memento {
private final String state;
public Memento(String state) { this.state = state; }
public String getState() { return state; }
}
// 原发器(需要保存状态的对象)
public class Originator {
private String state;
public void setState(String state) { this.state = state; }
public String getState() { return state; }
public Memento createMemento() { return new Memento(state); }
public void restoreMemento(Memento m) { this.state = m.getState(); }
}
// 管理者(保存备忘录历史)
public class Caretaker {
private Stack<Memento> history = new Stack<>();
public void save(Memento m) { history.push(m); }
public Memento undo() { return history.pop(); }
}
// 使用
Originator editor = new Originator();
Caretaker caretaker = new Caretaker();
editor.setState("版本1");
caretaker.save(editor.createMemento());
editor.setState("版本2");
caretaker.save(editor.createMemento());
editor.setState("版本3");
editor.restoreMemento(caretaker.undo()); // 恢复到版本2
21.3 备忘录模式应用实例
- 文本编辑器的 Ctrl+Z
- 游戏存档/读档
- 浏览器后退
- 数据库事务回滚
21.4 实现多次撤销
使用栈保存多个备忘录,支持多级撤销和重做(双栈实现)。
21.5 备忘录模式优缺点与适用环境
适用环境:需要保存和恢复对象的状态;需要实现撤销操作;需要事务回滚。
第22章 观察者模式
22.1 观察者模式概述
观察者模式(Observer Pattern)定义对象间的一种一对多依赖关系,当一个对象状态改变时,所有依赖于它的对象都得到通知并自动更新。又称发布-订阅模式。
22.2 观察者模式结构与实现
// 抽象主题(被观察者)
public interface Subject {
void register(Observer o);
void unregister(Observer o);
void notifyObservers();
}
// 抽象观察者
public interface Observer {
void update(String message);
}
// 具体主题
public class NewsAgency implements Subject {
private List<Observer> observers = new ArrayList<>();
private String news;
public void setNews(String news) {
this.news = news;
notifyObservers(); // 状态变化,自动通知
}
@Override
public void register(Observer o) { observers.add(o); }
@Override
public void unregister(Observer o) { observers.remove(o); }
@Override
public void notifyObservers() {
observers.forEach(o -> o.update(news));
}
}
// 具体观察者
public class NewsChannel implements Observer {
private String name;
public NewsChannel(String name) { this.name = name; }
@Override
public void update(String message) {
System.out.println("[" + name + "] 收到新闻:" + message);
}
}
22.3 观察者模式应用实例
- 事件监听机制
- 消息队列(Kafka、RabbitMQ)
- 社交媒体的关注/粉丝
- 股票价格通知
22.4 JDK对观察者模式的支持
- java.util.Observable / java.util.Observer(Java 9已废弃)
- Java 9+ 推荐使用 java.util.concurrent.Flow(Publisher / Subscriber)
- PropertyChangeListener / PropertyChangeSupport
22.5 观察者模式与Java事件处理
AWT/Swing的事件委托模型就是观察者模式:
事件源(Button) → 事件对象(ActionEvent) → 事件监听器(ActionListener)
22.6 观察者模式与MVC
在MVC架构中:
- Model = 被观察者
- View = 观察者
- Model变化时View自动更新,实现数据与展示的解耦
22.7 观察者模式优缺点与适用环境
| 主题与观察者松耦合 | 观察者过多时通知开销大 |
| 支持广播通信 | 可能引起循环依赖 |
| 符合OCP | 通知顺序不可控 |
第23章 状态模式
23.1 状态模式概述
状态模式(State Pattern)允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
23.2 状态模式结构与实现
// 抽象状态
public abstract class TrafficState {
protected TrafficLight context;
public abstract void handle();
}
// 具体状态
public class GreenState extends TrafficState {
@Override
public void handle() {
System.out.println("🟢 绿灯:通行");
context.setState(new YellowState());
}
}
public class YellowState extends TrafficState {
@Override
public void handle() {
System.out.println("🟡 黄灯:减速");
context.setState(new RedState());
}
}
public class RedState extends TrafficState {
@Override
public void handle() {
System.out.println("🔴 红灯:停止");
context.setState(new GreenState());
}
}
// 环境类(红绿灯)
public class TrafficLight {
private TrafficState state;
public TrafficLight() { this.state = new RedState(); }
public void setState(TrafficState state) {
this.state = state;
state.context = this;
}
public void change() { state.handle(); }
}
23.3 状态模式应用实例
- TCP连接状态(ESTABLISHED、LISTEN、CLOSED)
- 线程状态(NEW、RUNNABLE、BLOCKED、TERMINATED)
- 订单状态流转(待付款→已付款→已发货→已完成)
- 电梯运行状态
23.4 共享状态
如果状态对象没有内部状态(无状态),可以被多个Context共享(类似享元模式)。
23.5 使用环境类实现状态转换
状态转换逻辑可以:
- 放在状态类中(分散,各状态自治)
- 集中在环境类中(集中,易于管理)
23.6 状态模式优缺点与适用环境
适用环境:对象行为取决于状态,运行时根据状态改变行为;代码中包含大量与状态有关的条件语句(if-else/switch)。
第24章 策略模式
24.1 策略模式概述
策略模式(Strategy Pattern)定义一系列算法,将每一个算法封装起来,并让它们可以相互替换。让算法的变化独立于使用算法的客户。
24.2 策略模式结构与实现
// 抽象策略
public interface PayStrategy {
void pay(double amount);
}
// 具体策略
public class AliPayStrategy implements PayStrategy {
@Override
public void pay(double amount) {
System.out.println("支付宝支付:" + amount + "元");
}
}
public class WechatPayStrategy implements PayStrategy {
@Override
public void pay(double amount) {
System.out.println("微信支付:" + amount + "元");
}
}
public class CreditCardStrategy implements PayStrategy {
@Override
public void pay(double amount) {
System.out.println("信用卡支付:" + amount + "元");
}
}
// 环境类(支付上下文)
public class PayContext {
private PayStrategy strategy;
public PayContext(PayStrategy strategy) { this.strategy = strategy; }
public void executePay(double amount) {
strategy.pay(amount);
}
}
// 客户端
PayContext ctx = new PayContext(new AliPayStrategy());
ctx.executePay(99.9); // 支付宝支付:99.9元
24.3 策略模式应用实例
- 支付方式选择
- 促销折扣计算(满减、打折、返现)
- 数据压缩/加密算法选择
- 排序算法选择
24.4 Java SE中的布局管理
java.awt.LayoutManager 就是策略模式的经典应用:
frame.setLayout(new FlowLayout()); // 流式布局
frame.setLayout(new BorderLayout()); // 边界布局
frame.setLayout(new GridLayout(3,3)); // 网格布局
24.5 策略模式优缺点与适用环境
| 算法可自由切换 | 客户端需了解所有策略 |
| 避免多重条件判断 | 策略类数量增多 |
| 扩展性好,符合OCP | 所有策略类都需对外暴露 |
💡 实际开发中常结合工厂模式或枚举来选择策略,避免客户端直接依赖具体策略。
第25章 模板方法模式
25.1 模板方法模式概述
模板方法模式(Template Method Pattern)定义一个操作中算法的骨架,将一些步骤延迟到子类中。使得子类可以不改变算法结构即可重定义某些特定步骤。
💡 这是最简单的设计模式之一,也是使用最广泛的模式之一。
25.2 模板方法模式结构与实现
// 抽象类:定义模板方法
public abstract class DataProcessor {
// 模板方法(final防止子类覆盖)
public final void process() {
readData();
processData();
if (needLog()) { // 钩子方法控制流程
writeLog();
}
writeData();
}
// 抽象步骤:子类必须实现
protected abstract void readData();
protected abstract void processData();
// 公共步骤:子类可直接使用
protected void writeData() {
System.out.println("📝 写入数据");
}
// 钩子方法:子类可选择性覆盖
protected boolean needLog() { return true; }
protected void writeLog() {
System.out.println("📋 记录日志");
}
}
// 具体子类:CSV处理器
public class CSVProcessor extends DataProcessor {
@Override
protected void readData() {
System.out.println("📂 读取CSV文件");
}
@Override
protected void processData() {
System.out.println("⚙️ 解析CSV数据");
}
}
// 具体子类:数据库处理器(不需要日志)
public class DBProcessor extends DataProcessor {
@Override
protected void readData() {
System.out.println("🗄️ 从数据库读取");
}
@Override
protected void processData() {
System.out.println("⚙️ 数据清洗转换");
}
@Override
protected boolean needLog() { return false; } // 覆盖钩子
}
25.3 模板方法模式应用实例
- JUnit的 setUp() / tearDown() / runTest()
- Spring的 JdbcTemplate、RestTemplate
- Servlet的 HttpServlet(doGet、doPost)
- AbstractList、AbstractMap
25.4 钩子方法的使用
钩子方法(Hook Method)提供默认实现,子类可以选择性覆盖,用于:
- 控制模板方法的执行流程
- 提供可选的扩展点
- 实现"好莱坞原则"(Don’t call us, we’ll call you)
25.5 模板方法模式优缺点与适用环境
| 封装不变部分,扩展可变部分 | 每个不同实现都需要一个子类 |
| 提取公共代码,便于维护 | 继承带来强耦合 |
| 行为由父类控制,子类实现 | 阅读代码需要跳转(反直觉) |
第26章 访问者模式
26.1 访问者模式概述
访问者模式(Visitor Pattern)表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。
⚠️ 这是GoF 23种模式中最复杂、使用最少的模式。
26.2 访问者模式结构与实现
// 抽象元素
public interface Element {
void accept(Visitor visitor); // 双分派的关键
}
// 具体元素
public class Book implements Element {
private String name;
private double price;
public Book(String name, double price) {
this.name = name;
this.price = price;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this); // 将自身传给访问者
}
// getters…
}
public class Fruit implements Element {
private String name;
private double weight;
public Fruit(String name, double weight) {
this.name = name;
this.weight = weight;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 抽象访问者
public interface Visitor {
void visit(Book book);
void visit(Fruit fruit);
}
// 具体访问者:计算总价
public class TotalPriceVisitor implements Visitor {
private double total = 0;
@Override
public void visit(Book book) {
total += book.getPrice();
}
@Override
public void visit(Fruit fruit) {
total += fruit.getWeight() * 10; // 假设10元/kg
}
public double getTotal() { return total; }
}
// 对象结构(购物车)
public class ShoppingCart {
private List<Element> items = new ArrayList<>();
public void add(Element e) { items.add(e); }
public void accept(Visitor visitor) {
for (Element item : items) {
item.accept(visitor);
}
}
}
26.3 访问者模式应用实例
- 编译器对AST的多种操作(类型检查、代码生成、优化)
- 购物车结算(价格计算、重量统计、折扣计算)
- 文档结构的多格式导出(PDF、HTML、Markdown)
26.4 访问者模式与组合模式联用
访问者模式常与组合模式配合,遍历树形结构并对每个节点执行不同操作。
26.5 访问者模式优缺点与适用环境
| 增加新操作很容易(新增Visitor) | 增加新元素很困难(需修改所有Visitor) |
| 将相关操作集中到一个访问者中 | 破坏封装,需暴露元素内部状态 |
| 可以跨层次访问不同对象 | 使用场景少,学习成本高 |
适用环境:对象结构中的元素类很少改变,但经常需要在此结构上定义新的操作。
附录:UML类图
A.1 UML概述
UML(Unified Modeling Language)是面向对象建模的标准语言,设计模式的描述大量使用UML类图。
A.2 类与类的UML表示
┌─────────────────────┐
│ 类名(抽象用斜体) │ ← 第一层:类名
├─────────────────────┤
│ – name: String │ ← 第二层:属性
│ + age: int │
├─────────────────────┤
│ + getName(): String│ ← 第三层:方法
│ + setName(n): void │
└─────────────────────┘
访问修饰符:+ public – private # protected ~ package
A.3 类之间的关系
| 关联 | ——→ | 成员变量引用 | 对象间的长期引用关系 |
| 聚合 | ◇—— | 成员变量(可独立存在) | 整体与部分(弱拥有) |
| 组合 | ◆—— | 成员变量(不可独立) | 整体与部分(强拥有) |
| 继承 | ——▷ | extends | 泛化关系(is-a) |
| 实现 | –▷ | implements | 类实现接口 |
| 依赖 | –→ | 方法参数/局部变量 | 临时使用关系 |
💡 记忆口诀:依赖最弱,关联次之,聚合组合更强,继承实现最强。
全文总结
🗺️ 设计模式全景图
设计模式(23+1种)
│
├── 创建型(怎么造)
│ ├── 简单工厂 ── 一个工厂造所有
│ ├── 工厂方法 ── 一个工厂造一种
│ ├── 抽象工厂 ── 一个工厂造一族
│ ├── 建造者 ── 分步骤造复杂对象
│ ├── 原型 ── 克隆已有对象
│ └── 单例 ── 全局只有一个
│
├── 结构型(怎么组)
│ ├── 适配器 ── 接口转换
│ ├── 桥接 ── 多维度分离
│ ├── 组合 ── 树形结构
│ ├── 装饰 ── 动态增强
│ ├── 外观 ── 统一入口
│ ├── 享元 ── 共享复用
│ └── 代理 ── 控制访问
│
└── 行为型(怎么动)
├── 职责链 ── 链式处理
├── 命令 ── 请求对象化
├── 解释器 ── 文法解析
├── 迭代器 ── 统一遍历
├── 中介者 ── 集中协调
├── 备忘录 ── 状态快照
├── 观察者 ── 发布订阅
├── 状态 ── 状态驱动行为
├── 策略 ── 算法可替换
├── 模板方法 ── 固定骨架
└── 访问者 ── 操作与结构分离
📌 学习建议
🔥 高频面试模式 TOP 5
| 1 | 单例 | 线程安全、DCL、枚举实现 |
| 2 | 工厂(三兄弟) | 区别、OCP、Spring中的应用 |
| 3 | 代理 | 静态vs动态、JDK vs CGLIB、AOP |
| 4 | 观察者 | 事件机制、消息队列、MVC |
| 5 | 策略 | 消除if-else、Spring中的应用 |
💡 一句话总结:设计模式不是银弹,而是前人踩坑后总结的经验地图。读懂地图,才能少走弯路。
如果这篇文章对你有帮助,请 点赞 👍 收藏 ⭐ 关注 三连支持!你的鼓励是我持续输出的最大动力!
📚 参考书籍:《Java设计模式》(刘伟 著,清华大学出版社)
📅 更新时间:2026年8月
网硕互联帮助中心




评论前必须登录!
注册