设计模式概览
设计模式是软件开发中对常见设计问题的经验总结。它不是固定代码模板,而是一组可复用的设计思路,用来提升代码的可维护性、扩展性和可读性。
对初学者来说,学习设计模式的重点不是背出 23 个名字,而是理解:代码为什么会变复杂、变化点在哪里、对象之间应该怎样协作。
为什么要学习设计模式
- 降低模块之间的耦合,避免一个改动牵动大片代码。
- 提高代码复用能力,把稳定的结构沉淀下来。
- 让系统更容易扩展和维护,面对需求变化时更从容。
- 统一团队对常见设计问题的表达方式,例如“这里可以用策略模式”比长篇解释更高效。
23 种 GoF 设计模式是什么
GoF(Gang of Four)设计模式来自《设计模式:可复用面向对象软件的基础》。书中总结了 23 种经典模式,它们主要面向面向对象编程,但背后的思想同样适用于 Java、C#、TypeScript、Python 等语言。
这 23 种模式通常分为三类:
| 分类 | 关注点 | 包含模式 |
|---|---|---|
| 创建型 | 对象如何创建 | 单例、工厂方法、抽象工厂、建造者、原型 |
| 结构型 | 类或对象如何组合 | 适配器、桥接、组合、装饰器、外观、享元、代理 |
| 行为型 | 对象之间如何协作 | 责任链、命令、解释器、迭代器、中介者、备忘录、观察者、状态、策略、模板方法、访问者 |
为什么这样分类
分类的依据是“模式主要解决哪一类问题”。同一个模式可能也会影响其它方面,但通常有一个最核心的关注点。
创建型:把创建过程从使用过程里分离出来
当对象创建逻辑很简单时,直接 new 就很好。但如果创建对象需要选择实现类、拼装多个依赖、隐藏复杂构造步骤,创建型模式就能让调用方只关心“我要什么”,而不是“怎么一步步造出来”。
- 单例关注“全局只需要一个实例”。
- 工厂方法和抽象工厂关注“把具体产品的创建交给工厂”。
- 建造者关注“分步骤构建复杂对象”。
- 原型关注“通过复制已有对象创建新对象”。
结构型:让对象组合关系更灵活
结构型模式关注类与对象如何组织在一起。它们常用于适配旧接口、给对象动态增加能力、隐藏复杂子系统、表示树形结构,或者通过代理控制访问。
- 适配器解决“接口不兼容”。
- 桥接解决“多个维度独立变化”。
- 组合解决“整体和部分用一致方式处理”。
- 装饰器解决“运行期叠加功能”。
- 外观解决“给复杂系统提供简单入口”。
- 享元解决“大量相似对象的共享”。
- 代理解决“访问前后需要控制或增强”。
行为型:安排对象之间的职责与协作
行为型模式关注流程、通信和职责分配。它们帮助我们把条件分支、状态变化、事件通知、命令执行、算法步骤等逻辑组织得更清楚。
- 策略、状态、模板方法关注“行为如何变化”。
- 观察者、中介者、责任链关注“对象如何通信”。
- 命令、备忘录、访问者关注“把操作、历史或访问逻辑独立出来”。
- 迭代器、解释器关注“遍历集合或解释规则”。
推荐学习路线
可以按“先原则、再高频、后复杂”的顺序学习,不必一开始就平均用力。
- 先理解设计原则:单一职责、开闭原则、依赖倒置等原则能帮助你判断模式为什么成立。
- 从最常见的模式开始:策略、工厂方法、单例、模板方法、观察者、适配器、装饰器、代理、外观,这些模式在业务代码和框架源码中很常见。
- 结合业务场景理解分类:遇到“创建复杂”看创建型,遇到“对象关系复杂”看结构型,遇到“流程协作复杂”看行为型。
- 再学习相似模式的区别:例如策略和状态都能消除分支,但策略通常由外部选择算法,状态通常由对象内部状态驱动行为切换。
- 最后补齐低频或更抽象的模式:如解释器、访问者、享元,它们使用场景更明确,理解典型案例即可。
建议每学一个模式都问四个问题:它解决什么问题?不使用会怎样?它引入了哪些角色?使用后增加了哪些成本?
如何判断是否需要设计模式
设计模式不是越多越好。可以用下面几个信号判断是否值得引入:
- 存在明确变化点:例如支付方式、导出格式、通知渠道会持续增加,适合考虑策略、工厂等模式。
- 重复分支开始扩散:多个地方都有相似的
if/else或switch,修改一个规则要改很多文件。 - 对象创建越来越复杂:构造参数很多、创建步骤固定、实现类选择逻辑散落在业务代码中。
- 调用方知道太多细节:业务代码需要了解子系统内部多个类的调用顺序,可能需要外观或封装服务。
- 扩展总是伴随修改稳定代码:每次新增功能都要改核心流程,说明可能违反开闭原则。
如果只是一次性逻辑、需求还不稳定、代码量很小,直接写清楚通常更好。模式应该服务于可维护性,而不是为了显得“高级”。
常见误区
- 把设计模式当成代码模板:模式描述的是角色和协作方式,不同语言和项目中的实现可以不同。
- 为了使用模式而使用模式:没有变化点时强行抽象,会增加类数量和理解成本。
- 只记类图,不理解问题:不知道模式解决的问题,就很容易在不合适的场景套用。
- 忽略模式带来的成本:模式通常会增加间接层,降低直接阅读代码的速度,需要用扩展收益来抵消。
- 认为模式能替代设计原则:设计原则帮助判断方向,模式只是常见解决方案之一。
- 一次性设计过度完整:初学时容易提前预留很多扩展点,但真实需求未必会来。更推荐在变化出现后逐步重构。
目录
基础
创建型模式
创建型模式关注对象创建过程,把对象的创建和使用解耦。
结构型模式
结构型模式关注类或对象之间的组合关系,让系统结构更灵活。
行为型模式
行为型模式关注对象之间的职责分配和协作方式。
