GoF 23 种设计模式:面向对象软件设计精髓
设计模式 GoF 23 种:面向对象软件设计精髓
什么是设计模式?
设计模式(Design Patterns)是面向对象软件开发中反复出现的问题的通用可复用解决方案。它们就像建筑蓝图,不是可直接照抄的代码,而是指导你搭建结构、分配职责、降低耦合的模板。GoF(Gang of Four,四人组)在1994年出版的《设计模式:可复用面向对象软件的基础》中提出了23个经典模式,至今仍是工程师理解架构与设计思想的核心根基。
设计模式分类
23 种模式按用途划分为三大类:
- 创建型模式:关注对象创建机制,力求创建过程更灵活、可复用。
- 结构型模式:将类或对象组合成更大的结构,确保结构灵活、高效。
- 行为型模式:专注于对象间的职责分配和通信协作。
一、创建型模式(5 种)
1. 单例模式
确保一个类只有一个实例,并提供全局访问点。
适用场景:全局配置、数据库连接池、日志管理器、线程池等需要唯一实例的场景。
结构示例(饿汉式):
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {}
public static Singleton getInstance() { return INSTANCE; }
}
要点:私有构造、静态实例、公有访问方法。多线程下可考虑双检锁或静态内部类。
2. 工厂方法模式
定义一个创建对象的接口,但让子类决定实例化哪个类。工厂方法让类的实例化推迟到子类。
适用场景:当一个类无法预知它将创建何种对象的类时,或希望子类指定创建对象。
结构代码:
interface Product { void use(); }
class ConcreteProduct implements Product { ... }
abstract class Creator {
abstract Product factoryMethod();
void someOperation() { Product p = factoryMethod(); p.use(); }
}
class ConcreteCreator extends Creator {
Product factoryMethod() { return new ConcreteProduct(); }
}
3. 抽象工厂模式
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
适用场景:系统需要兼容多种系列产品(如跨平台 GUI 组件,不同主题的按钮/文本框),并且希望保持一致性。
结构示例:
interface GUIFactory { Button createButton(); TextField createTextField(); }
class WinFactory implements GUIFactory { ... } // 返回WinButton, WinTextField
class MacFactory implements GUIFactory { ... } // 返回MacButton, MacTextField
4. 建造者模式
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
适用场景:建造一个包含多个组成部分的对象(如电脑组装、套餐组合),步骤固定但部件可变。
结构:
class Product { /* 多个部分 */ }
abstract class Builder {
abstract void buildPartA();
abstract void buildPartB();
abstract Product getResult();
}
class ConcreteBuilder extends Builder { ... }
class Director {
void construct(Builder b) { b.buildPartA(); b.buildPartB(); }
}
5. 原型模式
用原型实例指定要创建对象的种类,并通过拷贝这些原型创建新对象。
适用场景:创建成本高(如复杂初始化、网络资源获取)的对象,或需要预置对象状态的情况。
实现:Java 中实现 Cloneable 接口并重写 clone() 方法,或使用序列化深拷贝。
二、结构型模式(7 种)
6. 适配器模式
将一个类的接口转换成客户希望的另一个接口,使原本因接口不匹配而无法合作的类能一起工作。
适用场景:整合遗留系统、复用第三方库、旧接口与新系统对接。
示例(对象适配器):
interface Target { void request(); }
class Adaptee { void specificRequest() {...} }
class Adapter implements Target {
private Adaptee adaptee;
Adapter(Adaptee a) { this.adaptee = a; }
public void request() { adaptee.specificRequest(); }
}
7. 桥接模式
将抽象部分与它的实现部分分离,使它们都可以独立变化。
适用场景:多种平台、多种维度的变化(如不同设备 + 不同遥控器),避免类爆炸。
结构:抽象类包含实现接口的引用,通过组合实现多维度扩展。
interface Device { void on(); void off(); }
class TV implements Device { ... }
class Radio implements Device { ... }
abstract class RemoteControl { protected Device dev; ... }
class BasicRemote extends RemoteControl { ... }
8. 组合模式
将对象组合成树形结构以表示“部分-整体”的层次结构,使得用户对单个对象和组合对象的使用具有一致性。
适用场景:文件系统、GUI 容器与组件、组织结构等树形层次结构。
核心角色:Component(抽象类)、Leaf(叶结点)、Composite(组合结点),三者共享相同操作接口。
9. 装饰模式
动态地给对象添加一些额外的职责,相比生成子类更灵活。
适用场景:在不影响其他对象的前提下,动态、透明地给单个对象增加功能(如 I/O 流:BufferedInputStream 装饰 InputStream)。
interface Component { void operation(); }
class ConcreteComponent implements Component { ... }
abstract class Decorator implements Component {
protected Component component;
Decorator(Component c) { this.component = c; }
public void operation() { component.operation(); }
}
class ConcreteDecoratorA extends Decorator {
public void operation() { super.operation(); addedBehavior(); }
}
10. 外观模式
为子系统中的一组接口提供一个一致的界面,简化接口使用。
适用场景:为复杂子系统提供简单入口(如编译器外观隐藏词法、语法、代码生成等子系统),或对遗留系统进行封装。
示例:
class CPU { void start(){} }
class Memory { void load(){} }
class HardDrive { void read(){} }
class ComputerFacade {
private CPU cpu = new CPU();
private Memory mem = new Memory();
private HardDrive hd = new HardDrive();
void startComputer() { cpu.start(); mem.load(); hd.read(); }
}
11. 享元模式
运用共享技术有效地支持大量细粒度的对象。
适用场景:系统存在大量相似对象,且对象的大部分状态可变为外部状态(如文字处理中的字符对象、游戏中的树木、子弹等)。
结构:享元工厂维护对象池,内蕴状态共享,外蕴状态由客户端传入。
class Glyph { char c; ... } // 可共享
class GlyphFactory {
Map<Character, Glyph> pool = new HashMap<>();
Glyph getGlyph(char c) { ... }
}
12. 代理模式
为其他对象提供一种代理以控制对这个对象的访问。
适用场景:安全控制、延迟加载(虚拟代理)、远程代理(RMI)、日志记录等。
分类:
- 远程代理:为不同地址空间的对象提供本地代表。
- 虚拟代理:按需创建开销大的对象。
- 保护代理:控制对原始对象的访问权限。
interface Subject { void request(); }
class RealSubject implements Subject { ... }
class Proxy implements Subject {
private RealSubject real;
public void request() { if(real==null) real=new RealSubject(); real.request(); }
}
三、行为型模式(11 种)
13. 责任链模式
使多个对象都有机会处理请求,避免请求发送者与接受者耦合。将这些对象连成一条链,并沿着链传递请求,直到有一个对象处理它。
适用场景:审批流程、异常处理链、事件冒泡、日志级别处理。
abstract class Handler {
protected Handler next;
void setNext(Handler h) { next = h; }
abstract void handleRequest(Request req);
}
class ConcreteHandlerA extends Handler {
void handleRequest(Request req) { if(可处理) {...} else if(next!=null) next.handleRequest(req); }
}
14. 命令模式
将请求封装为一个对象,从而可用不同的请求对客户进行参数化,支持撤销操作和请求队列。
适用场景:菜单命令、按钮动作、撤销/重做、事务、任务调度。
结构:Command 接口声明 execute,具体命令持有接收者。调用者通过命令对象执行请求。
interface Command { void execute(); void undo(); }
class LightOnCommand implements Command {
private Light light;
public void execute() { light.on(); }
public void undo() { light.off(); }
}
class RemoteControl {
void setCommand(Command c) { ... }
void pressButton() { c.execute(); }
}
15. 解释器模式
给定一个语言,定义它的文法表示,并定义一个解释器,使用该表示来解释语言中的句子。
适用场景:规则引擎、编译器、正则表达式处理、数学公式解析。实际应用中较少直接手写解释器,多使用现成框架。
要点:终结符表达式、非终结符表达式、上下文。每个解释器负责解释一部分语法。
16. 迭代器模式
提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。
适用场景:遍历集合、数据流,支持多种遍历策略。
典型实现:Java 的 Iterator 接口,hasNext() 和 next()。
17. 中介者模式
用一个中介对象来封装一系列对象的交互,中介者使各对象不需要显示地相互引用,降低耦合。
适用场景:GUI 对话框、复杂网络通信、航空管制,避免网状连接变为星形结构。
interface Mediator { void send(String msg, Colleague c); }
class ConcreteMediator implements Mediator {
Colleague1 c1;
Colleague2 c2;
void send(String msg, Colleague c) { if(c==c1) c2.notify(msg); else c1.notify(msg); }
}
18. 备忘录模式
在不破坏封装的前提下,捕获一个对象的内部状态,并在该对象之外保存这个状态,以便将来能将该对象恢复到原先保存的状态。
适用场景:Undo 操作、游戏存档、事务回滚。
结构:发起人(Originator)、备忘录(Memento)、管理者(Caretaker)。备忘录不可对外暴露内部细节。
19. 观察者模式
定义对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。
适用场景:事件处理、MVC 架构、订阅-发布系统、数据绑定。
interface Observer { void update(String msg); }
class ConcreteObserver implements Observer { ... }
class Subject {
private List<Observer> observers = new ArrayList<>();
void attach(Observer o) { ... }
void notifyAll(String msg) { for(Observer o: observers) o.update(msg); }
}
20. 状态模式
允许对象在内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
适用场景:订单状态、电梯状态、游戏角色状态,避免大量 switch-case 或 if-else。
interface State { void handle(); }
class ConcreteStateA implements State { ... } // 可包含状态转移逻辑
class Context {
private State current;
void setState(State s) { current = s; }
void request() { current.handle(); }
}
21. 策略模式
定义一系列算法,把它们封装起来,并且使它们可互相替换。策略模式让算法的变化独立于使用算法的客户。
适用场景:多种支付方式、排序算法选择、折扣策略。
interface Strategy { void algorithm(); }
class ConcreteStrategyA implements Strategy { ... }
class Context {
private Strategy s;
void setStrategy(Strategy s) { this.s = s; }
void execute() { s.algorithm(); }
}
22. 模板方法模式
在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
适用场景:框架设计、流程固定但细节可变(如 JUnit 的 setUp/tearDown)。
abstract class AbstractClass {
final void templateMethod() { step1(); step2(); step3(); }
abstract void step1();
abstract void step2();
void step3() { /* 默认实现 */ }
}
23. 访问者模式
表示一个作用于某对象结构中的各元素的操作,使你可以在不改变各元素的类的前提下定义作用于这些元素的新操作。
适用场景:对象结构稳定但需经常定义新操作,如编译器 AST 遍历、报表生成。
结构:Visitor 声明 visit 方法,每个访问方法接收具体的 Element 类型。Element 提供一个 accept 方法,将自身传递给访问者。
interface Visitor { void visit(ConcreteElementA a); void visit(ConcreteElementB b); }
interface Element { void accept(Visitor v); }
class ConcreteElementA implements Element { public void accept(Visitor v) { v.visit(this); } }
学习路径与总结
- 先理解问题,再学习解法:每个模式都针对特定场景,不要为了用模式而用模式。
- 结合 UML 类图:通过类图掌握参与者关系,比只看代码更直观。
- 实际重构中练习:寻找项目中违反开闭原则、多条件判断、强耦合的地方,思考适用模式进行优化。
- 由简入深:建议按策略 → 单例 → 装饰 → 观察者 → 工厂方法 → 模板方法 → 代理 → 组合 → 命令 → 适配器的顺序学习,再逐步扩展。
掌握这 23 种模式,你就能读懂大多数架构框架的设计意图,也能在软件需求变化时快速做出优雅、可扩展的应对。