设计模式全景指南:GoF 23种设计模式分类、SOLID原则与PHP实战综述

刚入行的程序员最大的困惑不是「不会写代码」,而是「写了之后改不动」。一个需求过来,发现要动几十个文件;加个新功能,原来的逻辑莫名其妙崩了;接手别人的代码,只能祈祷别点错。这些问题背后往往缺的不是语法熟练度,而是对代码结构的理解——也就是设计模式要解决的问题。

本文是设计模式系列的开篇综述,先建立全局认知:设计模式是什么、从哪来的、分几类、什么时候该用、什么时候不该用。后续文章会对每一类模式做深入拆解,并结合 PHP 开发场景给出可落地的代码示例。

设计模式到底是什么

设计模式(Design Pattern)是对反复出现的编程问题的通用解决方案。它不是让你直接复制粘贴的代码片段,而是一套经过验证的「解题思路」。就像建筑里不会每次重新发明门窗的构造一样,软件工程里也有处理对象创建、类组合、行为协作的成熟模板。

一个设计模式通常包含四个要素:

  • 模式名称:一个简短、有辨识度的名字,便于团队沟通(比如「这里用工厂模式就行」)。
  • 问题描述:这个模式要解决什么场景下的痛点。
  • 解决方案:描述模式的核心结构——有哪些角色、怎么协作。
  • 效果与权衡:用这个模式能得到什么好处,又会带来哪些额外成本。

关键认知:设计模式不是银弹。每个模式都有适用边界,盲目套用反而会让简单问题变得啰嗦。好代码的标准永远只有一个——能用最小认知负担把业务表达清楚。模式只是帮你达到这个目标的工具之一。

从 GoF 到现代:设计模式的演进

设计模式的系统化始于 1994 年,Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides 四人合著的《设计模式:可复用面向对象软件的基础》(GoF 23)定义了 23 种经典模式。这本书的影响力大到「四人帮」(Gang of Four)成了设计模式的代名词。

但随着语言演进,很多当年的「模式」已经被语言特性消化了:

  • 迭代器模式(Iterator)——现在 PHP 里 foreach 天然支持,不用自己写。
  • 观察者模式(Observer)——事件驱动框架(如 Laravel Event)已经内建了。
  • 命令模式(Command)——消息队列、Job 系统的普及使其日常化。
  • 单例模式(Singleton)——现代 DI 容器天然管理对象生命周期,手动写单例反而多余。

所以学设计模式不是为了「记住 23 种」,而是理解背后的组织原则。就算某个模式的具体写法不再需要手写,它的思想——封装变化、面向接口、组合优于继承——仍然是现代软件设计的基石。

三大分类:创建型、结构型、行为型

GoF 将 23 种模式分为三类。这个分类不是按技术线分的,而是按「解决什么问题」来划的。理解分类本身就比记具体模式名字更有用,因为分类直接对应日常开发中遇到的三大类痛点。

创建型模式(Creational Patterns)——对象怎么造

把对象的创建过程从使用方抽离出去,让「谁来创建」「怎么创建」「什么时候创建」可以独立变化。

模式 一句话概括 典型场景
单例模式 全局只有一个实例 数据库连接、配置管理(现代已少用)
工厂方法 子类决定创建哪个对象 多支付渠道、多日志驱动切换
抽象工厂 创建一系列相关对象 跨平台 UI 组件族
建造者模式 分步构建复杂对象 SQL 查询构建器、表单构建器
原型模式 克隆已有对象 模板配置、复杂对象复制

结构型模式(Structural Patterns)——类和对象怎么搭

解决「怎么用已有的东西拼出新东西」。核心思想:通过组合把不同接口、不同职责的类协同起来。

模式 一句话概括 典型场景
适配器模式 把不兼容的接口转成可用的 对接第三方 API,统一内部接口
装饰器模式 动态给对象加功能 中间件链、缓存层叠加
代理模式 给对象加一层控制 懒加载、权限检查、远程调用
外观模式 复杂系统对外提供简单入口 Service 层封装多个 Repository
组合模式 树形结构的统一处理 菜单层级、目录树、组织架构
桥接模式 抽象和实现独立变化 多数据库驱动 + 多查询构建器
享元模式 共享细粒度对象减少内存 字符对象池、游戏粒子系统

行为型模式(Behavioral Patterns)——对象之间怎么协作

关注的是「谁干什么」「怎么通知」「怎么把流程串起来」。这是日常开发中打交道最多的类型。

模式 一句话概括 典型场景
策略模式 算法族可互换 多支付方式、多排序算法
观察者模式 一对多事件通知 事件系统、消息推送
责任链模式 请求沿链传递直到有人处理 审批流、中间件管道
模板方法 父类定骨架,子类填细节 数据导入流程、报表生成
命令模式 把请求打包成对象 任务队列、撤销操作
状态模式 对象行为随状态改变 订单状态机、审批状态流转
迭代器模式 统一遍历方式 集合遍历(语言已内建)
中介者模式 集中协调多对象通信 聊天室、Event Bus
备忘录模式 快照与恢复 编辑器撤销、游戏存档
解释器模式 自定义语法解析 规则引擎、DSL
访问者模式 数据结构与操作分离 AST 遍历、报表统计

