设计模式:可复用面向对象软件的基础
《设计模式》是软件工程领域的里程碑式著作,由 Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides 四位作者合著,被尊称为"GoF 四人帮"。本书首次出版于 1994 年,提出了 23 种经典设计模式,分为创建型、结构型、行为型三大类。设计模式是对软件设计中反复出现的问题的标准化解决方案,学习设计模式可以帮助程序员理解优秀设计的原理,提高代码的可维护性、可扩展性和可复用性。这本书被誉为"面向对象设计的圣经",影响了整整一代软件工程师,是程序员必读的经典之作。
本书速读
📖 本书核心内容
《设计模式:可复用面向对象软件的基础》(Design Patterns: Elements of Reusable Object-Oriented Software)是软件工程领域的里程碑式著作,由 Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides 四位作者合著,被尊称为"GoF 四人帮"(Gang of Four)。本书首次出版于 1994 年,提出了 23 种经典设计模式,分为创建型、结构型、行为型三大类。设计模式是对软件设计中反复出现的问题的标准化解决方案,它不是具体的代码,而是解决问题的思路和模板。学习设计模式可以帮助程序员理解优秀设计的原理,提高代码的可维护性、可扩展性和可复用性。这本书被誉为"面向对象设计的圣经",影响了整整一代软件工程师,是程序员必读的经典之作。虽然本书出版已近 30 年,但其中的设计思想依然适用,现代框架和库中处处可见设计模式的身影。
🎯 创建型模式(5 种)
创建型模式关注对象的创建过程,将对象的创建与使用分离,提高系统的灵活性和可维护性。
1. 单例模式(Singleton)
意图:确保一个类只有一个实例,并提供全局访问点。应用场景:配置管理器、日志记录器、数据库连接池、线程池、缓存系统。实现要点:私有构造函数、静态实例、线程安全(双重检查锁定或懒汉式)。优点:控制实例数量、节省资源、全局访问。缺点:扩展困难、可能隐藏依赖关系。示例:Spring 的默认 Bean 作用域、Java 的 Runtime 类。
2. 工厂方法模式(Factory Method)
意图:定义创建对象的接口,让子类决定实例化哪个类。应用场景:框架扩展、插件系统、多数据库支持、日志系统。实现要点:定义工厂接口、具体工厂实现、产品接口、具体产品实现。优点:符合开闭原则、解耦创建与使用、易于扩展。缺点:增加类数量、增加系统复杂度。示例:Java 的 Calendar.getInstance()、DOM API 的 Document.createElement()。
3. 抽象工厂模式(Abstract Factory)
意图:提供创建一系列相关或相互依赖对象的接口,无需指定具体类。应用场景:UI 主题切换、跨平台组件、数据库方言、配置族。实现要点:定义抽象工厂接口、具体工厂实现、抽象产品接口、具体产品实现。优点:隔离具体类、易于交换产品族、保证产品一致性。缺点:增加抽象层、产品族扩展困难。示例:Java 的 javax.xml.parsers.DocumentBuilderFactory、GUI 库的主题系统。
4. 建造者模式(Builder)
意图:将复杂对象的构建与表示分离,使同样的构建过程可以创建不同的表示。应用场景:复杂对象构建、链式调用、参数可选的构造函数、SQL 查询构建。实现要点:定义建造者接口、具体建造者实现、指挥者(Director)、产品类。优点:封装复杂构建逻辑、支持链式调用、参数验证。缺点:增加类数量、产品必须有共同点。示例:Java 的 StringBuilder、Apache Commons 的 ToStringBuilder、Lombok 的@Builder。
5. 原型模式(Prototype)
意图:用原型实例指定创建对象的种类,并通过拷贝原型创建新对象。应用场景:对象创建成本高、需要深拷贝、避免构造函数调用、游戏对象复制。实现要点:定义原型接口(clone 方法)、具体原型实现、深拷贝 vs 浅拷贝。优点:隐藏创建细节、提高性能、简化对象创建。缺点:深拷贝复杂、需要实现 Cloneable。示例:Java 的 Cloneable 接口、原型设计工具、游戏对象池。
🎯 结构型模式(7 种)
结构型模式关注类和对象的组合方式,构建灵活、高效的类结构。
6. 适配器模式(Adapter)
意图:将一个类的接口转换成客户期望的另一个接口。应用场景:旧系统兼容、第三方库集成、接口不匹配、遗留代码适配。实现要点:定义目标接口、适配者类、适配器实现(类适配器/对象适配器)。优点:提高复用性、解耦目标与适配者、符合开闭原则。缺点:增加复杂度、过度使用增加混乱。示例:Java 的 Arrays.asList()、Spring 的 Adapter 模式应用、电源转换器。
7. 桥接模式(Bridge)
意图:将抽象与实现分离,使它们可以独立变化。应用场景:跨平台 UI、多数据库支持、多维度变化、避免类爆炸。实现要点:定义抽象接口、实现接口、扩展抽象类、具体实现类。优点:解耦抽象与实现、支持独立扩展、符合开闭原则。缺点:增加理解难度、增加设计复杂度。示例:JDBC 驱动、AWT/Swing 的组件实现、跨平台框架。
8. 组合模式(Composite)
意图:将对象组合成树形结构表示"部分 - 整体"的层次结构,使客户端一致对待单个对象和组合对象。应用场景:文件系统、UI 组件树、组织架构、表达式解析。实现要点:定义组件接口、叶子节点、组合节点、递归操作。优点:简化客户端代码、易于添加新组件、支持递归组合。缺点:限制叶子节点类型、设计较复杂。示例:文件系统目录结构、Swing 的 JComponent、XML DOM 树。
9. 装饰器模式(Decorator)
意图:动态地给对象添加额外职责,比继承更灵活。应用场景:IO 流、日志增强、权限控制、缓存层、功能扩展。实现要点:定义组件接口、具体组件、装饰器基类、具体装饰器。优点:动态添加职责、避免类爆炸、符合开闭原则。缺点:增加小对象数量、调试困难。示例:Java 的 IO 流(BufferedInputStream)、Spring AOP、咖啡加料系统。
10. 外观模式(Facade)
意图:为子系统中的一组接口提供一个统一的高层接口。应用场景:简化复杂系统、提供统一入口、降低耦合、框架封装。实现要点:定义外观类、封装子系统调用、提供简化接口。优点:简化使用、降低耦合、符合迪米特法则。缺点:可能违反开闭原则、外观类可能过大。示例:Spring 的 JdbcTemplate、SLF4J 日志门面、启动一键化系统。
11. 享元模式(Flyweight)
意图:运用共享技术有效支持大量细粒度的对象。应用场景:文本编辑器字符、游戏对象池、数据库连接池、缓存系统。实现要点:区分内部状态(共享)和外部状态(不共享)、工厂管理共享对象。优点:减少对象数量、节省内存、提高效率。缺点:增加复杂度、需要分离状态。示例:Java 的 String 常量池、Integer 缓存、围棋棋子对象。
12. 代理模式(Proxy)
意图:为其他对象提供一种代理以控制对这个对象的访问。应用场景:远程代理、虚拟代理、保护代理、智能引用、缓存代理。实现要点:定义代理接口、真实主题、代理类、访问控制。优点:控制访问、增加额外功能、符合开闭原则。缺点:增加延迟、增加复杂度。示例:Spring AOP 代理、RMI 远程调用、懒加载代理。
🎯 行为型模式(11 种)
行为型模式关注对象之间的通信和责任分配,定义清晰的对象交互方式。
13. 责任链模式(Chain of Responsibility)
意图:使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合。应用场景:审批流程、异常处理、过滤器链、中间件、事件传播。实现要点:定义处理者接口、具体处理者、设置后继者、传递请求。优点:解耦发送者与接收者、动态组织处理链、符合开闭原则。缺点:请求可能未被处理、性能影响、调试困难。示例:Java Servlet 过滤器、Spring Security 过滤器链、DOM 事件冒泡。
14. 命令模式(Command)
意图:将请求封装为对象,使你可以参数化客户端、队列或日志请求,以及支持可撤销操作。应用场景:撤销/重做、宏命令、任务队列、GUI 按钮、事务系统。实现要点:定义命令接口、具体命令、接收者、调用者。优点:解耦调用者与接收者、支持撤销、支持组合命令。缺点:增加类数量、可能过度设计。示例:文本编辑器的撤销功能、Spring 的 JdbcTemplate、任务调度系统。
15. 解释器模式(Interpreter)
意图:给定一个语言,定义它的文法的一种表示,并定义一个解释器。应用场景:SQL 解析、正则表达式、编译器、规则引擎、表达式计算。实现要点:定义抽象解释器、终结符解释器、非终结符解释器、上下文。优点:易于实现语言、易于修改文法、支持递归。缺点:性能较差、增加类数量、维护困难。示例:正则表达式引擎、SQL 解析器、编译器的语法分析。
16. 迭代器模式(Iterator)
意图:提供一种方法顺序访问聚合对象中的各个元素,而不暴露其内部表示。应用场景:集合遍历、自定义数据结构、多路复用迭代、流式处理。实现要点:定义迭代器接口(hasNext/next)、聚合接口、具体迭代器。优点:统一遍历接口、支持多种遍历、解耦集合与遍历。缺点:增加类数量、功能有限。示例:Java 的 Iterator/Iterable、C#的 foreach、Python 的生成器。
17. 中介者模式(Mediator)
意图:用一个中介对象来封装一系列的对象交互,使各对象不需要显式地相互引用。应用场景:聊天室、MVC 架构、事件总线、航班调度、UI 组件协调。实现要点:定义中介者接口、具体中介者、同事类、注册与通知。优点:减少网状依赖、简化对象交互、符合迪米特法则。缺点:中介者可能过大、增加复杂度。示例:MVC 中的 Controller、事件总线、聊天室服务器。
18. 备忘录模式(Memento)
意图:在不破坏封装性的前提下,捕获对象的内部状态,并在对象之外保存这个状态。应用场景:撤销/重做、游戏存档、配置快照、状态恢复。实现要点:定义备忘录类、发起人类、管理者类、状态保存与恢复。优点:提供状态恢复、保持封装性、简化发起人。缺点:消耗资源、可能暴露状态。示例:文本编辑器的撤销、游戏存档系统、数据库事务回滚。
19. 观察者模式(Observer)
意图:定义对象间的一对多依赖关系,当一个对象状态改变时,所有依赖者都会收到通知并自动更新。应用场景:事件系统、MVC 架构、消息推送、响应式编程、发布订阅。实现要点:定义主题接口、观察者接口、具体主题、具体观察者、通知机制。优点:解耦主题与观察者、支持广播、符合开闭原则。缺点:循环依赖风险、性能影响、更新顺序不确定。示例:Java 的 Observable/Observer、Vue 的响应式系统、消息队列。
20. 状态模式(State)
意图:允许一个对象在其内部状态改变时改变它的行为。应用场景:工作流引擎、游戏角色状态、订单状态机、TCP 连接状态。实现要点:定义状态接口、具体状态类、上下文类、状态转换。优点:消除条件判断、封装状态逻辑、符合开闭原则。缺点:增加类数量、状态转换复杂。示例:工作流系统、游戏 AI 状态机、订单处理流程。
21. 策略模式(Strategy)
意图:定义一系列算法,将每个算法封装起来,使它们可以相互替换。应用场景:排序算法、支付方式、折扣计算、压缩算法、验证规则。实现要点:定义策略接口、具体策略实现、上下文类、策略选择。优点:消除条件判断、算法独立变化、符合开闭原则。缺点:增加类数量、客户端需了解策略。示例:Collections.sort()、Spring 的 Resource 接口、支付系统。
22. 模板方法模式(Template Method)
意图:定义算法骨架,将某些步骤延迟到子类实现。应用场景:框架设计、流程控制、标准化操作、数据处理流水线。实现要点:定义抽象模板、具体模板实现、钩子方法、基本方法。优点:代码复用、控制扩展点、符合开闭原则。缺点:增加类数量、限制灵活性。示例:Java 的 Arrays.sort()、Spring 的 JdbcTemplate、Servlet 生命周期。
23. 访问者模式(Visitor)
意图:表示一个作用于某对象结构中的各元素的操作,使你可以在不改变各元素类的前提下定义作用于这些元素的新操作。应用场景:编译器 AST 处理、文档导出、报表生成、跨类操作。实现要点:定义访问者接口、具体访问者、元素接口、具体元素、接受方法。优点:增加新操作容易、集中相关操作、突破封装限制。缺点:增加新元素困难、破坏封装性、增加耦合。示例:编译器语法树处理、XML 解析器、报表生成系统。
⭐ 设计原则总结
"面向接口编程,而不是面向实现编程。"
"组合优于继承。"
"对扩展开放,对修改关闭(开闭原则)。"
"依赖抽象,而非具体依赖(依赖倒置原则)。"
"找出应用中可能需要变化之处,把它们独立出来,不要和那些不需要变化的代码混在一起。"
"设计模式是手段,不是目的。"
📚 阅读建议
本书适合以下人群阅读:中级及以上程序员:需要有一定的面向对象编程经验,理解类、继承、多态等基本概念;软件设计师和架构师:需要掌握系统设计的基本原则和常用模式;框架开发者:需要理解如何设计灵活、可扩展的 API。建议重点阅读:
- 第 1-2 章(绪论和设计基础):理解设计模式的基本概念、分类、描述方式,适合所有读者精读。
- 创建型模式章节:单例、工厂是最常用的模式,建议优先学习,理解对象创建的最佳实践。
- 行为型模式章节:观察者、策略、模板方法在实际开发中应用广泛,建议重点掌握。
- 结构型模式章节:适配器、装饰器在框架设计中常见,建议结合具体框架学习。
读完本书,你将获得:1)系统的面向对象设计知识,理解 23 种经典设计模式的结构和应用场景;2)识别设计问题的能力,能够发现代码中的"坏味道"并选择合适的模式改进;3)设计灵活、可维护代码的能力,能够应用设计原则和模式构建高质量的软件;4)阅读优秀框架代码的能力,能够理解主流框架中的设计模式应用。正如作者所说:"掌握设计模式,就是掌握优秀设计的语言。"这本书值得每个程序员反复阅读,在实践中不断深化理解。