← 返回博客列表
一、引言:设计模式概述
设计模式(Design Pattern)是软件设计中给定上下文环境下对普遍存在问题的一种可复用的解决方案 。它不是一段可以直接翻译成代码的成品代码,而是一套解决某类问题的经验总结 ,是架构师与程序员之间交流的"通用词汇"。
设计模式的起源(GoF 四人帮)
1994 年,Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides 四位作者(合称 Gang of Four,GoF )出版了经典著作《Design Patterns: Elements of Reusable Object-Oriented Software》(设计模式:可复用面向对象软件的基础)。书中总结了 23 种 面向对象设计模式,奠定了设计模式的理论体系。这 23 种模式也被称为 GoF 23 种设计模式 ,是软考系统架构设计师的核心考点。
GoF 将 23 种设计模式按照目的(职责) 划分为三大类:
创建型模式(Creational Patterns,5 种) :关注对象的创建过程,将对象的创建与使用分离,使客户端无需关心对象如何被创建、组合和表示。
结构型模式(Structural Patterns,7 种) :关注类与对象的组合,通过组合获得更大的结构,解决接口适配、职责扩展等问题。
行为型模式(Behavioral Patterns,11 种) :关注对象之间的职责分配与算法交互,描述对象之间如何协作、如何分配职责。
此外,按范围(类/对象) 划分,模式还可分为:类模式(处理类与子类的关系,在编译时确定,如工厂方法、适配器(类)、解释器、模板方法)和对象模式(处理对象间的关系,在运行时动态变化,其余大多数模式)。
1.1 设计模式的四要素
每一个设计模式都包含四个基本要素,描述时必须完整:
要素
含义
示例(单例模式)
模式名(Pattern Name)
用一两个词描述模式的问题、解法和效果,便于交流
Singleton(单例)
问题(Problem)
何时使用该模式,描述前提条件和待解决的问题
系统只需要一个实例,且需要全局访问点
解决方案(Solution)
设计的组成成分、它们的关系、职责和协作方式
私有构造、静态持有唯一实例、静态获取方法
效果(Consequences)
应用模式带来的好处与代价(权衡)
控制唯一实例、全局访问;但易成为"全局变量"
1.2 23 种模式分类总览
GoF 23 种设计模式
创建型模式(5)
结构型模式(7)
行为型模式(11)
单例 Singleton
工厂方法 Factory Method
抽象工厂 Abstract Factory
建造者 Builder
原型 Prototype
适配器 Adapter
桥接 Bridge
组合 Composite
装饰器 Decorator
外观 Facade
享元 Flyweight
代理 Proxy
责任链 Chain of Resp.
命令 Command
解释器 Interpreter
迭代器 Iterator
中介者 Mediator
备忘录 Memento
观察者 Observer
状态 State
策略 Strategy
模板方法 Template Method
访问者 Visitor
按范围划分(软考考点)
类模式(编译时确定):工厂方法、适配器(类)、解释器、模板方法
对象模式(运行时动态):其余 19 种均为对象模式
记忆口诀:创建5 + 结构7 + 行为11 = 23
高频考点:单例、工厂方法、观察者、策略、代理、适配器、装饰器
图 1:GoF 23 种设计模式分类总览
软考记忆技巧
口诀"创五结七行十一":创建型 5 个、结构型 7 个、行为型 11 个,合计 23。
创建型口诀:"单工抽建原"(单例、工厂方法、抽象工厂、建造者、原型);
结构型口诀:"适桥组装外享代"(适配器、桥接、组合、装饰、外观、享元、代理);
行为型口诀:"责命解迭中备观状策模访"(责任链、命令、解释器、迭代器、中介者、备忘录、观察者、状态、策略、模板方法、访问者)。
下面按三大类逐一深入讲解。每个模式均包含:意图、UML 类图(SVG)、代码示例、适用场景、生活类比 。
二、创建型模式(5 种)详解
创建型模式抽象了实例化过程,帮助系统独立于对象的创建、组合和表示。核心思想是将对象的创建与使用分离 ,客户端不需要知道对象是如何被创建和组装的。
2.1 单例模式(Singleton)创建型
意图 :保证一个类仅有一个实例,并提供一个访问它的全局访问点。
适用场景 :系统只需要一个实例对象;客户访问点需要唯一且全局可访问,如配置管理器、日志管理器、线程池、数据库连接池、Windows 任务管理器、操作系统中的打印后台处理程序。
生活类比 :一个国家只有一个国家主席/总统;一台电脑只有一个任务管理器(无论打开多少次都是同一个窗口)。
Singleton
- instance: Singleton
- data: Object
- Singleton()
+ getInstance(): Singleton
+ operation(): void
+ getData(): Object
1 instance
Client
getInstance()
图 2:单例模式 UML 类图(私有构造 + 静态实例 + 全局访问点)
Java 实现(双重检查锁定 DCL,多线程安全) :
// 双重检查锁定(Double-Checked Locking)实现线程安全的单例
public class Singleton {
// 1. volatile 防止指令重排序(关键!)
private static volatile Singleton instance;
// 2. 私有构造函数,禁止外部 new
private Singleton() {}
// 3. 静态全局访问点
public static Singleton getInstance() {
if (instance == null ) { // 第一次检查,避免不必要的同步
synchronized (Singleton.class ) {
if (instance == null ) { // 第二次检查,确保唯一实例
instance = new Singleton();
}
}
}
return instance;
}
}
多线程安全注意点
1. 懒汉式(非线程安全) :最简单但多线程下会创建多个实例,不可用。
2. 懒汉式 + synchronized :每次获取都加锁,性能差。
3. 双重检查锁定 DCL :必须加 volatile,否则对象初始化指令重排序会导致拿到未初始化完成的对象。
4. 静态内部类(推荐) :利用类加载机制保证线程安全,延迟加载且无锁。
5. 枚举(最佳) :天然防反射、防序列化破坏,Effective Java 推荐。
// 静态内部类实现(推荐,线程安全 + 延迟加载)
public class Singleton {
private Singleton() {}
private static class Holder {
static final Singleton INSTANCE = new Singleton();
}
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
优点
保证唯一实例,节省系统资源
提供受控的全局访问点
延迟初始化(懒汉式)
缺点
没有接口,扩展困难
对 OCP 原则不友好
易被当作"全局变量"滥用
2.2 工厂方法模式(Factory Method)创建型
意图 :定义一个用于创建对象的接口,但让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。
适用场景 :客户端不需要知道所创建对象的具体类;希望子类决定创建哪个对象;将对象的创建与使用解耦。
生活类比 :物流公司(Logistics)运输货物,公路运输用卡车(Truck),海运用轮船(Ship)。运输工具的创建由具体物流子类决定,调用者只关心"运输"接口。
«interface»
Transport
+ deliver(): void
Truck
+ deliver()
Ship
+ deliver()
Logistics
+ createTransport(): Transport
+ planDelivery(): void
RoadLogistics
+ createTransport()
SeaLogistics
+ createTransport()
creates
图 3:工厂方法模式 UML 类图(Logistics 物流运输示例)
// 产品接口
public interface Transport {
void deliver();
}
// 具体产品
public class Truck implements Transport {
public void deliver() {
System.out.println("通过公路运输货物" );
}
}
public class Ship implements Transport {
public void deliver() {
System.out.println("通过海运运输货物" );
}
}
// 抽象创建者(工厂方法)
public abstract class Logistics {
public abstract Transport createTransport(); // 工厂方法
public void planDelivery() {
Transport t = createTransport(); // 依赖抽象,不知具体类
t.deliver();
}
}
// 具体创建者
public class RoadLogistics extends Logistics {
public Transport createTransport() { return new Truck(); }
}
public class SeaLogistics extends Logistics {
public Transport createTransport() { return new Ship(); }
}
工厂方法 vs 简单工厂
简单工厂(静态工厂方法)由一个工厂类根据参数返回不同实例,违反 OCP(新增产品需改工厂代码)。工厂方法将工厂抽象化,每新增一种产品只需新增对应工厂子类,符合开闭原则。
2.3 抽象工厂模式(Abstract Factory)创建型
意图 :提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
适用场景 :系统需要独立于产品的创建、组合和表示;系统由多个产品族中的一个来配置;强调一系列相关产品对象一起使用。
生活类比 :跨平台 UI 工厂——Windows 风格工厂生产 Windows 按钮 + Windows 文本框;Mac 风格工厂生产 Mac 按钮 + Mac 文本框。同一工厂生产的产品风格一致、相互搭配。
«interface»
GUIFactory
+ createButton(): Button
+ createCheckbox(): Checkbox
WinFactory
+ createButton()
+ createCheckbox()
MacFactory
+ createButton()
+ createCheckbox()
Button
+ render()
Checkbox
+ render()
WinButton
MacButton
MacCheckbox
图 4:抽象工厂模式 UML 类图(跨平台 UI 工厂示例)
public interface GUIFactory {
Button createButton();
Checkbox createCheckbox();
}
public class WinFactory implements GUIFactory {
public Button createButton() { return new WinButton(); }
public Checkbox createCheckbox() { return new WinCheckbox(); }
}
public class MacFactory implements GUIFactory {
public Button createButton() { return new MacButton(); }
public Checkbox createCheckbox() { return new MacCheckbox(); }
}
// 客户端:依赖抽象工厂,整套产品族一起替换
public class Application {
private Button button;
private Checkbox checkbox;
public Application(GUIFactory factory) {
button = factory.createButton();
checkbox = factory.createCheckbox(); // 同族产品风格一致
}
}
抽象工厂 vs 工厂方法(软考高频对比)
工厂方法 :生产一种 产品,一个工厂方法对应一个产品等级结构。
抽象工厂 :生产一族 产品(多个相关产品),面向产品族。每新增一个产品族(如 Linux 风格)只需新增一个工厂;但新增产品等级(如新增 Scrollbar)需要修改抽象工厂接口及所有实现 ,违反 OCP。
2.4 建造者模式(Builder)创建型
意图 :将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
适用场景 :需要生成的对象具有复杂的内部结构;对象的属性需要按一定顺序构建;需要把构造代码与表示代码分离。
生活类比 :点餐——套餐构建者按"主食+小吃+饮料"顺序组装;SQL 查询构建器按 SELECT/FROM/WHERE/ORDER BY 拼装 SQL。
Director
- builder: Builder
+ construct(): Product
«interface» Builder
+ buildPartA(): void
+ buildPartB(): void
+ buildPartC(): void
+ getResult(): Product
SQLBuilder
+ buildPartA()
+ buildPartB()
+ getResult()
Product (SQL)
- sql: String
+ execute(): Result
uses
creates
图 5:建造者模式 UML 类图(SQL 查询构建器示例)
// SQL 查询构建器示例
public class SQLBuilder {
private StringBuilder sql = new StringBuilder();
public SQLBuilder select(String cols) {
sql.append("SELECT " ).append(cols);
return this ;
}
public SQLBuilder from(String table) {
sql.append(" FROM " ).append(table);
return this ;
}
public SQLBuilder where(String cond) {
sql.append(" WHERE " ).append(cond);
return this ;
}
public SQLBuilder orderBy(String col) {
sql.append(" ORDER BY " ).append(col);
return this ;
}
public String build() { return sql.toString(); }
}
// 使用:链式调用
String q = new SQLBuilder()
.select("id, name" ).from("user" )
.where("age > 18" ).orderBy("id" ).build();
建造者模式要点 :分离构造与表示,Director 控制构建顺序,Builder 提供各部件构建方法。链式调用(Fluent Builder)是常见简化形式。与工厂模式区别:建造者关注按步骤构建复杂对象 ,工厂关注一次性创建产品 。
2.5 原型模式(Prototype)创建型
意图 :用原型实例指定创建对象的种类,并通过拷贝这些原型来创建新对象。
适用场景 :当创建新对象成本较大(如需要复杂计算、数据库查询)时,通过复制已有对象提高效率;系统应独立于产品的创建、组合和表示。
生活类比 :文档复制——以一个已有文档为模板,复制一份后稍作修改得到新文档;细胞分裂;分形几何。
«interface» Prototype
+ clone(): Prototype
Document
- content: String
- author: String
+ clone(): Document
clone()
Client
clone
图 6:原型模式 UML 类图(文档克隆示例)
// Java 实现:实现 Cloneable 接口
public class Document implements Cloneable {
private String content;
private String author;
public Document(String content, String author) {
this .content = content;
this .author = author;
}
@Override
public Document clone() {
try {
return (Document) super .clone(); // 浅拷贝
} catch (CloneNotSupportedException e) {
throw new AssertionError();
}
}
}
// 使用
Document original = new Document("正文内容" , "张三" );
Document copy = original.clone(); // 通过克隆快速创建
浅拷贝 vs 深拷贝(重要考点)
浅拷贝(Shallow Copy) :仅复制基本类型和引用,引用类型仍指向同一对象。修改克隆体的引用对象会影响原型。
深拷贝(Deep Copy) :递归复制所有引用对象,克隆体与原型完全独立。实现方式:手动逐字段复制、序列化/反序列化。
三、结构型模式(7 种)详解
结构型模式关注类与对象的组合 ,通过继承或组合获得更大的结构。核心是"如何把类/对象组合成更大的结构",解决接口适配、职责扩展、结构简化等问题。
3.1 适配器模式(Adapter)结构型
意图 :将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
适用场景 :需要使用已有的类,但其接口与需要的接口不匹配;想创建一个可以复用的类,用于与不相关或不可预见的类协同工作。
生活类比 :电源适配器——中式插头(两脚扁插)插不进欧式插座(两脚圆插),用转换头适配;Type-C 转 USB 适配器。
«interface» Target
+ request(): void
Adapter
- adaptee: Adaptee
+ request(): void
Adaptee
+ specificRequest()
holds
Client
uses Target
两种适配器形式(软考考点)
类适配器:Adapter extends Adaptee implements Target(多重继承,Java 不支持多继承类)
对象适配器:Adapter implements Target,内部持有 Adaptee 引用(推荐,更灵活)
类适配器用继承,对象适配器用组合,对象适配器更符合合成复用原则
图 7:适配器模式 UML 类图(对象适配器形式 + 电源适配器类比)
// 对象适配器:220V 电源适配为 5V
public interface Target { // 目标接口:5V
int output5V();
}
public class Adaptee { // 被适配者:220V
public int output220V() { return 220; }
}
public class PowerAdapter implements Target {
private Adaptee adaptee; // 持有被适配者
public PowerAdapter(Adaptee a) { this .adaptee = a; }
public int output5V() { // 转换接口
int v = adaptee.output220V();
return v / 44; // 220 -> 5
}
}
3.2 桥接模式(Bridge)结构型
意图 :将抽象部分与它的实现部分分离,使它们都可以独立地变化。
适用场景 :不希望在抽象和实现之间有固定的绑定关系;抽象和实现都应可以通过子类扩充;对抽象的实现部分的修改不影响客户端。
生活类比 :遥控器(抽象)与设备(实现)——基础遥控器、高级遥控器(抽象维度);电视、收音机(实现维度)。两个维度独立扩展,用桥接避免多维度继承爆炸。
RemoteControl
- device: Device
+ togglePower()
+ volumeDown()
AdvancedRemote
+ mute()
«interface» Device
+ isEnabled()
TV
+ isEnabled()
Radio
+ isEnabled()
桥(组合)
抽象维度(遥控器)与实现维度(设备)独立扩展,通过组合连接,避免类爆炸
图 8:桥接模式 UML 类图(遥控器 + 设备示例)
桥接模式核心价值 :将"多维度变化"分离。如果不桥接,2 种遥控器 × 3 种设备 = 6 个子类;每加一种遥控器或设备,子类数量乘积增长(类爆炸)。桥接后,两个维度独立继承,组合即可,符合合成复用原则。
3.3 组合模式(Composite)结构型
意图 :将对象组合成树形结构以表示"部分-整体"的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
适用场景 :表示对象的部分-整体层次结构;希望客户端忽略组合对象与单个对象的不同。
生活类比 :文件系统——文件夹可包含文件和子文件夹,统一用"打开/删除"操作;组织架构树;菜单树。
«interface» Component
+ operation(): void
+ add(Component)
File (Leaf)
+ operation()
- add() 抛异常
Folder (Composite)
- children: List
+ operation()
+ add(Component)
children 0..*
客户端统一调用 component.operation()
叶子节点直接执行,组合节点递归调用所有子节点
透明式 vs 安全式:add 放 Component(透明)还是 Composite(安全)
图 9:组合模式 UML 类图(文件系统树示例)
3.4 装饰器模式(Decorator)结构型
意图 :动态地给一个对象添加一些额外的职责。就增加功能来说,装饰器模式相比生成子类更加灵活。
适用场景 :在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责;当不能采用生成子类的方法进行扩充时。
生活类比 :咖啡点单——基础咖啡(Espresso),可加牛奶、糖、奶油等装饰,层层叠加价格与口味;手机贴膜、加壳。
«interface» Coffee
+ cost(): double
Espresso
+ cost(): 15.0
Decorator
- coffee: Coffee
+ cost(): double
MilkDecorator
+ cost(): +3
SugarDecorator
+ cost(): +1
decorates
装饰器同类型包装,逐层委托 cost()
Java I/O 流是经典应用:InputStream → BufferedInputStream → DataInputStream
图 10:装饰器模式 UML 类图(咖啡点单示例)
public interface Coffee { double cost(); }
public class Espresso implements Coffee {
public double cost() { return 15.0; }
}
public abstract class Decorator implements Coffee {
protected Coffee coffee;
public Decorator(Coffee c) { this .coffee = c; }
}
public class MilkDecorator extends Decorator {
public MilkDecorator(Coffee c) { super (c); }
public double cost() { return coffee.cost() + 3.0; } // 委托+加价
}
// 使用:层层包装
Coffee c = new MilkDecorator(new SugarDecorator(new Espresso()));
// cost = 15 + 1 + 3 = 19
3.5 外观模式(Facade)结构型
意图 :为子系统中的一组接口提供一个一致的界面(入口),外观模式定义了一个高层接口,这个接口使得这一子系统更容易使用。
适用场景 :为复杂子系统提供简单接口;客户程序与抽象类的实现部分之间存在着很大的依赖性;需要分层子系统时。
生活类比 :家庭影院外观——一键"看电影"按钮,外观内部依次开启投影仪、降下幕布、开音响、调暗灯光、播放影片,用户无需逐一操作各子系统。
HomeTheaterFacade
+ watchMovie(): void
Client
简化调用
Projector
+ on()/off()
AudioSystem
+ setVolume()
Lights
+ dim()
Screen
+ down()
外观封装子系统,提供统一入口,客户端无需了解子系统细节
图 11:外观模式 UML 类图(家庭影院外观示例)
外观模式要点 :不是封装/隐藏子系统,而是提供简化入口 。子系统仍可直接访问(外观不限制)。常用于分层架构层间入口、SDK/API 简化。外观模式符合"迪米特法则(最少知道原则)"。
3.6 享元模式(Flyweight)结构型
意图 :运用共享技术有效地支持大量细粒度的对象。
适用场景 :一个应用程序使用了大量的对象;由于大量对象造成很大存储开销;对象的大多数状态可以变为外部状态。
生活类比 :文本编辑器中的字符——'a' 出现一万次不必创建一万个对象,共享同一个 'a' 享元,仅位置、颜色等外部状态不同;围棋棋子(只有黑白两种);线程池、连接池。
«interface» Flyweight
+ operation(extrinsicState)
CharacterFlyweight
- symbol: char (内在状态)
+ operation(font,size)
外部状态由参数传入
FlyweightFactory
- pool: Map
+ getFlyweight(key)
+ size(): int
manage
Client
持有外部状态
内在状态 vs 外部状态(核心考点)
内在状态(intrinsic):共享的、不可变的、存储在享元内部(如字符 'a')
外部状态(extrinsic):可变的、由客户端传入、不共享(如位置、字体、颜色)
图 12:享元模式 UML 类图(文本编辑器字符示例)
3.7 代理模式(Proxy)结构型
意图 :为其他对象提供一种代理以控制对这个对象的访问。
适用场景 :远程代理(为不同地址空间的对象提供局部代表)、虚拟代理(延迟创建开销大的对象)、保护代理(控制访问权限)、智能引用代理(访问时附加操作,如计数)、缓存代理。
生活类比 :明星经纪人——记者(客户端)不能直接采访明星(真实对象),需通过经纪人(代理)安排;银行柜员代办。
«interface» Subject
+ request(): void
RealSubject
+ request()
Proxy
- realSubject: RealSubject
+ request() // 前置/后置处理
委托
Client
代理的常见类型(软考考点)
远程代理:代表不同地址空间对象(RPC Stub)
虚拟代理:延迟加载开销大的对象(懒加载图片)
保护代理:控制访问权限(权限校验)| 智能引用代理:附加计数/缓存
图 13:代理模式 UML 类图(含三种代理类型说明)
// 虚拟代理:延迟加载大图片
public interface Image { void display(); }
public class RealImage implements Image {
private String file;
public RealImage(String f) {
this .file = f;
loadFromDisk(); // 构造时加载,开销大
}
private void loadFromDisk() { System.out.println("加载 " + file); }
public void display() { System.out.println("显示 " + file); }
}
public class ProxyImage implements Image {
private RealImage real;
private String file;
public ProxyImage(String f) { this .file = f; }
public void display() {
if (real == null ) real = new RealImage(file); // 延迟到真正需要时
real.display();
}
}
四、行为型模式(11 种)详解
行为型模式关注对象之间的职责分配与算法交互 ,描述对象之间如何协作、如何分配职责、如何通信。是 GoF 中数量最多的一类(11 种)。
4.1 责任链模式(Chain of Responsibility)行为型
意图 :使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递请求,直到有一个对象处理它为止。
适用场景 :有多个对象可以处理一个请求,哪个对象处理由运行时刻决定;想在不明确指定接收者的情况下向多个对象中的一个提交请求。
生活类比 :审批工作流——请假 1 天组长审批,3 天经理审批,7 天总监审批,30 天总经理审批。请求沿链传递直到被处理;Servlet Filter 链;事件冒泡。
«abstract» Handler
- successor: Handler
+ setNext(Handler)
+ handleRequest(req)
next
TeamLeader
+ handleRequest()
Manager
+ handleRequest()
Director
+ handleRequest()
GeneralManager
+ handleRequest()
请求沿链传递,直到被处理或到达链尾
图 14:责任链模式 UML 类图(审批工作流示例)
4.2 命令模式(Command)行为型
意图 :将一个请求封装为一个对象,从而使得可以用不同的请求对客户进行参数化;对请求排队或记录请求日志,以及支持可撤销的操作。
适用场景 :需要抽象出待执行的动作以参数化某对象;需要在不同的时间指定请求、排队请求、执行请求;需要支持撤销/重做。
生活类比 :遥控器按钮——每个按钮封装一个命令对象(开灯、关灯、调台),遥控器(调用者)不直接操作电器(接收者),按钮可撤销、可排队。
RemoteControl
+ setCommand(cmd)
«interface» Command
+ execute(): void
+ undo(): void
holds
LightOnCommand
- light: Light
+ execute() / undo()
Light (Receiver)
+ turnOn()
+ turnOff()
调用
请求封装为对象,支持撤销、排队、日志
图 15:命令模式 UML 类图(遥控器示例)
// 命令模式:遥控器与灯
public interface Command {
void execute();
void undo();
}
public class LightOnCommand implements Command {
private Light light;
public LightOnCommand(Light l) { this .light = l; }
public void execute() { light.turnOn(); }
public void undo() { light.turnOff(); }
}
public class RemoteControl { // 调用者
private Command command;
public void setCommand(Command c) { this .command = c; }
public void pressButton() { command.execute(); }
}
4.3 解释器模式(Interpreter)行为型
意图 :给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。
适用场景 :当有一个语言需要解释执行,并且可将该语言中的句子表示为一个抽象语法树时;如正则表达式、SQL 解析、规则引擎、配置文件解析。
生活类比 :规则引擎——"满 100 减 20 且会员打 9 折"被解析为一棵表达式树(And 节点 = 满减节点 + 折扣节点),递归求值。
«abstract» Expression
+ interpret(ctx): Object
TerminalExpr
+ interpret(ctx)
NonTerminalExpr (And/Or)
- left, right: Expression
+ interpret(ctx)
0..*
Context
变量/符号表
递归解释抽象语法树,终结符 + 非终结符
类模式,编译时确定;规则引擎、SQL 解析、正则引擎常用
图 16:解释器模式 UML 类图(规则引擎示例)
4.4 迭代器模式(Iterator)行为型
意图 :提供一种方法顺序访问一个聚合对象中各个元素,而又不暴露该对象的内部表示。
适用场景 :访问一个聚合对象的内容而无须暴露它的内部表示;需要为遍历不同的聚合结构提供统一的接口(多态迭代)。
生活类比 :电视遥控器换台——按"下一个"顺序浏览所有频道,无需知道频道列表如何存储;Java 的 foreach/Iterator。
«interface» Iterator
+ hasNext(): boolean
+ next(): Object
ListIterator
+ hasNext() / next()
«interface» Aggregate
+ createIterator(): Iterator
ListAggregate
+ createIterator()
creates
holds
分离聚合对象的遍历行为,统一访问接口
图 17:迭代器模式 UML 类图(集合遍历示例)
意图 :用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互。
适用场景 :一组对象以定义良好但是复杂的方式进行通信,产生的相互依赖关系结构混乱且难以理解;一个对象引用其他很多对象并且直接与这些对象通信,导致难以复用该对象。
生活类比 :聊天室——用户之间不直接通信,都通过聊天室(中介者)转发消息;机场塔台调度航班;MVC 中 Controller 是 Model 与 View 的中介。
«interface» Mediator
+ notify(sender, event)
ChatRoom
+ notify(sender, event)
«abstract» Colleague
- mediator: Mediator
+ send(msg)
UserA
UserB
UserC
网状交互 → 星型交互,降低耦合
图 18:中介者模式 UML 类图(聊天室示例)
中介者 vs 观察者(高频对比)
中介者将对象间的多对多 网状通信集中到中介者,变为星型 ,强调"集中控制";观察者是一对多 的广播订阅,强调"发布-订阅"。两者可结合:中介者内部用观察者通知各同事。
4.6 备忘录模式(Memento)行为型
意图 :在不破坏封装性的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便以后当需要时能将该对象恢复到原先保存的状态。
适用场景 :必须保存一个对象在某个时刻的(部分)状态,以便以后恢复;直接通过接口获取状态会暴露实现细节破坏封装性。如文本编辑器撤销、游戏存档、事务回滚。
生活类比 :文本编辑器撤销——每次编辑前保存文档快照(备忘录),按 Ctrl+Z 恢复到上一个快照;游戏存档读档。
Originator
- state: String
+ createMemento()
+ restore(m)
Memento
- state: String
+ getState()
Caretaker
- stack: List
+ save(m)
+ undo(): Memento
creates
holds
三角色:原发器 + 备忘录 + 管理者
Originator 创建/恢复,Caretaker 仅保管不读取内容(窄接口 vs 宽接口)
保证封装性:外部无法直接访问对象内部状态
图 19:备忘录模式 UML 类图(文本编辑器撤销示例)
4.7 观察者模式(Observer)行为型
意图 :定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
适用场景 :当一个抽象模型有两个方面,其中一个方面依赖于另一个方面;对一个对象的改变将同时改变其他对象,而不知道具体有多少对象有待改变;MVC 中 Model 与 View 的关系。
生活类比 :股票行情——股民(观察者)订阅某只股票(主题),股价变动时自动通知所有订阅者;微信公众号推送;事件监听器。
«interface» Subject
+ attach(o)
+ detach(o)
+ notify()
Stock (Subject)
- price: double
+ setPrice()
«interface» Observer
+ update(subject)
Investor
+ update(subject)
observers 0..*
notify
发布-订阅:主题状态变化自动通知所有观察者
图 20:观察者模式 UML 类图(股票行情示例)
// 观察者模式:股票行情
public interface Observer { void update(Stock s); }
public class Stock { // Subject
private List<Observer> observers = new ArrayList<>();
private double price;
public void attach(Observer o) { observers.add(o); }
public void setPrice(double p) {
this .price = p;
notifyAll(); // 状态变化自动通知
}
private void notifyAll() {
for (Observer o : observers) o.update(this );
}
}
public class Investor implements Observer {
public void update(Stock s) {
System.out.println("股价更新: " + s.getPrice());
}
}
4.8 状态模式(State)行为型
意图 :允许一个对象在其内部状态改变时改变它的行为。对象看起来似乎修改了它的类。
适用场景 :一个对象的行为取决于它的状态,并且它必须在运行时刻根据状态改变它的行为;一个操作中含有庞大的多分支的条件语句,且这些分支依赖于该对象的状态。
生活类比 :自动售货机——投币状态、选择商品状态、出货状态、退币状态。同一"投入硬币"动作在不同状态下行为不同;TCP 连接状态(LISTEN/ESTABLISHED/CLOSED)。
VendingMachine
- state: State
+ setState(s)
+ insertCoin() / dispense()
«interface» State
+ insertCoin(ctx)
+ dispense(ctx)
state 1
CoinState
SoldState
IdleState
用状态对象替代庞大的 if-else / switch
状态转移由状态对象自身触发,符合开闭原则
图 21:状态模式 UML 类图(自动售货机示例)
状态模式 vs 策略模式(超高频考点)
两者结构几乎相同(都持有策略/状态对象委托执行),但意图不同 :
策略模式:客户端主动选择 算法,策略对象通常无状态 ,相互独立。
状态模式:对象自身状态驱动 行为变化,状态对象可触发状态转移,状态间有关联 。策略是"用哪个算法",状态是"现在是什么状态"。
4.9 策略模式(Strategy)行为型
意图 :定义一系列的算法,把它们一个个封装起来,并且使它们可相互替换。本模式使得算法可独立于使用它的客户而变化。
适用场景 :许多相关的类仅仅是行为有异;需要使用一个算法的不同变体;算法使用客户不应该知道的数据。
生活类比 :支付方式——下单时选择支付宝、微信、银行卡,每种支付是一个策略,可互换;出行方式选择(打车/地铁/步行);排序算法选择。
Order (Context)
- strategy: PayStrategy
+ setStrategy(s)
+ checkout()
«interface» PayStrategy
+ pay(amount): boolean
strategy
AlipayStrategy
WechatStrategy
CardStrategy
封装可互换的算法族,消除条件判断语句
图 22:策略模式 UML 类图(支付方式示例)
// 策略模式:支付方式
public interface PayStrategy { boolean pay(double amount); }
public class AlipayStrategy implements PayStrategy {
public boolean pay(double a) { System.out.println("支付宝支付 " + a); return true ; }
}
public class Order {
private PayStrategy strategy;
public void setStrategy(PayStrategy s) { this .strategy = s; }
public void checkout(double amt) { strategy.pay(amt); }
}
// 客户端根据用户选择切换策略
order.setStrategy(new AlipayStrategy());
order.checkout(99.9);
4.10 模板方法模式(Template Method)行为型
意图 :定义一个操作中算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
适用场景 :一次性实现一个算法的不变部分,并将可变的行为留给子类来实现;各子类中公共行为应被提取出来集中到公共父类中以避免代码重复。
生活类比 :烹饪食谱——做菜流程固定(备料→热锅→下菜→调味→出锅),但每道菜的"调味"步骤不同;Spring 框架的 JdbcTemplate。
«abstract» CookRecipe
+ cook() { // 模板方法(final)
prepare(); heatOil(); addDish();
season(); plate(); }
# season() // 抽象方法
# plate() // 钩子(可选)
TomatoEgg
# season() 加盐糖
KungPaoChicken
# season() 加辣椒
SweetSourPork
# season() 加醋糖
父类定义算法骨架,子类实现可变步骤(类行为型)
图 23:模板方法模式 UML 类图(烹饪食谱示例)
模板方法要点 :模板方法通常声明为 final,防止子类覆盖算法骨架。子类只重写"钩子"和抽象步骤。这是类行为型模式 (基于继承),与策略模式(对象行为型,基于组合)形成对比。钩子方法(hook)是可选覆盖的默认实现方法。
4.11 访问者模式(Visitor)行为型
意图 :表示一个作用于某对象结构中的各元素的操作。它使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
适用场景 :一个对象结构包含很多类型的对象,希望对这些对象实施一些依赖于其具体类型的操作;需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作"污染"这些对象的类。如编译器 AST 遍历、文档导出(XML/HTML/PDF)。
生活类比 :编译器——抽象语法树有 If/While/Assign 等节点,"类型检查""代码生成""格式化"是不同访问者,节点类不变即可扩展新访问者。
«interface» Visitor
+ visit(IfNode n)
+ visit(WhileNode n)
+ visit(AssignNode n)
CodeGenVisitor
+ visit(各节点)
«interface» Node (Element)
+ accept(v: Visitor)
{ v.visit(this); } // 双重分派
IfNode
WhileNode
accept/visit
双重分派:新增操作易,新增元素类型难
对"操作频繁变化、元素结构稳定"的场景适用(违反依赖倒置)
图 24:访问者模式 UML 类图(编译器 AST 示例)
访问者模式权衡 :优点是易于新增操作(新增 Visitor 即可,不改元素类);缺点是新增元素类型困难 (要改所有 Visitor 接口及实现),且元素必须暴露内部细节给访问者,违反封装性。适用于"元素结构稳定、操作多变"的场景。
五、模式对比与选择
许多设计模式在结构上相似,但意图与应用场景不同。掌握它们的区别与选择依据 是软考论述题和案例分析的高频考点。
5.1 易混淆模式对比
对比项
模式 A
模式 B
核心区别
策略 vs 状态
策略模式
状态模式
策略:客户端主动选算法,策略相互独立无状态;状态:对象状态驱动行为,状态间可转移
观察者 vs 中介者
观察者模式
中介者模式
观察者:一对多广播,发布-订阅;中介者:多对多集中到中介者,星型交互
工厂方法 vs 抽象工厂
工厂方法
抽象工厂
工厂方法:单一产品等级,一个方法产一种产品;抽象工厂:产品族,多个方法产一族相关产品
代理 vs 装饰器 vs 适配器
代理 / 装饰器
适配器
代理:控制访问,接口不变;装饰器:增强功能,接口不变;适配器:转换接口,接口改变
装饰器 vs 代理
装饰器
代理
装饰器:客户端主动组装,关注增加职责;代理:客户端透明,关注访问控制/延迟
外观 vs 代理
外观
代理
外观:简化子系统入口,一对多;代理:代表单一对象,一对一
组合 vs 装饰器
组合
装饰器
组合:树形结构聚合同类;装饰器:链式包装增强单一对象
桥接 vs 策略
桥接
策略
桥接:分离两个独立变化维度(结构型);策略:替换算法(行为型)
命令 vs 策略
命令
策略
命令:封装请求为对象,支持撤销/排队;策略:封装可互换算法
原型 vs 单例
原型
单例
原型:通过克隆产生多个新实例;单例:保证唯一实例
代理 / 装饰器 / 适配器 三者关键区别(必考)
三者结构极其相似(都持有目标对象引用并实现同一接口),但意图 不同:
• 适配器 :目的是接口转换 ,使不兼容接口能协作,接口会改变 。
• 装饰器 :目的是增加职责 ,不改接口,客户端主动组装。
• 代理 :目的是控制访问 (远程/虚拟/保护),不改接口,客户端透明使用。
5.2 模式选择决策树
设计问题属于哪一类?
(创建对象 / 组合结构 / 对象协作)
创建型
结构型
行为型
唯一实例?
→ 单例
克隆已有对象?
→ 原型
分步构建复杂对象?
→ 建造者
单一产品 / 产品族?
→ 工厂方法 / 抽象工厂
接口不兼容?
→ 适配器
控制访问 / 延迟加载?
→ 代理
动态增加职责?
→ 装饰器
树形部分-整体?
→ 组合
两维度独立变化?
→ 桥接
简化子系统入口?
→ 外观
大量细粒度对象共享?
→ 享元
一对多通知?
→ 观察者
状态驱动行为?
→ 状态
可互换算法?
→ 策略
链式处理请求?
→ 责任链
封装请求/撤销?
→ 命令
算法骨架可定制?
→ 模板方法
多对象集中交互?
→ 中介者
顺序遍历聚合?
→ 迭代器
保存/恢复状态?
→ 备忘录
决策树仅作参考,实际需结合场景权衡
图 25:设计模式选择决策树
六、设计模式实战案例:电商平台
真实系统中往往不是单一模式的应用,而是多种模式协同工作。下面以一个典型电商平台为例,展示设计模式如何落地。
电商平台模式应用全景
表现层 / 接入
外观 Facade
责任链(过滤器)
适配器(第三方登录)
代理(限流/鉴权/缓存)
业务层 / 领域服务
策略(促销/折扣)
策略(支付方式)
状态(订单状态机)
观察者(库存/通知)
中介者(多服务协作)
创建 / 构建
单例(配置/日志)
工厂方法(商品)
抽象工厂(多端UI)
建造者(订单)
原型(商品模板复制)
结构 / 复用
组合(分类树)
装饰器(优惠券叠加)
桥接(支付渠道+渠道)
享元(SKU/图片缓存)
命令(下单/取消/退款)
同一系统协同应用 15+ 种模式,遵循 SOLID 与高内聚低耦合
图 26:电商平台中设计模式应用全景图
以"下单流程"为例串讲多种模式协作:
下单流程的模式协作
责任链 :请求先经过鉴权过滤器 → 限流过滤器 → 参数校验过滤器,逐级处理。
代理 :商品服务调用走 RPC 代理(远程代理),带缓存代理避免重复查库。
外观 :OrderFacade 统一封装库存、优惠券、支付、积分多个子系统,前端只调一个接口。
策略 :根据用户等级选择折扣策略;根据支付选择支付策略。
状态 :订单状态机(待支付→已支付→已发货→已完成/已取消)驱动行为。
观察者 :下单成功后通知库存扣减、发送短信、推送积分系统。
命令 :下单、取消、退款封装为命令对象,支持事务回滚与异步队列。
装饰器 :优惠券、满减、会员折扣层层装饰最终价格。
建造者 :复杂订单对象分步构建(商品、地址、优惠、支付)。
实战忠告 :不要为了用模式而用模式。模式是解决重复问题的经验 ,只有当问题真正出现、且简单方案难以应对时才引入。过度使用模式会增加系统复杂度,违反 KISS 原则。
七、设计原则
设计模式是"术",设计原则是"道"。设计模式背后都遵循若干面向对象设计原则,理解原则才能灵活运用甚至创造模式。其中最核心的是 SOLID 五大原则 。
7.1 SOLID 原则
SOLID 五大原则
S - SRP
单一职责
一个类只应有
一个变化的
原因
O - OCP
开闭原则
对扩展开放
对修改关闭
(抽象+多态)
L - LSP
里氏替换
子类必须能完
全替换父类而
不破坏行为
I - ISP
接口隔离
客户端不应依赖
它不需要的接口
(小接口)
D - DIP
依赖倒置
依赖抽象而非
具体实现
(面向接口)
SOLID 是设计模式的"基因"
开闭原则 OCP 是核心目标,其余四原则都是为达成 OCP 的手段
工厂/策略/状态/装饰器/代理等模式都是 OCP 与 DIP 的典型应用
软考高频:DIP 体现为"高层不依赖低层,都依赖抽象"
图 27:SOLID 五大设计原则
原则
全称
核心含义
对应模式举例
SRP
Single Responsibility Principle
一个类应该只有一个引起它变化的原因(职责单一)
外观、代理分离职责
OCP
Open-Closed Principle
软件实体应对扩展开放,对修改关闭(通过抽象与多态实现)
策略、工厂方法、观察者、装饰器
LSP
Liskov Substitution Principle
所有引用父类的地方必须能透明地使用子类对象,子类不破坏父类行为约定
组合、装饰器、代理的接口一致性
ISP
Interface Segregation Principle
客户端不应被迫依赖它不使用的方法,接口要小而专
适配器、外观拆分接口
DIP
Dependency Inversion Principle
高层模块不依赖低层模块,二者都依赖抽象;抽象不依赖细节,细节依赖抽象
工厂方法、策略、桥接、观察者
7.2 其他重要设计原则
DRY — Don't Repeat Yourself(不要重复自己)
系统中的每一项知识/逻辑都应只有唯一、无歧义、权威 的表示。避免代码重复,重复会带来维护灾难(改一处漏多处)。通过提取公共方法、基类、工具类消除重复。
KISS — Keep It Simple, Stupid(保持简单)
简单是设计的首要目标。不要过度设计,能用简单方案解决的就不要引入复杂模式。复杂度是软件的头号敌人。
YAGNI — You Aren't Gonna Need It(你不会需要它)
不要为"将来可能用到"的功能提前设计/实现。只实现当前真正需要的功能,遵循敏捷思想,避免无用代码带来的维护负担。
LoD — Law of Demeter(迪米特法则 / 最少知道原则)
一个对象应该对其他对象保持最少的了解。只与"直接朋友"通信(成员、参数、返回值),不要与"朋友的朋友"通信(避免 a.b.c.method() 链式调用)。外观模式、中介者模式 是迪米特法则的典型应用。
CRP / CARP — 合成复用原则(Composition/Aggregate Reuse Principle)
优先使用对象的组合/聚合 ,而不是类的继承 来达到复用目的。继承是"is-a"强耦合,组合是"has-a"松耦合。桥接、策略、装饰器、代理都体现了"多用组合少用继承"。
软考高频:合成复用原则 vs 继承复用
继承复用:实现简单,但破坏封装(子类可见父类细节)、耦合度高(父类变化影响所有子类)、不支持运行时改变。
组合复用:不破坏封装、耦合度低、可在运行时动态切换、符合 DIP。因此"多用组合,少用继承" 是面向对象设计的金科玉律。
八、软考考点总结与真题
设计模式是系统架构设计师考试的必考核心知识点 ,在上午综合知识、下午案例分析、论文中均会出现。下面梳理高频考点与真题。
8.1 高频考点速览
考点
考查要点
频次
三大分类
创建型 5 / 结构型 7 / 行为型 11 的归属
★★★★★
类模式 vs 对象模式
类模式(工厂方法、类适配器、解释器、模板方法)4 种
★★★★
策略 vs 状态
意图区别:客户端选算法 vs 状态驱动行为
★★★★★
代理 / 装饰器 / 适配器
三者结构相似、意图不同(控制/增强/转换接口)
★★★★★
工厂方法 vs 抽象工厂
单一产品 vs 产品族
★★★★
单例线程安全
双重检查锁定的 volatile、静态内部类
★★★
观察者 vs 中介者
一对多广播 vs 多对多集中
★★★★
SOLID 原则
五原则含义、DIP 依赖倒置
★★★★★
合成复用原则
多用组合少用继承
★★★★
模式与场景匹配
给定场景选合适模式(案例分析)
★★★★★
8.2 软考真题
【真题 1】创建型模式辨析(综合知识)
某软件公司要开发一个图形界面组件库,需要支持 Windows、macOS、Linux 三种操作系统风格,且每种风格下都要有按钮、文本框、滚动条三种组件。为使组件创建与使用解耦,最合适采用的设计模式是( )。
A. 工厂方法模式 B. 抽象工厂模式 C. 建造者模式 D. 原型模式
答案:B
解析:本题存在多个产品等级 (按钮、文本框、滚动条)和多个产品族 (Windows、macOS、Linux 风格),是典型的"产品族"场景,应使用抽象工厂模式 。每个具体工厂(WinFactory、MacFactory、LinuxFactory)负责创建一族风格一致的组件。工厂方法只处理单一产品等级;建造者用于分步构建复杂对象;原型用于克隆已有对象,均不符合。
【真题 2】结构型模式辨析(综合知识)
某系统需要访问一个第三方库提供的类,但该类的接口与系统期望的接口不兼容。为使两者能协同工作,且不修改第三方库源码,应采用( );若希望在不改变接口的前提下,为已有对象动态增加日志功能,应采用( );若要为远程对象提供本地代表以控制访问,应采用( )。
① 适配器模式 ② 装饰器模式 ③ 代理模式 ④ 外观模式
答案:① 适配器模式;② 装饰器模式;③ 代理模式
解析:
• 接口不兼容、需转换接口 → 适配器 (接口会改变)。
• 不改接口、动态增加职责(日志)→ 装饰器 (接口不变,增强功能)。
• 为远程对象提供本地代表、控制访问 → 代理 (远程代理)。
三者结构相似(都持有目标引用),但意图 不同:适配器转换接口、装饰器增强职责、代理控制访问,是软考超高频对比点。
【真题 3】行为型模式辨析(案例分析)
某电商系统订单处理流程中,订单会经历"待支付→已支付→已发货→已完成→已关闭"等状态,同一操作(如"支付")在不同状态下行为不同。例如待支付状态下可支付,已支付状态下再次支付应提示"已支付"。开发人员最初用大量 if-else 判断状态,导致代码臃肿、难以维护。请回答:
(1)最合适采用哪种设计模式重构?说明理由。
(2)该模式与策略模式在意图上有何区别?
参考答案:
(1)应采用状态模式 。理由:订单的行为取决于其内部状态 ,且状态会转移,用状态对象封装每个状态下的行为,可消除庞大的 if-else/switch 分支,新增状态只需新增状态类,符合开闭原则。Context(订单)持有当前状态对象,将状态相关操作委托给状态对象执行。
(2)状态模式与策略模式结构相似,但意图不同:策略模式 是客户端主动选择 算法,策略对象相互独立、通常无状态、不发生转移;状态模式 是由对象自身状态驱动 行为变化,状态间存在转移关系,状态对象可在执行后触发状态切换。本题中订单状态会自动流转(支付后从待支付→已支付),属于状态驱动,故选状态模式而非策略模式。
8.3 23 种模式记忆技巧
分类数字记忆
"创五结七行十一":创建型 5、结构型 7、行为型 11,合计 23。
创建型口诀:单工抽建原
单(单例)—工(工厂方法)—抽(抽象工厂)—建(建造者)—原(原型)
记忆:单独工厂抽建原型(一个人在工厂里抽空建造原型机)
结构型口诀:适桥组装外享代
适(适配器)—桥(桥接)—组(组合)—装(装饰器)—外(外观)—享(享元)—代(代理)
记忆:过桥时适配组装好外观,享受代理服务
行为型口诀:责命解迭中备观状策模访
责(责任链)—命(命令)—解(解释器)—迭(迭代器)—中(中介者)—备(备忘录)—观(观察者)—状(状态)—策(策略)—模(模板方法)—访(访问者)
记忆:责任命令解释迭代,中介备忘观察状态,策略模板访问
易错点提醒
1. 适配器有类适配器和对象适配器两种 ,类适配器用继承(Java 不支持多继承类),对象适配器用组合(推荐)。
2. 类模式只有 4 种 :工厂方法、(类)适配器、解释器、模板方法,其余均为对象模式。
3. 单例的 volatile 必须加,防止指令重排序;枚举单例是最佳实践。
4. 抽象工厂新增产品族容易,新增产品等级难 (违反 OCP)。
5. 享元的关键是分离内在状态(共享)与外部状态(不共享) 。
6. 访问者用双重分派 ,新增操作易、新增元素类型难。
7. 模板方法是类行为型 (继承),策略是对象行为型(组合)。
结语
设计模式是软件工程领域沉淀的智慧结晶 ,是架构师必备的工具箱。23 种 GoF 模式按创建型、结构型、行为型三大类组织,每种模式都遵循 SOLID 等面向对象设计原则。掌握设计模式的关键不在于死记结构,而在于理解每个模式解决的问题、适用的场景、带来的权衡 ,以及它们之间的联系与区别。
备考复习建议
1. 分类记忆 :创五结七行十一,类模式 4 种要分清。
2. 意图对比 :策略/状态、观察者/中介者、代理/装饰器/适配器、工厂方法/抽象工厂。
3. 原则关联 :每个模式对应哪些 SOLID 原则。
4. 场景匹配 :能根据案例描述快速选出合适模式(下午题重点)。
5. 代码理解 :重点模式(单例、工厂、策略、观察者、代理)能写出核心代码。
6. 论文准备 :结合实际项目阐述模式应用与权衡。
设计模式不是银弹,过度使用反而增加复杂度。真正的高手懂得在恰当的场景用恰当的模式 ,并在简单方案足够时保持克制。正如 GoF 所言:"模式是经过验证的解决方案,但理解何时不用模式同样重要。"