SOLID 原则:设计模式的底层逻辑

谈设计模式绕不开 SOLID 五大原则。你可以把 SOLID 看作「为什么要这么设计」的理论基础,设计模式则是「具体怎么做」的实践方案。

  • S — 单一职责(SRP):一个类只干一件事。违反的标志是「这个类经常因为不同原因被改」。
  • O — 开闭原则(OCP):对扩展开放,对修改关闭。加新功能应该加新代码,而不是改旧代码。
  • L — 里氏替换(LSP):子类必须能无缝替换父类。打破这条的常见信号是子类重写方法时抛了 UnsupportedOperationException
  • I — 接口隔离(ISP):接口要小而精,不该强迫调用方依赖它不需要的方法。「胖接口」是代码耦合的温床。
  • D — 依赖倒置(DIP):高层模块不应依赖低层模块,两者都依赖抽象。这就是为什么我们要写 interface、依赖注入。

把这五条放一起看,本质就一句话:让代码的修改范围尽可能小,扩展成本尽可能低。这和设计模式的目标完全一致。

什么时候该用——以及更重要的,什么时候不该用

该用设计模式的信号

  • 同样的 if-elseswitch 在多个地方重复出现,而且每加一种新情况就要改所有地方。(考虑策略模式)
  • 两个类的逻辑几乎一样,只是创建对象或调用步骤稍有不同。(考虑工厂方法、模板方法)
  • 你要对接一个新的第三方服务,但它的接口和现有代码风格不兼容。(考虑适配器)
  • 一段逻辑需要在运行时组合多种行为,且组合方式会频繁变化。(考虑装饰器、组合)
  • 修改一个类就需要连带改好几个其他类。(考虑依赖倒置、中介者)

不该用的常见陷阱

  • 模式崇拜:为了用模式而用模式。一个 if 能解决的问题偏要套上策略 + 工厂 + 抽象工厂,代码量翻三倍。
  • 过早抽象:系统还没跑通就先画一大套 UML。先实现,等「第二遍写同样的东西」时再抽象。
  • 忽略语言特性:PHP 8 有枚举、match 表达式、命名参数、Fibers,很多以前要用模式绕的问题现在语言本身就解决了。写 PHP 就用 PHP 的方式。
  • 忽视性能:有些模式(如装饰器嵌套太多、观察者泛滥)会在热点路径上增加大量对象和方法调用。不一定是问题,但要做的时候心里有数。

PHP 场景下的设计模式落地

本系列文章会以 PHP 为主要示例语言(思想通用,用什么语言都能看懂),结合以下真实业务场景来讲解每个模式:

  • 支付系统:多支付渠道的切换 → 策略模式 + 工厂方法
  • 优惠券计算:满减、折扣、赠品的叠加规则 → 装饰器 + 责任链
  • 订单状态机:待支付 → 已支付 → 已发货 → 已完成 → 已取消 → 退款中 → 已退款 → 状态模式
  • 数据导出:Excel / CSV / PDF 多格式支持 → 策略模式 + 模板方法
  • 消息通知:短信、邮件、站内信的多通道发送 → 观察者 + 适配器
  • 多数据源切换:读写分离、分库路由 → 代理模式

每个场景都会给出「不好的写法」和「重构后的写法」的对比,让你直观感受模式带来的变化。

系列文章计划

本系列将按照「先建立分类认知,再逐个深入」的节奏展开:

  1. 【总述】设计模式全景指南(本文)
  2. 创建型模式:工厂方法与抽象工厂
  3. 创建型模式:建造者与原型的实战
  4. 结构型模式:适配器、装饰器与代理
  5. 结构型模式:外观、组合与桥接
  6. 行为型模式:策略与模板方法
  7. 行为型模式:观察者、责任链与命令
  8. 行为型模式:状态、中介者与其他
  9. 实战篇:用设计模式重构一个遗留业务模块

写在最后

设计模式不是面试题库,也不该是简历上凑数的那一行。它真正值钱的地方在于:当你面对一个「改不动」的系统时,你知道问题出在哪;当你开始一个新模块时,你知道过去的人踩过的哪些坑可以用哪个结构来规避。

记住三条底线:

  • 模式是手段,不是目的。如果去掉模式代码更清晰,那就去掉。
  • 三遍原则。第一遍正常写,第二遍发现和别处很像时重构,第三遍遇到同样的模式再考虑抽象。
  • 团队共识比模式本身重要。大家不理解的「优雅模式」不如大家都能维护的「朴实代码」。

接下来的文章,我们逐个拆解每类模式,用真实 PHP 代码讲清楚每个模式的结构、适用场景和常见误区。欢迎持续关注。