设计模式是软件工程领域的经验结晶,掌握设计模式不仅是技术的提升,更是架构思维的飞跃。本文将系统性地解析Java 23种设计模式,从理论基础到实战应用,助你构建优雅、可维护的代码体系。
文章目录
- 设计模式分类与概述
- 创建型设计模式详细解析
- 结构型设计模式详细解析
-
- [6. 适配器模式](#6. 适配器模式)
- [7. 桥接模式](#7. 桥接模式)
- [8. 组合模式](#8. 组合模式)
- [9. 装饰器模式](#9. 装饰器模式)
- [10. 外观模式](#10. 外观模式)
- [11. 享元模式](#11. 享元模式)
- [12. 代理模式](#12. 代理模式)
- 行为型设计模式详细解析
-
- [13. 责任链模式](#13. 责任链模式)
- [14. 命令模式](#14. 命令模式)
- [15. 解释器模式](#15. 解释器模式)
- [16. 迭代器模式](#16. 迭代器模式)
- [17. 中介者模式](#17. 中介者模式)
- [18. 备忘录模式](#18. 备忘录模式)
- [19. 观察者模式](#19. 观察者模式)
- [20. 状态模式](#20. 状态模式)
- [21. 策略模式](#21. 策略模式)
- [22. 模板方法模式](#22. 模板方法模式)
- [23. 访问者模式](#23. 访问者模式)
- 实际项目应用指南
- 模式选择决策框架
- 总结
设计模式分类与概述
设计模式根据其解决问题的不同类型,可分为三大类别:创建型 、结构型 和行为型。每类模式都有其独特的核心目标和典型应用场景。
创建型设计模式概述
核心目标:封装对象创建过程,实现对象实例化与使用逻辑的解耦。
创建型设计模式通过将对象实例化逻辑与使用逻辑解耦,提升系统的灵活性和可维护性。这种解耦机制使代码在面对需求变化时,能通过调整创建逻辑而非修改使用代码来适应变化,符合开闭原则。
五种核心创建型模式及其定位:
| 模式名称 | 核心定位 | 典型应用场景 |
|---|---|---|
| 单例模式 | 确保类在系统中仅存在唯一实例 | 全局配置管理、日志对象、数据库连接池 |
| 工厂方法模式 | 定义创建对象的接口,由子类决定具体实现 | 数据库驱动选择、日志框架 |
| 抽象工厂模式 | 提供接口创建一系列相关对象 | 跨平台UI组件库、产品族创建 |
| 建造者模式 | 分离复杂对象的构建与表示 | 多参数POJO构建、文档生成器 |
| 原型模式 | 通过复制现有实例创建新对象 | 对象池化、复杂对象的快速复制 |
结构型设计模式概述
核心目标:优化类与对象组合关系,通过灵活组合方式实现功能复用与系统解耦。
结构型设计模式包含七种核心模式,每种模式针对特定的结构优化场景:
- 适配器模式:解决接口不兼容问题
- 装饰器模式:实现对象功能的动态扩展
- 代理模式:控制对象访问,实现功能增强
- 组合模式:构建树形结构,统一处理单个与组合对象
- 桥接模式:分离抽象与实现,实现多维度独立扩展
- 外观模式:简化复杂系统交互,提供统一入口
- 享元模式:实现对象复用,降低内存占用
典型应用:
- Spring AOP框架通过代理模式实现横切关注点分离
- UI组件库利用组合模式构建复杂界面树形结构
行为型设计模式概述
核心目标:规范对象间交互逻辑与职责分配,通过封装行为变化提升系统可维护性。
行为型设计模式涵盖11种经典模式,专注于解决复杂业务场景下的对象协作问题。
典型应用场景:
- 业务流程:责任链模式实现请求的分级处理
- 状态管理:状态模式处理对象生命周期的状态转换
- 事件驱动:观察者模式构建松耦合的通知机制
创建型设计模式详细解析
1. 单例模式
核心特点
- 控制实例唯一性,确保类在系统中只存在一个实例
- 支持延迟初始化或饿汉式初始化
- 提供全局访问点,便于系统统一调用
UML类图描述
Singleton
├── - instance: Singleton (静态私有)
├── - Singleton() (私有构造)
└── + getInstance(): Singleton (静态公有方法)
适用场景
- 资源共享场景:数据库连接池、线程池
- 配置管理场景:系统参数配置类、环境配置中心
- 日志管理场景:日志记录器、异常追踪器
优缺点分析
| 优势 | 劣势 |
|---|---|
| 节省系统资源,避免重复创建 | 扩展性差,难以继承和修改 |
| 避免多实例导致的冲突问题 | 全局状态导致单元测试困难 |
| 提供全局统一的访问入口 | 违反单一职责原则 |
实际项目应用指南
选择依据:
- 需要全局唯一实例的场景
- 需要频繁创建和销毁的对象
- 资源密集型对象(如数据库连接)
使用技巧:
java
// 推荐:枚举式单例(线程安全,防反射攻击)
public enum Singleton {
INSTANCE;
public void doSomething() {
// 业务逻辑
}
}
// 双重检查锁定(DCL)
public class Singleton {
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;
}
}
常见误区:
- ❌ 忽略多线程环境下的线程安全问题
- ❌ 序列化破坏单例(需重写
readResolve()方法) - ❌ 反射攻击未防护(在构造器中添加校验)
2. 工厂方法模式
核心特点
- 定义创建对象的接口,由子类决定实例化类型
- 产品创建与使用解耦,符合开闭原则
- 工厂接口标准化产品创建流程
UML类图描述
┌─────────────────┐
│ ProductFactory │ (抽象工厂)
└────────┬────────┘
│ factoryMethod()
↓
┌───────────────────────────┐
│ │
┌─────┴──────┐ ┌────────┴────────┐
│ Concrete │ │ Concrete │
│Factory1 │ │Factory2 │
└─────┬──────┘ └────────┬────────┘
│ │
↓ ↓
┌─────────┐ ┌─────────┐
│Product1 │ │Product2 │
└─────────┘ └─────────┘
适用场景
- 产品种类稳定但具体实现多变
- 框架设计需要支持扩展
- 数据库驱动、日志输出等多类型产品
优缺点分析
| 优势 | 劣势 |
|---|---|
| 符合开闭原则,扩展新产品无需修改代码 | 每新增一种产品需对应新增工厂类 |
| 产品创建与使用解耦 | 系统类数量增加,复杂度上升 |
- 工厂类职责单一,便于维护 | 过度抽象可能导致设计复杂化 |
实际项目应用指南
选择依据:
- 需要动态决定创建哪种产品
- 产品类型可能频繁扩展
- 框架设计需要灵活的创建机制
使用技巧:
java
// 抽象产品
public interface Logger {
void log(String message);
}
// 具体产品
public class ConsoleLogger implements Logger {
public void log(String message) {
System.out.println("[Console] " + message);
}
}
public class FileLogger implements Logger {
public void log(String message) {
// 写入文件
}
}
// 抽象工厂
public interface LoggerFactory {
Logger createLogger();
}
// 具体工厂
public class ConsoleLoggerFactory implements LoggerFactory {
public Logger createLogger() {
return new ConsoleLogger();
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
LoggerFactory factory = new ConsoleLoggerFactory();
Logger logger = factory.createLogger();
logger.log("Hello World!");
}
}
常见误区:
- ❌ 工厂类职责过重,添加业务逻辑
- ❌ 简单场景过度抽象
- ❌ 未使用依赖注入管理工厂生命周期
3. 抽象工厂模式
核心特点
- 创建产品族,确保产品族内的一致性
- 接口隔离,分离不同产品族的创建逻辑
- 支持多产品族的协同工作
UML类图描述
┌─────────────────┐
│ AbstractFactory │
└────────┬────────┘
│
┌───────────┼───────────┐
↓ ↓ ↓
┌──────────┐ ┌──────────┐ ┌──────────┐
│createA() │ │createB() │ │createC() │
└──────────┘ └──────────┘ └──────────┘
适用场景
- 需要多个产品族且族内产品需协同工作
- 跨平台UI组件库(Windows/macOS风格)
- 多数据库访问层设计
优缺点分析
| 优势 | 劣势 |
|---|---|
| 确保产品族内的一致性 | 新增产品族需修改抽象工厂接口 |
| 隔离具体实现细节 | 系统抽象度高,理解难度大 |
| 支持产品族的整体切换 | 扩展困难,违反开闭原则 |
实际项目应用指南
选择依据:
- 产品数量多且分成多个族
- 需要保证同族产品的一致性
- 系统需要支持多套产品体系
使用技巧:
java
// 抽象产品族
public interface Button {
void render();
}
public interface TextBox {
void render();
}
// Windows产品族
public class WindowsButton implements Button {
public void render() {
System.out.println("渲染Windows风格按钮");
}
}
public class WindowsTextBox implements TextBox {
public void render() {
System.out.println("渲染Windows风格文本框");
}
}
// macOS产品族
public class MacButton implements Button {
public void render() {
System.out.println("渲染Mac风格按钮");
}
}
// 抽象工厂
public interface GUIFactory {
Button createButton();
TextBox createTextBox();
}
// Windows工厂
public class WindowsFactory implements GUIFactory {
public Button createButton() {
return new WindowsButton();
}
public TextBox createTextBox() {
return new WindowsTextBox();
}
}
常见误区:
- ❌ 产品族结构不稳定时使用
- ❌ 单一产品场景误用抽象工厂
- ❌ 工厂接口过于庞大
4. 建造者模式
核心特点
- 分步构建复杂对象,将创建过程分解为独立步骤
- 隔离构建过程与表示,相同流程可生成不同产品
- 指挥者控制构建流程,统一调度建造步骤
UML类图描述
┌───────────┐
│ Director │ (指挥者)
└─────┬─────┘
│
┌─────┴─────┐
│ Builder │ (抽象建造者)
└─────┬─────┘
│
┌─────┴──────────┐
↓ ↓
┌─────────┐ ┌─────────┐
│Concrete │ │ Product │
│Builder │ │ │
└─────────┘ └─────────┘
适用场景
- 对象属性多且构建逻辑复杂
- 多参数POJO构建,避免构造器膨胀
- 文档生成器(HTML/Markdown文档分步构建)
优缺点分析
| 优势 | 劣势 |
|---|---|
| 构建过程清晰可控 | 类数量增加,需定义多个建造者 |
- 灵活切换产品表示 | 构建逻辑分散,步骤分布在不同类中 |
| 支持构建步骤的灵活组合 | 适用场景相对有限 |
实际项目应用指南
选择依据:
- 对象有多个可选参数
- 创建过程复杂,需要分步骤
- 不同构建过程需要创建不同表示
使用技巧:
java
// 产品类
public class Computer {
private String cpu;
private String ram;
private String storage;
private Computer(Builder builder) {
this.cpu = builder.cpu;
this.ram = builder.ram;
this.storage = builder.storage;
}
public static class Builder {
private String cpu = "Intel i5";
private String ram = "8GB";
private String storage = "256GB SSD";
public Builder cpu(String cpu) {
this.cpu = cpu;
return this;
}
public Builder ram(String ram) {
this.ram = ram;
return this;
}
public Builder storage(String storage) {
this.storage = storage;
return this;
}
public Computer build() {
return new Computer(this);
}
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
Computer computer = new Computer.Builder()
.cpu("Intel i7")
.ram("16GB")
.storage("1TB SSD")
.build();
}
}
常见误区:
- ❌ 与工厂模式混淆(建造者聚焦分步构建,工厂侧重类型选择)
- ❌ 简单对象滥用建造者模式
- ❌ 建造者类包含业务逻辑
5. 原型模式
核心特点
- 避免直接使用构造函数,通过克隆方法生成对象副本
- 支持深拷贝与浅拷贝的灵活控制
- 高效创建对象,避免重复初始化开销
UML类图描述
┌─────────────┐
│ Prototype │ (抽象原型)
├─────────────┤
│ + clone() │
└─────┬───────┘
│
┌────┴────┐
↓ ↓
┌─────────┐ ┌─────────┐
│Concrete │ │Concrete │
│Prototype│ │Prototype│
└─────────┘ └─────────┘
适用场景
- 对象创建成本高(数据库连接、复杂初始化)
- 需要动态生成大量相似对象(游戏角色、对象池管理)
- 需保留对象状态快照进行恢复操作
优缺点分析
| 优势 | 劣势 |
|---|---|
| 性能优化,避免重复初始化开销 | 深拷贝实现复杂,需处理嵌套对象引用 |
| 简化创建过程,无需关注构造细节 | 可能违反单一职责原则 |
| 支持对象状态的保存与恢复 | Cloneable接口仅作为标记,无实际方法 |
实际项目应用指南
选择依据:
- 对象初始化耗时较长
- 需要大量相似对象
- 需要保存对象状态快照
使用技巧:
java
// 实现Cloneable接口
public class Sheep implements Cloneable {
private String name;
private int age;
public Sheep(String name, int age) {
this.name = name;
this.age = age;
}
// 浅拷贝
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
// 深拷贝实现
@Override
protected Object deepClone() throws CloneNotSupportedException {
Sheep sheep = (Sheep) super.clone();
// 处理引用类型的深拷贝
return sheep;
}
}
// 客户端使用
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Sheep original = new Sheep("多利", 2);
Sheep cloned = (Sheep) original.clone();
}
}
常见误区:
- ❌ 未区分浅拷贝与深拷贝
- ❌ 忽略嵌套对象的引用问题
- ❌ 序列化方式实现深拷贝时忘记实现Serializable
结构型设计模式详细解析
6. 适配器模式
核心特点
- 转换接口,解决接口不兼容问题
- 兼容性处理,使现有类能够复用
- 透明复用适配者功能
UML类图描述
┌───────────┐ ┌─────────────┐
│ Client │─────────►│ Target │
└───────────┘ └──────┬──────┘
│
┌───────┴────────┐
│ Adapter │
└───────┬────────┘
│
┌───────┴────────┐
│ Adaptee │
│ (现有不兼容类) │
└────────────────┘
适用场景
- 接口不兼容的系统集成
- 第三方库适配(将外部API转换为系统内部接口)
- 旧系统升级(保留legacy代码功能对接新接口)
优缺点分析
| 优势 | 劣势 |
|---|---|
| 实现现有代码复用 | 增加系统复杂度 |
| 促进系统解耦 | 过多适配器导致维护困难 |
| 灵活性好,可以透明复用 | 需要额外的适配器类 |
实际项目应用指南
选择依据:
- 需要使用现有类但其接口不满足需求
- 两个类所做的事情相同但接口不同
- 需要集成第三方库或遗留系统
使用技巧:
java
// 目标接口
public interface MediaPlayer {
void play(String audioType, String fileName);
}
// 被适配的类(现有不兼容接口)
public class AdvancedMediaPlayer {
public void playVlc(String fileName) {
System.out.println("播放VLC文件: " + fileName);
}
public void playMp4(String fileName) {
System.out.println("播放MP4文件: " + fileName);
}
}
// 适配器类
public class MediaAdapter implements MediaPlayer {
private AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter() {
advancedMusicPlayer = new AdvancedMediaPlayer();
}
@Override
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("vlc")) {
advancedMusicPlayer.playVlc(fileName);
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedMusicPlayer.playMp4(fileName);
}
}
}
常见误区:
- ❌ 类适配器与对象适配器混淆(推荐使用对象适配器降低耦合)
- ❌ 适配器职责过重,承担过多业务逻辑
- ❌ 过度使用适配器导致系统复杂度上升
7. 桥接模式
核心特点
- 分离抽象与实现,通过组合而非继承
- 独立扩展两个维度
- 符合开闭原则,可独立扩展任一维度
UML类图描述
┌──────────┐
│ Abstraction│ (抽象化)
└────┬─────┘
│ implements
┌────┴─────┐
↓ │
┌───────────┐ │
│ Refined │────┼────► ┌─────────────┐
│Abstraction│ │ │Implementor │
└───────────┘ │ └──────┬──────┘
│ │
┌─────────┴────────┐ ↓
↓ ↓┌────────────┐
┌──────────┐ ┌──────────┤Concrete │
│Concrete │ │Concrete │Implementor│
│Implemento│ │Abstraction│ │
└──────────┘ └──────────┘└────────────┘
适用场景
- 多维度变化且需独立扩展
- 图形界面中形状与颜色的组合
- 消息系统中消息类型与传输方式的搭配
优缺点分析
| 优势 | 劣势 |
|---|---|
| 扩展性好,独立扩展两个维度 | 系统抽象度高,理解难度大 |
| 符合开闭原则 | 需要正确识别抽象和实现维度 |
| 减少子类数量 | 设计复杂度增加 |
实际项目应用指南
选择依据:
- 存在两个或多个独立变化的维度
- 需要独立扩展抽象和实现
- 避免继承导致的类爆炸
使用技巧:
java
// 实现接口(颜色维度)
public interface Color {
void applyColor();
}
public class Red implements Color {
public void applyColor() {
System.out.println("应用红色");
}
}
public class Blue implements Color {
public void applyColor() {
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 {
public Circle(Color color) {
super(color);
}
@Override
public void draw() {
System.out.print("圆形 - ");
color.applyColor();
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
Shape redCircle = new Circle(new Red());
redCircle.draw(); // 输出:圆形 - 应用红色
}
}
常见误区:
- ❌ 单一维度变化场景使用
- ❌ 抽象层与实现层未真正解耦
- ❌ 未能正确识别独立变化的维度
8. 组合模式
核心特点
- 树形结构表示部分-整体关系
- 统一对待单个对象与组合对象
- 支持递归组合操作
UML类图描述
┌─────────────┐
│ Component │ (抽象构件)
├─────────────┤
│ + operation()│
│ + add() │
│ + remove() │
└─────┬───────┘
│
┌──────┴──────┐
↓ ↓
┌─────────┐ ┌──────────┐
│ Leaf │ │Composite │ (容器构件)
└─────────┘ ├──────────┤
│ + add() │
│ + remove()│
└────┬─────┘
│
└────► Component
适用场景
- 树形结构数据(文件系统、UI组件树)
- 组织架构(部门与员工)
- 需要统一处理单个对象与组合对象
优缺点分析
| 优势 | 劣势 |
|---|---|
| 简化客户端代码,无需区分处理 | 设计复杂度较高 |
| 易于扩展新构件类型 | 限制容器中的构件类型时需额外判断 |
| 支持递归操作,层次清晰 | 过度通用化可能违背单一职责 |
实际项目应用指南
选择依据:
- 存在树形结构或部分-整体层次关系
- 需要统一处理单个对象和组合对象
- 客户端代码需要忽略组合对象与单个对象的差异
使用技巧:
java
// 抽象构件
public abstract class FileSystemComponent {
protected String name;
public FileSystemComponent(String name) {
this.name = name;
}
public abstract void display();
}
// 叶子构件(文件)
public class File extends FileSystemComponent {
public File(String name) {
super(name);
}
@Override
public void display() {
System.out.println("文件: " + name);
}
}
// 容器构件(文件夹)
public class Folder extends FileSystemComponent {
private List<FileSystemComponent> children = new ArrayList<>();
public Folder(String name) {
super(name);
}
public void add(FileSystemComponent component) {
children.add(component);
}
public void remove(FileSystemComponent component) {
children.remove(component);
}
@Override
public void display() {
System.out.println("文件夹: " + name);
for (FileSystemComponent component : children) {
component.display();
}
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
Folder root = new Folder("根目录");
File file1 = new File("文件1.txt");
File file2 = new File("文件2.txt");
root.add(file1);
root.add(file2);
root.display();
}
}
常见误区:
- ❌ 叶子节点实现add/remove方法(应抛出UnsupportedOperationException)
- ❌ 树形结构过深导致性能问题
- ❌ 未选择合适的组合模式(透明组合 vs 安全组合)
9. 装饰器模式
核心特点
- 动态扩展对象功能,无需修改原有类
- 通过组合替代继承,避免类爆炸
- 透明包裹对象,对客户端透明
UML类图描述
┌─────────────┐
│ Component │ (抽象构件)
├─────────────┤
│ + operation()│
└─────┬───────┘
│
┌──────┴──────┐
↓ ↓
┌─────────┐ ┌──────────────┐
│Concrete │ │Decorator │
│Component│ └──────┬───────┘
└─────────┘ │
┌───────┴───────┐
↓ ↓
┌────────────┐ ┌────────────┐
│ Concrete │ │ Concrete │
│ Decorator │ │ Decorator │
└────────────┘ └────────────┘
适用场景
- I/O流包装、日志功能增强
- 权限控制、缓存管理
- 需动态添加或移除功能的场景
优缺点分析
| 优势 | 劣势 |
|---|---|
| 功能扩展灵活,运行时动态添加 | 多层装饰导致调试困难 |
| 避免类爆炸,通过组合替代继承 | 增加代码复杂度与理解成本 |
| 符合开闭原则 | 装饰顺序影响功能执行 |
实际项目应用指南
选择依据:
- 需要动态扩展对象功能
- 不能使用继承来扩展功能
- 需要为对象添加多个可选功能
使用技巧:
java
// 抽象构件
public interface Coffee {
double cost();
String description();
}
// 具体构件
public class SimpleCoffee implements Coffee {
public double cost() {
return 1.0;
}
public String description() {
return "简单咖啡";
}
}
// 抽象装饰器
public abstract class CoffeeDecorator implements Coffee {
protected Coffee decoratedCoffee;
public CoffeeDecorator(Coffee coffee) {
this.decoratedCoffee = coffee;
}
public double cost() {
return decoratedCoffee.cost();
}
public String description() {
return decoratedCoffee.description();
}
}
// 具体装饰器
public class MilkDecorator extends CoffeeDecorator {
public MilkDecorator(Coffee coffee) {
super(coffee);
}
@Override
public double cost() {
return super.cost() + 0.5;
}
@Override
public String description() {
return super.description() + ", 加牛奶";
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
Coffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new MilkDecorator(coffee); // 加双倍牛奶
System.out.println(coffee.description());
System.out.println("价格: $" + coffee.cost());
}
}
常见误区:
- ❌ 与继承混淆(装饰器使用组合)
- ❌ 装饰链过长导致调试困难
- ❌ 装饰器添加新方法破坏透明性
10. 外观模式
核心特点
- 简化子系统接口,提供统一访问入口
- 封装子系统复杂性,隐藏内部实现
- 实现客户端与子系统解耦
UML类图描述
┌──────────┐
│ Client │
└────┬─────┘
│
┌────┴────────┐
│ Facade │ (外观)
└────┬────────┘
│
┌────┼────────┐
↓ ↓ ↓
┌─────┐┌────┐ ┌──────┐
│SubA ││SubB│ │ SubC │
└─────┘└────┘ └──────┘
适用场景
- 复杂系统的简化访问
- 第三方API封装(如支付SDK统一接口)
- 框架入口类(如Spring的ApplicationContext)
优缺点分析
| 优势 | 劣势 |
|---|---|
| 降低使用难度,简化客户端代码 | 外观类可能成为性能瓶颈 |
| 提高系统安全性,隐藏内部实现 | 过度封装会限制子系统灵活使用 |
| 降低客户端与子系统的耦合 | 外观类职责可能过重 |
实际项目应用指南
选择依据:
- 子系统复杂,客户端难以直接使用
- 需要简化接口,降低学习成本
- 需要隔离客户端与子系统的变化
使用技巧:
java
// 子系统A
public class SubsystemA {
public void operationA() {
System.out.println("子系统A的操作");
}
}
// 子系统B
public class SubsystemB {
public void operationB() {
System.out.println("子系统B的操作");
}
}
// 子系统C
public class SubsystemC {
public void operationC() {
System.out.println("子系统C的操作");
}
}
// 外观类
public class Facade {
private SubsystemA subsystemA;
private SubsystemB subsystemB;
private SubsystemC subsystemC;
public Facade() {
subsystemA = new SubsystemA();
subsystemB = new SubsystemB();
subsystemC = new SubsystemC();
}
public void operation() {
subsystemA.operationA();
subsystemB.operationB();
subsystemC.operationC();
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
Facade facade = new Facade();
facade.operation();
}
}
常见误区:
- ❌ 外观类添加业务逻辑(仅负责协调)
- ❌ 为满足特殊需求暴露子系统接口
- ❌ 过度使用外观模式导致设计复杂化
11. 享元模式
核心特点
- 共享细粒度对象,减少内存占用
- 分离内部状态与外部状态
- 通过享元工厂管理对象池
UML类图描述
┌─────────────┐
│ Flyweight │ (抽象享元)
├─────────────┤
│ + operation()│
└─────┬───────┘
│
┌─────┴────────┐
↓ ↓
┌─────────┐ ┌─────────────┐
│Concrete │ │Flyweight │
│Flyweight│ │Factory │
└─────────┘ └─────────────┘
适用场景
- 大量相似对象导致内存占用过高
- 文本编辑器的字符缓存
- 游戏中的角色模型复用、数据库连接池管理
优缺点分析
| 优势 | 劣势 |
|---|---|
| 显著节省内存空间 | 设计复杂度增加 |
| 提升系统性能 | 外部状态管理成本上升 |
| 支持对象复用 | 需要正确识别内外部状态 |
实际项目应用指南
选择依据:
- 系统中存在大量相似对象
- 对象的大部分状态可以外部化
- 需要减少内存占用
使用技巧:
java
// 享元接口
public interface Flyweight {
void operation(String extrinsicState);
}
// 具体享元
public class ConcreteFlyweight implements Flyweight {
private String intrinsicState; // 内部状态
public ConcreteFlyweight(String intrinsicState) {
this.intrinsicState = intrinsicState;
}
@Override
public void operation(String extrinsicState) {
System.out.println("内部状态: " + intrinsicState +
", 外部状态: " + extrinsicState);
}
}
// 享元工厂
public class FlyweightFactory {
private Map<String, Flyweight> flyweights = new HashMap<>();
public Flyweight getFlyweight(String key) {
Flyweight flyweight = flyweights.get(key);
if (flyweight == null) {
flyweight = new ConcreteFlyweight(key);
flyweights.put(key, flyweight);
}
return flyweight;
}
public int getFlyweightCount() {
return flyweights.size();
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();
Flyweight flyweight1 = factory.getFlyweight("共享状态A");
flyweight1.operation("外部状态1");
Flyweight flyweight2 = factory.getFlyweight("共享状态A");
flyweight2.operation("外部状态2");
System.out.println("享元对象数量: " + factory.getFlyweightCount());
}
}
常见误区:
- ❌ 外部状态存储在享元对象中
- ❌ 未正确识别内部状态与外部状态
- ❌ 忽略线程安全问题
12. 代理模式
核心特点
- 控制对象访问,作为中间层
- 附加功能扩展(日志、缓存、权限)
- 延迟加载机制
UML类图描述
┌────────────┐
│ Subject │ (抽象主题)
├────────────┤
│ + request()│
└─────┬──────┘
│
┌──────┴──────┐
↓ ↓
┌─────────┐ ┌─────────────┐
│ Real │ │Proxy │
│ Subject │ └──────┬──────┘
└─────────┘ │
│
↓
RealSubject
适用场景
- 访问控制(权限保护)
- 远程服务调用的网络代理
- 延迟加载(虚拟代理)
- 智能引用(引用计数、缓存)
优缺点分析
| 优势 | 劣势 |
|---|---|
| 职责分离清晰(代理控制逻辑,真实对象业务逻辑) | 代理层增加性能开销 |
| 功能增强灵活,可动态添加横切关注点 | 系统复杂度提升 |
| 保护真实对象,控制访问 | 过度代理增加调用链长度 |
实际项目应用指南
选择依据:
- 需要控制对对象的访问
- 需要在访问对象时添加额外功能
- 需要延迟加载对象
使用技巧:
java
// 抽象主题
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("权限检查");
System.out.println("日志记录");
if (realSubject == null) {
realSubject = new RealSubject();
}
// 调用真实对象
realSubject.request();
// 后置处理
System.out.println("缓存结果");
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
Subject proxy = new Proxy();
proxy.request();
}
}
常见误区:
- ❌ 与装饰器模式混淆(代理侧重控制访问,装饰器侧重功能增强)
- ❌ 过度设计代理层
- ❌ 未考虑代理的性能开销
行为型设计模式详细解析
13. 责任链模式
核心特点
- 请求沿链传递,每个处理者自主决定处理或转发
- 动态组合责任,灵活配置处理链
- 实现请求发送者与处理者的解耦
UML类图描述
┌──────────────┐
│ Handler │ (抽象处理者)
├──────────────┤
│ + handleRequest()│
│ - next: Handler│
└──────┬───────┘
│
┌──────┴──────┐
↓ ↓
┌──────────┐ ┌──────────┐
│Concrete │ │Concrete │
│Handler1 │ │Handler2 │
└──────────┘ └──────────┘
适用场景
- 多对象可处理同一请求且处理顺序可变
- 表单验证中需依次校验不同字段
- 日志系统按级别传递日志事件
优缺点分析
| 优势 | 劣势 |
|---|---|
| 实现请求发送者与处理者的解耦 | 请求未被处理的风险 |
| 系统灵活性高,可动态调整责任链 | 链过长导致性能损耗 |
| 简化对象间的交互关系 | 调试困难,请求流向不明确 |
实际项目应用指南
选择依据:
- 有多个对象可以处理请求,但不确定具体哪个
- 需要动态指定处理对象集合
- 需要按指定顺序处理请求
使用技巧:
java
// 抽象处理者
public abstract class Handler {
protected Handler nextHandler;
public void setNext(Handler handler) {
this.nextHandler = handler;
}
public final void handleRequest(int level) {
if (this.getHandlerLevel() == level) {
this.echo();
} else if (nextHandler != null) {
nextHandler.handleRequest(level);
} else {
System.out.println("没有处理者处理该请求");
}
}
protected abstract int getHandlerLevel();
protected abstract void echo();
}
// 具体处理者
public class ConcreteHandler1 extends Handler {
@Override
protected int getHandlerLevel() {
return 1;
}
@Override
protected void echo() {
System.out.println("处理者1处理请求");
}
}
public class ConcreteHandler2 extends Handler {
@Override
protected int getHandlerLevel() {
return 2;
}
@Override
protected void echo() {
System.out.println("处理者2处理请求");
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
Handler handler1 = new ConcreteHandler1();
Handler handler2 = new ConcreteHandler2();
handler1.setNext(handler2);
handler1.handleRequest(1); // 处理者1处理
handler1.handleRequest(2); // 处理者2处理
}
}
常见误区:
- ❌ 未设置默认处理者导致请求未处理
- ❌ 责任链过长导致性能问题
- ❌ 处理者之间存在循环依赖
14. 命令模式
核心特点
- 封装请求为对象,实现操作的参数化
- 支持撤销/重做功能
- 解耦命令发送者与接收者
UML类图描述
┌─────────────┐ ┌──────────────┐
│ Client │----------→│ Receiver │
└─────────────┘ └──────────────┘
│
┌───────┼───────┐
↓ ↓ ↓
┌────────┐┌────────┐┌────────┐
│Command ││Invoker ││Concrete│
│ ││ ││Command │
└────────┘└────────┘└────────┘
适用场景
- 需记录操作历史、支持撤销
- GUI按钮事件响应、任务调度系统
- 事务管理框架
优缺点分析
| 优势 | 劣势 |
|---|---|
| 解耦发送者与接收者 | 增加系统类数量 |
| 支持命令组合形成宏操作 | 复杂命令逻辑提升维护难度 |
- 支持撤销/重做 | 命令粒度难以把握 |
| 便于扩展新命令类型 | |
实际项目应用指南
选择依据:
- 需要将调用者与接收者解耦
- 需要支持撤销/重做操作
- 需要记录操作历史
使用技巧:
java
// 命令接口
public interface Command {
void execute();
void undo();
}
// 接收者
public class Light {
public void on() {
System.out.println("灯打开");
}
public void off() {
System.out.println("灯关闭");
}
}
// 具体命令
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light light) {
this.light = light;
}
@Override
public void execute() {
light.on();
}
@Override
public void undo() {
light.off();
}
}
// 调用者
public class RemoteControl {
private Command command;
public void setCommand(Command command) {
this.command = command;
}
public void buttonPressed() {
command.execute();
}
public void undoButtonPressed() {
command.undo();
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
Light light = new Light();
Command lightOnCommand = new LightOnCommand(light);
RemoteControl remote = new RemoteControl();
remote.setCommand(lightOnCommand);
remote.buttonPressed(); // 灯打开
remote.undoButtonPressed(); // 灯关闭
}
}
常见误区:
- ❌ 命令粒度过细或过粗
- ❌ 忽略undo/redo的实现
- ❌ 命令对象持有过多状态
15. 解释器模式
核心特点
- 定义语法规则,提供解释执行机制
- 递归结构处理复杂表达式
- 适用于特定领域语言(DSL)实现
UML类图描述
┌─────────────┐
│Expression │ (抽象表达式)
├─────────────┤
│ + interpret()│
└─────┬───────┘
│
┌──────┴──────┐
↓ ↓
┌─────────┐ ┌──────────┐
│Terminal │ │Non-Terminal│
│Expression│ │Expression │
└─────────┘ └──────────┘
适用场景
- 简单语法规则的解析
- 配置文件解析、数学表达式计算
- SQL解析器、规则引擎
优缺点分析
| 优势 | 劣势 |
|---|---|
| 易于扩展语法规则 | 处理复杂语法时效率低下 |
- 针对性强,高效解决特定领域问题 | 维护困难,递归解释导致性能瓶颈 |
| 语法规则清晰易懂 | 仅适用于简单语法场景 |
实际项目应用指南
选择依据:
- 需要解析简单语法规则
- 频繁变化的特定领域语言
- 需要支持自定义表达式
使用技巧:
java
// 抽象表达式
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;
}
}
// 非终结符表达式(加法)
public class AddExpression implements Expression {
private Expression left;
private Expression right;
public AddExpression(Expression left, Expression right) {
this.left = left;
this.right = right;
}
@Override
public int interpret() {
return left.interpret() + right.interpret();
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
// 解析表达式: 5 + 3
Expression five = new NumberExpression(5);
Expression three = new NumberExpression(3);
Expression add = new AddExpression(five, three);
System.out.println("结果: " + add.interpret()); // 输出: 8
}
}
常见误区:
- ❌ 用于复杂语法场景(建议使用ANTLR等专业工具)
- ❌ 递归深度过大导致栈溢出
- ❌ 未考虑语法错误处理
16. 迭代器模式
核心特点
- 遍历聚合对象无需暴露内部结构
- 统一遍历接口,实现对不同容器的一致访问
- 支持多种遍历方式
UML类图描述
┌─────────────┐
│ Iterator │ (抽象迭代器)
├─────────────┤
│ + hasNext() │
│ + next() │
└─────────────┘
↑
│ implements
┌───────┴───────┐
│ Concrete │
│ Iterator │
└───────────────┘
│
↓
┌─────────────┐
│ Aggregate │ (聚合)
├─────────────┤
│ + iterator()│
└─────────────┘
适用场景
- 需遍历不同聚合结构
- Java集合框架中的Iterator接口
- 自定义容器需提供标准化遍历能力
优缺点分析
| 优势 | 劣势 |
|---|---|
| 简化客户端遍历逻辑 | 增加类数量(每个聚合需对应迭代器) |
- 符合单一职责原则 | 遍历算法相对固定,难以动态修改 |
| 数据存储与遍历行为分离 | 迭代期间集合修改可能引发异常 |
实际项目应用指南
选择依据:
- 需要遍历聚合对象
- 需要提供多种遍历方式
- 希望隐藏聚合对象的内部表示
使用技巧:
java
// 迭代器接口
public interface Iterator {
boolean hasNext();
Object next();
}
// 聚合接口
public interface Aggregate {
Iterator createIterator();
}
// 具体聚合
public class ConcreteAggregate implements Aggregate {
private List<Object> items = new ArrayList<>();
public void add(Object item) {
items.add(item);
}
@Override
public Iterator createIterator() {
return new ConcreteIterator(this);
}
public List<Object> getItems() {
return items;
}
}
// 具体迭代器
public class ConcreteIterator implements Iterator {
private ConcreteAggregate aggregate;
private int current = 0;
public ConcreteIterator(ConcreteAggregate aggregate) {
this.aggregate = aggregate;
}
@Override
public boolean hasNext() {
return current < aggregate.getItems().size();
}
@Override
public Object next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return aggregate.getItems().get(current++);
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
ConcreteAggregate aggregate = new ConcreteAggregate();
aggregate.add("项目1");
aggregate.add("项目2");
aggregate.add("项目3");
Iterator iterator = aggregate.createIterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
}
常见误区:
- ❌ 迭代器过度依赖具体聚合类
- ❌ 未正确实现remove()方法
- ❌ 忽略fail-fast机制
17. 中介者模式
核心特点
- 集中管理对象间交互,减少直接耦合
- 将多对多交互转化为一对多关系
- 简化维护复杂度
UML类图描述
┌─────────┐
│ Mediator│
└────┬────┘
│
┌──────┼──────┐
↓ ↓ ↓
┌────────┐┌────────┐┌────────┐
│Colleague││Colleague││Colleague│
│ A ││ B ││ C │
└────────┘└────────┘└────────┘
适用场景
- 对象间交互复杂的多对多场景
- 聊天室系统(用户通过服务器中转消息)
- GUI组件协调(按钮与文本框通过表单中介联动)
优缺点分析
| 优势 | 劣势 |
|---|---|
| 降低耦合度,集中控制交互逻辑 | 中介者可能成为性能瓶颈 |
| 简化对象间关系 | 过度集中化导致中介者复杂度激增 |
- 提高可维护性 | 中介者职责过重违背单一职责原则 |
实际项目应用指南
选择依据:
- 对象间存在复杂的多对多交互
- 需要集中控制交互逻辑
- 希望降低对象间的耦合度
使用技巧:
java
// 中介者接口
public interface Mediator {
void sendMessage(Colleague colleague, String message);
}
// 同事抽象类
public abstract class Colleague {
protected Mediator mediator;
public Colleague(Mediator mediator) {
this.mediator = mediator;
}
public abstract void receiveMessage(String message);
public abstract void sendMessage(String message);
}
// 具体同事
public class ConcreteColleagueA extends Colleague {
public ConcreteColleagueA(Mediator mediator) {
super(mediator);
}
@Override
public void receiveMessage(String message) {
System.out.println("同事A收到消息: " + message);
}
@Override
public void sendMessage(String message) {
System.out.println("同事A发送消息: " + message);
mediator.sendMessage(this, message);
}
}
// 具体中介者
public class ConcreteMediator implements Mediator {
private ConcreteColleagueA colleagueA;
private ConcreteColleagueB colleagueB;
public void setColleagueA(ConcreteColleagueA colleagueA) {
this.colleagueA = colleagueA;
}
public void setColleagueB(ConcreteColleagueB colleagueB) {
this.colleagueB = colleagueB;
}
@Override
public void sendMessage(Colleague colleague, String message) {
if (colleague == colleagueA) {
colleagueB.receiveMessage(message);
} else {
colleagueA.receiveMessage(message);
}
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
ConcreteMediator mediator = new ConcreteMediator();
ConcreteColleagueA colleagueA = new ConcreteColleagueA(mediator);
ConcreteColleagueB colleagueB = new ConcreteColleagueB(mediator);
mediator.setColleagueA(colleagueA);
mediator.setColleagueB(colleagueB);
colleagueA.sendMessage("你好,同事B");
colleagueB.sendMessage("你好,同事A");
}
}
常见误区:
- ❌ 中介者职责过重
- ❌ 同事对象直接持有具体中介者实例
- ❅ 未按功能拆分多个中介者
18. 备忘录模式
核心特点
- 保存对象内部状态,不破坏封装性
- 支持状态恢复
- 由原发器、备忘录、负责人三角色协作
UML类图描述
┌──────────┐
│ Originator│
└────┬─────┘
│ creates/uses
┌────┴────────┐
↓ ↓
┌─────────┐ ┌──────────┐
│Memento │ │ Caretaker│
└─────────┘ └──────────┘
适用场景
- 需要保存/恢复状态
- 文本编辑器的撤销功能
- 游戏进度存档系统
- 配置项版本管理
优缺点分析
| 优势 | 劣势 |
|---|---|
| 状态管理逻辑清晰 | 频繁创建备忘录导致资源消耗 |
| 有效保护对象封装性 | 复杂对象状态复制产生性能成本 |
- 支持多版本状态管理 | 需合理控制备忘录数量 |
实际项目应用指南
选择依据:
- 需要保存对象的历史状态
- 需要实现撤销/重做功能
- 需要对象状态的快照机制
使用技巧:
java
// 备忘录类
public class Memento {
private String state;
public Memento(String state) {
this.state = state;
}
public String getState() {
return state;
}
}
// 原发器
public class Originator {
private String state;
public void setState(String state) {
this.state = state;
System.out.println("当前状态: " + state);
}
public String getState() {
return state;
}
public Memento saveStateToMemento() {
return new Memento(state);
}
public void getStateFromMemento(Memento memento) {
state = memento.getState();
}
}
// 负责人
public class Caretaker {
private List<Memento> mementoList = new ArrayList<>();
public void add(Memento state) {
mementoList.add(state);
}
public Memento get(int index) {
return mementoList.get(index);
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
Originator originator = new Originator();
Caretaker caretaker = new Caretaker();
originator.setState("状态1");
caretaker.add(originator.saveStateToMemento());
originator.setState("状态2");
caretaker.add(originator.saveStateToMemento());
originator.setState("状态3");
// 恢复状态
originator.getStateFromMemento(caretaker.get(0));
System.out.println("恢复后的状态: " + originator.getState());
}
}
常见误区:
- ❌ 备忘录类暴露内部状态字段
- ❌ 未控制备忘录数量导致内存占用过高
- ❌ 忽略深拷贝问题
19. 观察者模式
核心特点
- 建立一对多的依赖关系
- 状态变化自动通知所有观察者
- 实现对象间的松耦合通信
UML类图描述
┌───────────┐
│ Subject │ (主题)
├───────────┤
│ + attach()│
│ + detach()│
│ + notify()│
└───────────┘
↑
│
┌───────┼───────┐
↓ ↓ ↓
┌────────┐┌────────┐┌────────┐
│Observer││Observer││Observer│
│ A ││ B ││ C │
└────────┘└────────┘└────────┘
适用场景
- 事件驱动场景(GUI按钮点击事件监听)
- 消息队列的订阅发布机制
- 分布式系统的数据同步
优缺点分析
| 优势 | 劣势 |
|---|---|
| 状态变化响应及时 | 通知顺序不确定 |
| 观察者扩展灵活 | 循环依赖可能导致死锁 |
- 松耦合设计 | 观察者数量多时性能问题 |
实际项目应用指南
选择依据:
- 对象间存在一对多依赖关系
- 一个对象的状态变化需要通知其他对象
- 需要构建松耦合的事件驱动系统
使用技巧:
java
// 观察者接口
public interface Observer {
void update(String message);
}
// 主题接口
public interface Subject {
void attach(Observer observer);
void detach(Observer observer);
void notifyObservers();
}
// 具体主题
public class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
private String state;
@Override
public void attach(Observer observer) {
observers.add(observer);
}
@Override
public void detach(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(state);
}
}
public void setState(String state) {
this.state = state;
notifyObservers();
}
}
// 具体观察者
public class ConcreteObserver implements Observer {
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " 收到消息: " + message);
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
ConcreteObserver observer1 = new ConcreteObserver("观察者1");
ConcreteObserver observer2 = new ConcreteObserver("观察者2");
subject.attach(observer1);
subject.attach(observer2);
subject.setState("状态已更新");
}
}
常见误区:
- ❌ 观察者未注销导致内存泄漏
- ❌ 通知顺序不明确
- ❅ 多线程环境下未考虑线程安全
20. 状态模式
核心特点
- 封装状态行为,替代复杂条件判断
- 状态转换由状态对象控制
- 对象状态改变时行为随之改变
UML类图描述
┌──────────┐
│ Context │ (环境类)
└────┬─────┘
│ uses
┌────┴────────┐
↓ ↓
┌─────────┐ ┌─────────┐
│ State │ │ Concrete│
│ (抽象) │ │State A │
└─────────┘ └─────────┘
↑
│
┌────────┴────────┐
↓ ↓
┌──────────┐ ┌──────────┐
│Concrete │ │Concrete │
│State B │ │State C │
└──────────┘ └──────────┘
适用场景
- 对象状态多且转换复杂
- 电商订单状态流转(待支付→已支付→已发货→已完成)
- 工作流引擎中的任务状态管理
优缺点分析
| 优势 | 劣势 |
|---|---|
| 状态逻辑清晰,易于维护 | 状态类数量多 |
| 符合开闭原则,扩展方便 | 状态转换逻辑分散 |
| 避免复杂条件判断 | 理解成本增加 |
实际项目应用指南
选择依据:
- 对象行为随状态变化而变化
- 状态转换规则复杂
- 需要大量条件判断来描述对象状态
使用技巧:
java
// 抽象状态
public interface State {
void handle(Context context);
}
// 具体状态A
public class ConcreteStateA implements State {
@Override
public void handle(Context context) {
System.out.println("当前状态: A");
context.setState(new ConcreteStateB());
}
}
// 具体状态B
public class ConcreteStateB implements State {
@Override
public void handle(Context context) {
System.out.println("当前状态: B");
context.setState(new ConcreteStateA());
}
}
// 环境类
public class Context {
private State state;
public Context(State state) {
this.state = state;
}
public void setState(State state) {
this.state = state;
}
public void request() {
state.handle(this);
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
Context context = new Context(new ConcreteStateA());
context.request(); // 当前状态: A
context.request(); // 当前状态: B
context.request(); // 当前状态: A
}
}
常见误区:
- ❌ 状态类承担过多职责
- ❌ 状态转换逻辑分散
- ❅ 未使用集中式状态管理器
21. 策略模式
核心特点
- 封装算法族,实现算法与使用分离
- 支持运行时动态切换算法
- 消除复杂条件判断
UML类图描述
┌───────────┐
│ Strategy │ (抽象策略)
├───────────┤
│ + execute()│
└─────┬─────┘
│
┌─────┴────────┐
↓ ↓
┌──────────┐ ┌──────────┐
│Concrete │ │Concrete │
│StrategyA │ │StrategyB │
└──────────┘ └──────────┘
↑ ↑
└─────┬─────┘
│
┌──────┴──────┐
│ Context │
└─────────────┘
适用场景
- 多种算法可选且需动态切换
- 支付系统中不同支付方式的选择
- 搜索引擎中多种排序算法的切换
优缺点分析
| 优势 | 劣势 |
|---|---|
| 算法切换灵活 | 客户端需了解所有策略 |
| 符合开闭原则 | 策略过多导致类膨胀 |
- 算法独立变化 | 策略选择逻辑复杂 |
实际项目应用指南
选择依据:
- 系统中有多种同类算法可以互换
- 算法需要动态切换
- 需要隐藏具体算法实现细节
使用技巧:
java
// 策略接口
public interface Strategy {
int execute(int a, int b);
}
// 具体策略A:加法
public class ConcreteStrategyAdd implements Strategy {
@Override
public int execute(int a, int b) {
return a + b;
}
}
// 具体策略B:减法
public class ConcreteStrategySubtract implements Strategy {
@Override
public int execute(int a, int b) {
return a - b;
}
}
// 上下文
public class Context {
private Strategy strategy;
public Context(Strategy strategy) {
this.strategy = strategy;
}
public void setStrategy(Strategy strategy) {
this.strategy = strategy;
}
public int executeStrategy(int a, int b) {
return strategy.execute(a, b);
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
Context context = new Context(new ConcreteStrategyAdd());
System.out.println("加法结果: " + context.executeStrategy(10, 5)); // 15
context.setStrategy(new ConcreteStrategySubtract());
System.out.println("减法结果: " + context.executeStrategy(10, 5)); // 5
}
}
常见误区:
- ❌ 客户端直接依赖具体策略类
- ❅ 策略选择逻辑复杂
- ❌ 未结合工厂模式管理策略
22. 模板方法模式
核心特点
- 定义算法骨架,延迟具体步骤到子类
- 固定流程,灵活实现
- 通过抽象类控制子类行为
UML类图描述
┌──────────────────┐
│AbstractClass │ (抽象类)
├──────────────────┤
│ + templateMethod()│
│ + primitiveOp1() │
│ + primitiveOp2() │
└─────┬────────────┘
│
┌─────┴────────┐
↓ ↓
┌──────────┐ ┌──────────┐
│Concrete │ │Concrete │
│Class1 │ │Class2 │
└──────────┘ └──────────┘
适用场景
- 核心流程固定但部分步骤实现可变
- 测试框架的用例执行流程
- 报表生成系统(固定采集-处理-渲染流程)
优缺点分析
| 优势 | 劣势 |
|---|---|
| 流程代码复用率高 | 子类数量可能膨胀 |
- 模板方法控制子类行为 | 模板方法修改影响所有子类 |
| 符合开闭原则 | 步骤间依赖关系模糊 |
实际项目应用指南
选择依据:
- 多个子类有公共行为,但实现细节不同
- 需要固定算法的执行顺序
- 需要控制子类的扩展
使用技巧:
java
// 抽象类
public abstract class AbstractClass {
// 模板方法,定义为final防止子类重写
public final void templateMethod() {
primitiveOperation1();
primitiveOperation2();
concreteOperation();
hook(); // 钩子方法
}
// 抽象方法,由子类实现
protected abstract void primitiveOperation1();
protected abstract void primitiveOperation2();
// 具体方法
private void concreteOperation() {
System.out.println("公共操作");
}
// 钩子方法,子类可选择重写
protected void hook() {
System.out.println("默认钩子方法");
}
}
// 具体类A
public class ConcreteClassA extends AbstractClass {
@Override
protected void primitiveOperation1() {
System.out.println("具体类A的操作1");
}
@Override
protected void primitiveOperation2() {
System.out.println("具体类A的操作2");
}
@Override
protected void hook() {
System.out.println("具体类A重写钩子方法");
}
}
// 具体类B
public class ConcreteClassB extends AbstractClass {
@Override
protected void primitiveOperation1() {
System.out.println("具体类B的操作1");
}
@Override
protected void primitiveOperation2() {
System.out.println("具体类B的操作2");
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
AbstractClass classA = new ConcreteClassA();
classA.templateMethod();
System.out.println("----------------");
AbstractClass classB = new ConcreteClassB();
classB.templateMethod();
}
}
常见误区:
- ❌ 模板方法包含过多步骤导致职责混乱
- ❅ 步骤间依赖关系模糊
- ❌ 未使用final修饰模板方法
23. 访问者模式
核心特点
- 分离数据结构与操作
- 通过双分派机制实现差异化操作
- 操作可独立扩展
UML类图描述
┌──────────┐ ┌──────────┐
│ Visitor │ │ Element │
├──────────┤ ├──────────┤
│ + visit(A)│ │ + accept()│
│ + visit(B)│ └────┬─────┘
└──────────┘ │
↑ ↑
┌────────┴──────┐ ┌─────┴──────┐
↓ ↓ ↓ ↓
┌──────────┐ ┌──────────┐ ┌──────┐ ┌──────┐
│Concrete │ │Concrete │ │Element│ │Element│
│Visitor │ │Visitor │ │ A │ │ B │
└──────────┘ └──────────┘ └──────┘ └──────┘
适用场景
- 数据结构稳定但需频繁添加新操作
- AST语法树解析
- 数据库表操作(对不同表执行查询、统计)
优缺点分析
| 优势 | 劣势 |
|---|---|
| 操作扩展灵活 | 新增元素类需修改所有访问者 |
| 相似操作集中管理 | 双分派增加系统复杂度 |
- 符合开闭原则 | 访问者依赖元素内部细节 |
实际项目应用指南
选择依据:
- 数据结构相对稳定
- 需要对数据结构执行多种不同的操作
- 操作逻辑需要集中管理
使用技巧:
java
// 访问者接口
public interface Visitor {
void visit(ConcreteElementA elementA);
void visit(ConcreteElementB elementB);
}
// 元素接口
public interface Element {
void accept(Visitor visitor);
}
// 具体元素A
public class ConcreteElementA implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public void operationA() {
System.out.println("元素A的操作");
}
}
// 具体元素B
public class ConcreteElementB implements Element {
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
public void operationB() {
System.out.println("元素B的操作");
}
}
// 具体访问者
public class ConcreteVisitor implements Visitor {
@Override
public void visit(ConcreteElementA elementA) {
System.out.println("访问者处理元素A");
elementA.operationA();
}
@Override
public void visit(ConcreteElementB elementB) {
System.out.println("访问者处理元素B");
elementB.operationB();
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
Element elementA = new ConcreteElementA();
Element elementB = new ConcreteElementB();
Visitor visitor = new ConcreteVisitor();
elementA.accept(visitor);
elementB.accept(visitor);
}
}
常见误区:
- ❅ 元素类型频繁变化时使用
- ❅ 访问者依赖元素内部细节
- ❌ 未考虑双分派的性能开销
实际项目应用指南
创建型设计模式应用要点
| 模式 | 选择依据 | 关键技巧 | 常见误区 |
|---|---|---|---|
| 单例模式 | 全局唯一实例需求 | 枚举实现、DCL双检锁 | 忽略线程安全、序列化破坏 |
| 工厂方法 | 产品类型需扩展 | 依赖倒置、单一职责 | 工厂职责过重 |
| 抽象工厂 | 产品族协同工作 | 接口隔离、产品族一致性 | 产品族不稳定时误用 |
| 建造者模式 | 复杂对象分步构建 | 链式调用、静态内部类 | 与工厂模式混淆 |
| 原型模式 | 对象创建成本高 | 区分深浅拷贝 | 忽略嵌套对象引用 |
结构型设计模式应用要点
| 模式 | 选择依据 | 关键技巧 | 常见误区 |
|---|---|---|---|
| 适配器模式 | 接口不兼容 | 对象适配器(组合) | 过度使用增加复杂度 |
| 桥接模式 | 多维度独立变化 | 组合优于继承 | 单一维度误用 |
| 组合模式 | 树形结构 | 递归遍历 | 叶子节点实现管理方法 |
| 装饰器模式 | 动态功能扩展 | 透明包裹 | 装饰链过长 |
| 外观模式 | 简化复杂系统 | 仅协调不添加逻辑 | 过度封装限制灵活性 |
| 享元模式 | 大量相似对象 | 分离内外状态 | 外部状态存储错误 |
| 代理模式 | 控制访问 | 静态/动态代理选择 | 与装饰器混淆 |
行为型设计模式应用要点
| 模式 | 选择依据 | 关键技巧 | 常见误区 |
|---|---|---|---|
| 责任链模式 | 动态处理链 | 设置默认处理者 | 链过长性能问题 |
| 命令模式 | 撤销/重做需求 | 命令粒度适中 | 命令对象状态过多 |
| 解释器模式 | 简单语法解析 | 递归结构 | 复杂语法误用 |
| 迭代器模式 | 统一遍历接口 | fail-fast机制 | 依赖具体聚合类 |
| 中介者模式 | 复杂交互协调 | 按功能拆分中介者 | 中介者职责过重 |
| 备忘录模式 | 状态保存恢复 | 深拷贝实现 | 备忘录暴露内部状态 |
| 观察者模式 | 状态通知 | 观察者注销 | 忽略线程安全 |
| 状态模式 | 复杂状态转换 | 封装状态行为 | 状态类职责不清 |
| 策略模式 | 算法动态切换 | 结合工厂模式 | 客户端依赖具体策略 |
| 模板方法 | 流程固定步骤可变 | final模板方法 | 步骤间依赖模糊 |
| 访问者模式 | 数据结构稳定操作多变 | 双分派机制 | 元素类型频繁变化 |
模式选择决策框架
决策框架设计原则
设计模式的选择应建立在问题驱动的底层逻辑之上,而非盲目追求技术实现。开发者需优先明确业务需求本质,再匹配适合的模式解决方案。
核心原则:
- 先明确问题边界与约束
- 再从设计模式库中筛选匹配方案
- 模式本质是经验抽象,而非银弹
评估三大核心因素:
- 需求稳定性:高频变更场景适合策略模式
- 系统扩展性:组件复用需求适合装饰器模式
- 团队技术栈适配度:影响代理模式应用
设计模式选择决策树
设计模式选择决策树
起点:判断核心问题类型
├─ 对象创建相关问题 → 创建型模式
│ ├─ 对象创建过程复杂且需解耦
│ │ ├─ 单一产品 → 工厂方法模式
│ │ └─ 产品族 → 抽象工厂模式
│ ├─ 控制实例数量或确保全局唯一
│ │ └─ 单例模式
│ └─ 动态指定对象类型或延迟初始化
│ ├─ 复制现有对象 → 原型模式
│ └─ 分步构建 → 建造者模式
│
├─ 类或对象组合问题 → 结构型模式
│ ├─ 接口不兼容需适配 → 适配器模式
│ ├─ 希望为对象添加额外功能 → 装饰器模式
│ ├─ 需统一复杂子系统访问接口 → 外观模式
│ ├─ 两个独立变化维度 → 桥接模式
│ ├─ 树形结构统一处理 → 组合模式
│ ├─ 控制对象访问 → 代理模式
│ └─ 大量相似对象复用 → 享元模式
│
└─ 对象交互协作问题 → 行为型模式
├─ 算法族需动态切换 → 策略模式
├─ 对象间需松耦合通信 → 观察者模式
├─ 复杂流程需分步控制
│ ├─ 流程固定步骤可变 → 模板方法模式
│ └─ 操作需记录/撤销 → 命令模式
├─ 请求处理者不确定或动态变化 → 责任链模式
├─ 对象状态多且转换复杂 → 状态模式
├─ 需遍历不同聚合结构 → 迭代器模式
├─ 多对象多对多交互 → 中介者模式
├─ 需保存/恢复状态 → 备忘录模式
├─ 简单语法解析 → 解释器模式
└─ 数据结构稳定但需频繁添加新操作 → 访问者模式
决策框架应用示例
示例1:订单状态流转场景(行为型)
决策路径 :业务场景 → 行为型模式 → 对象状态多且转换复杂 → 状态模式
分析逻辑:
- 订单系统存在待支付、已支付、已发货、已完成等多种状态
- 状态间转换规则复杂(如待支付超时自动取消)
- 状态模式通过将不同状态封装为独立类,使状态转换逻辑与业务实体解耦
- 符合"单一职责"原则,简化状态管理代码
示例2:日志框架设计场景(创建型+结构型)
决策路径 :业务场景 → 创建型模式 → 需要灵活创建不同日志对象 → 工厂方法模式
分析逻辑:
- 日志系统需支持控制台日志、文件日志、数据库日志等多种实现
- 可能动态扩展新日志类型
- 工厂方法模式通过定义日志对象的创建接口,由具体工厂类负责实例化特定类型的日志对象
- 满足"开闭原则",提升框架的扩展性和可维护性
示例3:多条件校验场景(行为型)
决策路径 :业务场景 → 行为型模式 → 请求处理者不确定或动态变化 → 责任链模式
分析逻辑:
- 表单提交需要依次校验:非空校验 → 格式校验 → 业务规则校验
- 校验规则可能动态增减
- 责任链模式允许客户端动态配置校验顺序和规则
- 实现校验逻辑与客户端的解耦
总结
设计模式是软件工程领域的经验结晶,但切记:
- 不要为了用模式而用模式------模式是解决问题的工具,而非目的
- 先理解问题本质------明确需求后再选择合适的模式
- 灵活运用------模式可以组合使用,也可以根据实际情况调整
- 持续学习------设计模式不是一成不变的,需要根据项目实践不断完善
掌握设计模式的核心不在于记住23种模式的定义,而在于理解它们背后的设计思想和解决问题的思维方式。在实际项目中,结合具体场景灵活运用,才能真正发挥设计模式的价值。