常见设计模式讲解
一、总体分类(来自 GOF)
- 创建型(Creational):负责对象创建,隐藏创建细节,常见:Singleton、Factory Method、Abstract Factory、Builder、Prototype。
- 结构型(Structural):描述类或对象如何组合,常见:Adapter、Bridge、Composite、Decorator、Facade、Flyweight、Proxy。
- 行为型(Behavioral):对象或类间的通信与职责分配,常见:Observer、Strategy、Command、Iterator、Template Method、State、Visitor、Chain of Responsibility、Mediator。
创建型模式 (Creational)
- Singleton — 单例模式
- 目的:保证类只有一个实例,并提供全局访问点。
- 解决的问题:需要唯一共享实例(配置、连接池、日志中心等),并避免频繁创建对象开销。
- 参与者:Singleton 类本身负责实例管理。
- 实现要点:
- 饿汉式(类加载时初始化)或懒汉式(延迟实例化)。
- 线程安全:双重检查+volatile、静态内部类(推荐)、枚举单例(防反序列化/反射)。
- 优点:简单、能节省资源。缺点:全局状态带来测试困难、隐藏依赖、破坏可替换性。
- 工程建议:优先使用容器(Spring 单例 Bean)替代手写单例;若必需,使用静态内部类或枚举实现。
- 陷阱:对单例做过多职责会导致“上帝对象”;序列化/反射需防护。
示例:
// 文件名:SingletonExamples.java
public class SingletonExamples {// 静态内部类方式(懒加载,线程安全)public static class LazyHolderSingleton {private LazyHolderSingleton() {}private static class Holder { static final LazyHolderSingleton INSTANCE = new LazyHolderSingleton(); }public static LazyHolderSingleton getInstance() { return Holder.INSTANCE; }public void hello() { System.out.println("Hello from LazyHolderSingleton"); }}// 枚举单例(推荐,防止反序列化创建新实例)public enum EnumSingleton {INSTANCE;public void hello() { System.out.println("Hello from EnumSingleton"); }}public static void main(String[] args) {LazyHolderSingleton.getInstance().hello();EnumSingleton.INSTANCE.hello();}
}
- Factory Method — 工厂方法
- 中文:工厂方法模式
- 目的:定义用于创建对象的接口,由子类决定实例化哪一个类,客户端调用接口而不直接 new。
- 解决的问题:客户端不依赖具体实现,便于扩展新的产品类型。
- 参与者:抽象工厂(Creator)声明 factoryMethod;具体工厂返回具体产品。
- 实现要点:把 new 操作封装在工厂方法中,子类/配置决定具体实现。
- 优点:符合开闭原则,便于新增产品。缺点:增加类数量,可能有许多小工厂类。
- 适用场景:需要延迟决定具体类或运行时通过配置/子类选择实现(例如不同 DB 驱动、不同策略类实例化)。
// 文件名:FactoryMethodExample.java
public class FactoryMethodExample {interface Product { void use(); }static class ConcreteProductA implements Product {public void use() { System.out.println("Using Product A"); }}static class ConcreteProductB implements Product {public void use() { System.out.println("Using Product B"); }}// Creator 抽象工厂static abstract class Creator {public abstract Product factoryMethod();public void doSomething() {Product p = factoryMethod();p.use();}}static class CreatorA extends Creator {public Product factoryMethod() { return new ConcreteProductA(); }}static class CreatorB extends Creator {public Product factoryMethod() { return new ConcreteProductB(); }}public static void main(String[] args) {Creator c1 = new CreatorA();Creator c2 = new CreatorB();c1.doSomething(); // 使用 Product Ac2.doSomething(); // 使用 Product B}
}
- Abstract Factory — 抽象工厂
- 中文:抽象工厂模式
- 目的:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类。
- 解决的问题:多个产品族需要一起使用且相互兼容时,便于在不同产品族之间切换。
- 参与者:抽象工厂、具体工厂、抽象产品、具体产品。
- 实现要点:一个工厂接口包含多种 createX 方法(createButton/createText 等),具体工厂实现整套产品族。
- 优点:易于交换整套产品族。缺点:新增产品(增加一种产品种类)需要修改抽象工厂接口与所有具体工厂。
- 场景:UI 皮肤切换、跨平台组件库等。
// 文件名:AbstractFactoryExample.java
public class AbstractFactoryExample {// 抽象产品interface Button { void paint(); }interface TextField { void paint(); }// 具体产品 - Windowsstatic class WindowsButton implements Button { public void paint() { System.out.println("Windows Button"); } }static class WindowsTextField implements TextField { public void paint() { System.out.println("Windows TextField"); } }// 具体产品 - Macstatic class MacButton implements Button { public void paint() { System.out.println("Mac Button"); } }static class MacTextField implements TextField { public void paint() { System.out.println("Mac TextField"); } }// 抽象工厂interface GUIFactory {Button createButton();TextField createTextField();}static class WindowsFactory implements GUIFactory {public Button createButton() { return new WindowsButton(); }public TextField createTextField() { return new WindowsTextField(); }}static class MacFactory implements GUIFactory {public Button createButton() { return new MacButton(); }public TextField createTextField() { return new MacTextField(); }}// 客户端代码:通过工厂获得一族产品,无需知道具体实现public static void main(String[] args) {GUIFactory factory = detectOSFactory();Button b = factory.createButton();TextField tf = factory.createTextField();b.paint(); tf.paint();}private static GUIFactory detectOSFactory() {String os = System.getProperty("os.name", "Windows").toLowerCase();if (os.contains("mac")) return new MacFactory();else return new WindowsFactory();}
}
- Builder — 建造者
- 中文:建造者模式
- 目的:将一个复杂对象的构建与表示分离,使得相同的构建过程可以创建不同的表示。
- 解决的问题:避免构造函数参数过多(telescoping constructor),便于构造可变/复杂对象(多可选参数)。
- 参与者:Builder 接口、ConcreteBuilder、Director(可选)、Product。
- 实现要点:链式 API(.withX().withY().build())或静态内部类 builder;支持校验与默认值。
- 优点:可读性高,便于扩展,不可变对象友好。缺点:实现稍繁琐。
- 场景:复杂 DTO、配置对象、需要渐进式构造的场景。
- Prototype — 原型模式
- 中文:原型模式
- 目的:通过复制已有实例来创建新对象(浅/深拷贝)。
- 解决的问题:对象构造成本高或配置复杂时复制原型更高效。
- 参与者:Prototype 接口(clone 方法)与具体原型。
- 实现要点:实现 clone() 或序列化复制,注意深拷贝 vs 浅拷贝、共享可变字段问题。
- 优点:创建速度快。缺点:深拷贝实现复杂,维护成本高。
- 场景:大量类似对象、对象创建耗时(例如复杂初始化或 expensive builder)。
// 文件名:BuilderExample.java
public class BuilderExample {public static class Person {private final String name;private final int age;private final String address;private final String phone;private Person(Builder b) {this.name = b.name; this.age = b.age; this.address = b.address; this.phone = b.phone;}public static Builder builder() { return new Builder(); }public static class Builder {private String name;private int age;private String address;private String phone;public Builder name(String name) { this.name = name; return this; }public Builder age(int age) { this.age = age; return this; }public Builder address(String address) { this.address = address; return this; }public Builder phone(String phone) { this.phone = phone; return this; }public Person build() {// 可加校验逻辑if (name == null || name.isEmpty()) throw new IllegalStateException("name required");return new Person(this);}}@Overridepublic String toString() {return "Person{name='" + name + "', age=" + age + ", address='" + address + "', phone='" + phone + "'}";}}public static void main(String[] args) {Person p = Person.builder().name("Alice").age(30).address("Beijing").build();System.out.println(p);}
}
结构型模式 (Structural)
6. Adapter — 适配器
- 中文:适配器模式
- 目的:将一个类的接口转换成客户端所期望的另一个接口,使接口不兼容的类能协同工作。
- 解决的问题:集成第三方库、旧接口适配新接口。
- 参与者:Target、Adapter、Adaptee。
- 实现要点:对象适配器(通过组合 Adaptee)或类适配器(通过继承)。优先用对象适配器。
- 优点:解耦、复用第三方代码。缺点:增加额外包装类。
- 场景:第三方系统适配、重构渐进迁移。
- Bridge — 桥接
- 中文:桥接模式
- 目的:把抽象部分与实现部分分离,使二者可以独立变化(抽象与实现解耦)。
- 解决的问题:当抽象与实现都可能扩展时,避免类爆炸。
- 参与者:Abstraction(持有 Implementor)、RefinedAbstraction、Implementor、ConcreteImplementor。
- 实现要点:Abstraction 持有一个 Implementor 接口实例,调用实现完成具体操作;扩展各自维度时只需添加子类或实现。
- 优点:解耦、灵活扩展。缺点:设计复杂度高。
- 场景:图形渲染框架(抽象图形和渲染实现分离)、多平台支持。
- Composite — 组合
- 中文:组合模式
- 目的:将对象组合成树形结构以表示“部分-整体”的层次结构,客户端以相同方式对待单个对象和组合对象。
- 解决的问题:递归树形结构的统一处理(文件系统、菜单)。
- 参与者:Component(叶与容器共有接口)、Leaf、Composite(包含子 Component 列表)。
- 实现要点:统一接口,Composite 实现 add/remove/child 操作并递归调度。
- 优点:简化客户端代码、可扩展。缺点:对叶子/容器行为差异的操作可能造成设计复杂。
- 场景:GUI 组件树、树状业务对象。
- Decorator — 装饰者
- 中文:装饰器模式
- 目的:动态地给对象添加职责,而不改变其接口;可按需组合功能。
- 解决的问题:避免通过大量子类扩展行为(类继承爆炸)。
- 参与者:Component、ConcreteComponent、Decorator(维护 Component 引用)、ConcreteDecorator。
- 实现要点:Decorator 实现 Component 接口并包装一个 Component 实例,调用前后可增强行为。
- 优点:组合灵活、开放扩展。缺点:调试困难(多层嵌套)、增加对象数量。
- 场景:I/O 流(Java IO)、动态权限/缓存/日志增强。
- Facade — 外观
- 中文:外观模式
- 目的:为复杂子系统提供一个统一的高层接口,简化客户端使用。
- 解决的问题:隐藏系统内部复杂性,减少客户端与子系统耦合。
- 实现要点:Facade 聚合多个子系统接口,封装调用顺序与错误处理。
- 优点:易用、低耦合。缺点:Facade 可能成为“上帝对象”并承担过多职责。
- 场景:提供模块对外服务入口、简化复杂 API。
- Flyweight — 享元
- 中文:享元模式
- 目的:通过共享尽可能多的对象来减少内存占用(内外状态分离)。
- 解决的问题:大量相似对象导致内存压力(例如字符渲染、棋子对象)。
- 参与者:Flyweight(共享内在状态)、UnsharedFlyweight、FlyweightFactory。
- 实现要点:将可共享的“内在状态”抽取出来并缓存;外部状态由客户端传入。
- 优点:显著节省内存。缺点:增加结构复杂性,管理开销,可能降低可读性。
- 场景:文本编辑器字符对象、大量重复数据(可用对象池/缓存替代)。
- Proxy — 代理
- 中文:代理模式
- 目的:为其他对象提供一种代理以控制对这个对象的访问(远程代理、虚代理、保护代理、缓存代理等)。
- 解决的问题:在不改变客户端代码的情况下插入访问控制、延迟加载、缓存、日志等逻辑。
- 参与者:Subject、RealSubject、Proxy(保存 RealSubject 引用并控制访问)。
- 实现要点:JDK 动态代理或字节码代理(CGLIB/ByteBuddy),或手写代理类。
- 优点:透明插入横切逻辑、延迟加载。缺点:可能带来性能开销与调试复杂度。
- 场景:AOP、RPC stub、懒加载、权限校验、缓存代理。
行为型模式 (Behavioral)
13. Observer — 观察者(发布-订阅)
- 中文:观察者模式 / 发布-订阅
- 目的:当一个对象状态变化时,自动通知依赖它的多个对象并更新(解耦通知者与订阅者)。
- 解决的问题:降低发布者与订阅者耦合,支持动态注册/注销。
- 参与者:Subject(维护 observers 列表)、Observer(更新接口)。
- 实现要点:事件驱动、线程模型(同步/异步);注意内存泄露(注销)。
- 优点:松耦合、灵活扩展。缺点:顺序不可控、回调可能阻塞发布线程。
- 工程实践:进程内用观察者,跨进程推荐消息中间件(Kafka/RabbitMQ);观察者处理应异步或有限时长,避免阻塞主流程。
- Strategy — 策略
- 中文:策略模式
- 目的:定义一系列算法,把每个算法封装起来,使它们可以互换,客户端通过上下文选择策略。
- 解决的问题:避免大量 if/else,通过策略替换实现开闭。
- 参与者:Strategy 接口、多个 ConcreteStrategy、Context(持有 Strategy)。
- 实现要点:通过 DI/工厂/注册表选择具体策略;策略应尽可能无状态或线程安全。
- 优点:代码清晰、易扩展。缺点:策略数量多时管理复杂。
- 场景:支付策略、折扣/促销策略、路由选择策略。
- Command — 命令
- 中文:命令模式
- 目的:将请求封装成对象,从而支持参数化、队列化、日志记录、撤销/恢复等功能。
- 解决的问题:解耦请求的发起者与执行者,支持延迟/撤销/重做与宏命令。
- 参与者:Command(execute/undo)、ConcreteCommand、Invoker、Receiver。
- 实现要点:命令对象应包含执行所需全部信息,支持可序列化便于持久化/重试。
- 优点:灵活、便于扩展操作集合。缺点:会产生大量小类,状态管理较复杂。
- 场景:任务队列、事务补偿、可撤销操作(编辑器 undo)。
- Iterator — 迭代器
- 中文:迭代器模式
- 目的:提供一种方法顺序访问聚合对象的元素,而不暴露其内部表示。
- 解决的问题:统一遍历接口并隐藏容器内部结构。
- 参与者:Iterator、Aggregate(返回 Iterator)。
- 实现要点:支持外部迭代(客户端驱动)或内部迭代;注意并发修改(fail-fast 或 snapshot)。
- 场景:集合遍历、自定义数据结构遍历。
- Template Method — 模板方法
- 中文:模板方法模式
- 目的:定义算法骨架,把可变步骤延迟到子类实现,固定流程由父类控制。
- 解决的问题:复用通用流程、把可变点抽象出来,避免重复代码。
- 参与者:抽象类(模板方法与抽象步骤)、具体子类实现步骤。
- 优点:复用模板、控制流程。缺点:通过继承扩展,灵活性受限(可用策略模式替代)。
- 场景:数据处理管道、框架中钩子方法(Spring 的初始化流程就是模板方法风格)。
- State — 状态
- 中文:状态模式
- 目的:允许对象在内部状态改变时改变行为,使对象看起来像改变了类。
- 解决的问题:替代大量状态分支语句,把状态及行为封装在状态类中。
- 参与者:Context(维护当前状态)、State 抽象、ConcreteState(实现行为并可切换 Context 的状态)。
- 优点:清晰表示状态与行为,易于扩展。缺点:增加类数量,状态切换复杂时管理成本高。
- 场景:订单状态机、工作流节点处理。
- Visitor — 访问者
- 中文:访问者模式
- 目的:在不改变对象结构的前提下,定义新的操作(把操作封装在访问者中,遍历对象结构执行操作)。
- 解决的问题:当需要对对象结构进行多种不相关操作时,避免在对象类中增加大量方法。
- 参与者:Visitor(visit 方法)、Element(accept(visitor))、ConcreteElement、ConcreteVisitor。
- 实现要点:Visitor 需要知道所有 Element 的类型(双分派),扩展 Element 类型代价大。
- 优点:易于添加新操作。缺点:难以扩展新的 Element(需修改 Visitor 接口)。
- 场景:编译器 AST 遍历、复杂对象报表/导出。
- Chain of Responsibility — 责任链
- 中文:职责链模式
- 目的:使多个对象都有机会处理请求,将处理者连成链并沿链传递请求,直到某个处理者处理它。
- 解决的问题:把请求的发送者和接收者解耦,方便动态组合处理逻辑(过滤链、审核链)。
- 参与者:Handler 抽象(handle 方法,持有 next),ConcreteHandler。
- 实现要点:链可以在运行时动态构造,支持 short-circuit 或继续传递;避免无限循环。
- 优点:灵活、扩展性好。缺点:调试困难、责任不明确时流向难以追踪。
- 场景:Web 过滤器链、审批流程、策略链。
- Mediator — 中介者
- 中文:中介者模式
- 目的:用中介对象封装一系列对象间的交互,减少对象之间的直接耦合,使它们通过中介者 communicate。
- 解决的问题:复杂交互导致类之间耦合过高;把复杂通信集中管理。
- 参与者:Mediator 接口、ConcreteMediator(知道各个 Colleague)、Colleague(组件)。
- 实现要点:中介者承担协调职责,Colleague 只需与中介者交互;中介者可能变成复杂“上帝”组件。
- 场景:GUI 控件交互、复杂业务流程协调、聊天系统中的转发。