1994 年 GoF 写下 23 个面向对象模式;2026 年 iluwatar/java-design-patterns 仓库收录了 192 个。三倍多的差距,不是堆砌,而是软件世界在 30 年里「要解决的问题」发生了根本迁移——从单线程桌面对象,走向并发、分布、微服务与云原生。本文以仓库为经(真实可查源码),以 GoF 三族为纬(经典骨架),串起模式、原则与定律之间的互文。
模式不是「工具箱里的工具」,而是一种被命名的共享词汇。一旦团队能用名字沟通,设计讨论就从「我给你讲一遍这段代码」降级为「这里用个装饰器就行」。
设计模式的概念并非软件独创。建筑学家 Christopher Alexander 在《建筑模式语言》中第一次提出:城市设计可以拆成一套可复用的「模式」——窗户该多高、街区该多大。1994 年,Erich Gamma、Richard Helm、Ralph Johnson、John Vlissides(即「四人组」GoF)把这套思想搬进面向对象编程,出版了《设计模式:可复用面向对象软件的基础》,给出 23 个模式。此后又有几十种模式被陆续发现,并蔓延到面向对象之外的领域。
但模式从诞生起就伴随争议。Refactoring.Guru 归纳了三点:① Paul Graham 的「锤子论」——模式常常是为缺失的语言特性打的补丁(典型如 Strategy,在现代 Java 里一个 lambda 就能替代);② 模式容易沦为教条,被「全心全意」套用而不顾项目实际;③ 不当使用——「手里只有一把锤子,看什么都像钉子」。这恰恰提醒我们:模式是权衡,不是银弹。
iluwatar/java-design-patterns 仓库的 192 个真实模式为素材(每个都有可运行的 Java 实现),以 GoF 三族(创建 / 结构 / 行为)为逻辑骨架,向上延伸出并发与架构的长尾。同时把它与已经整理的「26 条设计原则」「53 条定律」连成一张知识地图——原则是为什么,模式是怎么做;除 GoF 23 外的 169 个模式另有 《超越 GoF 的 169 个模式 · 详细介绍》 逐模式展开。所有模式可按意图归为三大经典族(GoF),但仓库的真正价值在于三大族之外那条长长的「尾巴」——并发、企业集成与架构。
| 主线 | 回答的根本问题 | 典型模式 |
|---|---|---|
| 创建 | 「谁来决定对象怎么生、何时生」——解耦实例化与使用方 | Factory Method · Abstract Factory · Builder · Prototype · Singleton |
| 结构 | 「对象如何组合成更大结构,却仍保持灵活」——用组合对抗继承僵化 | Adapter · Bridge · Composite · Decorator · Facade · Flyweight · Proxy |
| 行为 | 「职责与沟通归谁」——一个动作该由谁负责、状态归谁管 | Strategy · Observer · State · Command · Template · Iterator · Mediator · Visitor |
| 并发/架构 | 「多执行流 / 多服务如何协作且不崩」——超出单对象的系统级问题 | CQRS · Saga · Circuit Breaker · 六边形 · 微服务 · Reactor |
创建型解决的是同一处张力——把「实例化逻辑」从「使用方」手里夺回来。一旦调用方写死 new ConcreteA(),它就被钉死在这个类上;创建型把「生」的决定权上移,让使用方只依赖抽象。
定义一个创建对象的接口,但把「具体造哪个」留给子类。调用方依赖的是「工厂方法」而非具体类。
这是解耦实例化最常用的起点:框架提供骨架,应用方注入具体产品。它与依赖倒置(DIP)天然契合——高层模块只认抽象工厂与抽象产品。
当系统需要「一套配套的对象」(如跨平台的 UI 控件、一套天地鬼神的 DAO 实现),用一个工厂把整族产品一次性造齐,保证它们之间兼容。
关键是「产品族」概念:它约束了一组对象必须配套出现,避免混搭出错。代价是新增一个产品种类要改动所有工厂接口——这也是开闭原则在此处的真实代价。
当构造参数爆炸(十几个可选字段),用 Builder 把「构造过程」与「最终表示」分离;Step Builder 进一步用流式 API 把每一步变成类型安全的链式调用。
现代演化:Java 17 的 record + 具名构造参数在一定程度上削弱了 Builder 的必要性,但对「多步、可校验、部分必填」的构造,Builder 依然清晰。
当「从零 new」成本高、或要复制的对象构造逻辑复杂,直接克隆一个已配置好的原型更高效。
保证一个类只有一个实例,并提供一个全局访问点。仓库里还衍生出 multiton(按 key 多实例)、monostate(多实例共享同一状态)。
double-checked locking 版)在测试中是灾难——隐式全局依赖让单测无法隔离。今天的答案几乎总是依赖注入容器:让容器以「单例作用域」来管理生命周期,代码里不再出现 getInstance()。单例从「自己实现」退化成了「框架配置」。这条家族的现代终点是依赖注入 Dependency Injection:它把「谁负责装配所有对象」这个最难的创建问题,彻底交给容器。仓库中 dependency-injection、factory、factory-kit、abstract-document、object-pool、lazy-loading 都属于同一条脉络。
结构型的核心是组合优于继承的工程落地:在运行时把对象拼成更大的结构,却不让结构变成一整块改不动的石头。
旧系统、第三方库、不同版本的 API 往往接口不兼容。Adapter 在不改双方源码的前提下做接口转换——它是老旧系统接入新世界的「接生婆」。
要给一个对象加日志、加缓存、加事务、加权限,如果用继承,组合数会指数爆炸(LogCachedSecureXxx)。Decorator 用「包裹」在运行时层层叠加,每个装饰只关心自己那一件事。
现代演化:Spring 的 AOP + 注解,本质就是把 Decorator 变得近乎透明——你写 @Transactional,框架替你包了一层代理。这与「不可见代理」是同一思路。
远程代理、虚拟代理(lazy loading)、保护代理、动态代理——都是在「访问」这件事上做文章。仓库中 virtual-proxy、dynamic-proxy 是它最实用的两个分支。
当一个类有两个独立变化的维度(如「形状」×「颜色」、「消息」×「发送渠道」),Bridge 用组合替代多层继承,让两个维度各自演化、互不牵制。
Facade 为复杂子系统提供一个简单入口,把「调用方要懂 10 个类」降成「调用方只懂 1 个门面」。Composite 让「单个对象」与「对象容器」对调用方一视同仁,递归结构(菜单、DOM、文件系统)因此可以统一遍历。
同族还有 flyweight(共享细粒度对象,以空间换性能)、private-class-data、twin、front-controller、composite-view、composite-entity、delegation、converter、marker-interface。它们的共同点:都在重塑「对象之间的拼法」。
行为型回答的是「一个动作该由谁负责、状态归谁管、对象间怎么说话」。GoF 11 个行为模式里,有一半在解决同一类问题:把「if-else 的硬分支」换成「可替换的对象」。
排序、压缩、计费、校验……同一意图下的多种算法,抽成统一接口,运行时切换。
list.sort(comparator) 就是策略模式,只是没了那个 interface。这恰恰是「模式是对缺失语言特性的补丁」的最佳例证——它没消失,只是被语言吸收进了语法。一个对象状态变化,自动通知所有关心者。这是 GUI、消息总线、响应式编程的共同祖先。仓库中 observer、publish-subscribe、event-aggregator、event-based-asynchronous 都是它的变体。
订单的「待支付/已支付/已发货/已完成」如果全写在一个类里,就是一长串 if (state == ...)。State 模式让每个状态成为独立对象,行为随状态切换而自然迁移——本质是一个隐式状态机。
一旦「做什么」成了对象,它就可以被排队、撤销、重放、记录日志、跨网络传输。这是 undo/redo、任务队列、事务性消息(transactional-outbox)的共同基础。它与 CQRS、Saga 在架构层是一脉相承的。
Template Method 定好算法骨架、留钩子给子类;Iterator 把「怎么遍历」与「遍历什么」解耦(现代 for-each 已内置);Mediator 用一个人集中调度,切断对象间的网状依赖;Visitor 用双分派把「稳定的结构」与「易变的操作」分开,新增操作不必改结构类。
行为族在仓库中长得最茂密:chain-of-responsibility、command、commander、interpreter、memento、null-object(用空对象消除 null 判断)、servant、specification、callback、collection-pipeline、combinator、fluent-interface 等都在此列。
如果说 GoF 是「对象级别的手艺」,那么仓库多出来的那 169 个模式,几乎都在回答一个 1994 年没人认真问的问题: 当系统里有多个执行流、多台机器时,怎么办?这就是模式的疆域从「对象」放大到「系统」的过程。
这一支里有一整套「线程协作」的词汇:producer-consumer(用队列解耦生产与消费)、thread-pool-executor(复用线程、限流)、double-checked-locking(懒加载单例的并发安全写法)、active-object(把方法调用变成消息,串行化到专用线程)、reactor(事件循环、非阻塞)、leader-followers、balking、guarded-suspension、backpressure、fork-join、map-reduce、poison-pill。它们对应着现代高并发框架(Netty、Reactor、线程池)的底层心智模型。
下游反复超时,若还傻傻重试,会把调用方自己也拖死。断路器在失败率超阈值时「跳闸」,直接拒绝请求并进入半开探测。它是对墨菲定律(凡可能出错的终将出错)的工程回应:把异常路径当成一等公民。
跨多个微服务的「事务」无法用数据库 2PC(性能与可用性代价太高)。Saga 把大事务拆成一串本地事务,失败时按相反顺序执行补偿;transactional-outbox 则保证「写库」与「发消息」的最终一致。它们是对「强一致不可得」的务实承认。
这一支直接决定「代码怎么分层、服务怎么切」:model-view-controller / model-view-presenter / model-view-viewmodel / model-view-intent(UI 架构四兄弟)、layered-architecture、hexagonal-architecture(端口与适配器)、clean-architecture、onion-architecture、以及庞大的微服务家族(microservices-api-gateway、backends-for-frontends、microservices-bulkhead、microservices-distributed-tracing 等)。
CQRS(命令查询职责分离)把「写模型」与「读模型」拆开,各自优化——这正是原则页里「命令查询分离(CQS)」在架构层的放大。六边形架构用「端口 + 适配器」把业务逻辑围在中心、把数据库/UI/消息都变成可替换的外围,与依赖倒置(DIP)、接口隔离(ISP)是同一哲学的不同尺度表达。
模式不该被孤立阅读。把本系列已经梳理的两份材料拼进来,会浮现一张完整的认知地图:原则是「道」(为什么),模式是「法」(怎么做),重构手法是「术」,工具与框架是「器」。五层叠合,才是一个工程师真正可用的全景。
| 设计原则(道) | 对应模式(法) | 背后的定律(势) |
|---|---|---|
| 开闭原则 OCP | 策略 / 装饰器 / 观察者 | 复杂性守恒——改一处不波及其他 |
| 依赖倒置 DIP / 接口隔离 ISP | 工厂方法 / 适配器 / 六边形架构 | 得墨忒耳定律——只依赖需要依赖的 |
| 单一职责 SRP | 中介者 / 命令 / 服务层 | 康威定律——模块边界≈团队边界 |
| 命令查询分离 CQS | CQRS / Command 模式 | 墨菲定律——读写混在一起更易出错 |
| 组合优于继承 | 装饰器 / 组合 | 里氏替换 LSP——继承是强耦合赌注 |
microservices-idempotent-consumer)、容错模式。③ 林纳斯定律——「足够多的眼睛让缺陷浅显」,模式作为共享词汇,正是降低阅读门槛、让评审有效的工具;但 布鲁克斯法则 提醒:加人不能加速,过度模式化反而抬高沟通成本。知道一个模式,和知道何时收手,是两件事。模式是权衡,引入它的成本(理解成本、间接层、改动成本)必须被它的收益覆盖。
getInstance(),单测无法隔离、调用链不可见。能用 DI 就不要手写单例。| # | 问题 | 若答「否」 |
|---|---|---|
| ① | 这里真的存在「反复出现的问题」吗? | 只是一次性的特例,直接写清楚就好 |
| ② | 模式是否恰好匹配意图,还是「手里只有锤子」? | 退一步,先用最简单可行的写法 |
| ③ | 引入后,增删一个变化点的成本是否真的下降? | 若间接层反而让改动更难,撤掉它 |
一句话:模式是「被命名的最佳实践」,不是「必须打卡的 KPI」。当它让代码更简单、变更更局部,才配得上这个名字。
把抽象落成直觉。下面是评审代码时最常见的「异味 → 对应模式」对照,以及一份可直接用的评审清单。
| 代码异味 | 值得考虑的模式 |
|---|---|
满屏 new ConcreteX(),且具体类常变 |
Factory / Abstract Factory / Dependency Injection |
| 构造函数的参数长到离谱 | Builder / Step Builder |
大量 if (type == A) … else if (type == B) |
Strategy / State / Visitor |
| 日志 / 事务 / 鉴权等横切逻辑反复粘贴 | Decorator / Proxy / AOP |
| 一个对象变化要通知一堆其他对象 | Observer / Publish-Subscribe / EventBus |
| 复杂的条件组合查询 | Specification |
| 多步、可撤销的操作 | Command(配合 Memento) |
| 大对象图的统一遍历 | Iterator / Composite |
| 跨服务的一致性 | Saga / Transactional Outbox / CQRS |
| 下游不稳定,反复重试拖垮自己 | Circuit Breaker / Bulkhead / Retry |
以下按意图归类,列出仓库中的主要模式(每个链接到 java-design-patterns.com 对应页面)。部分模式跨类,此处按其主要意图归置;分组口径为本次整理的分析视角。
abstract-factory · abstract-document · builder · factory · factory-kit · factory-method · lazy-loading · multiton · monostate · object-pool · prototype · singleton · step-builder · dependency-injection
adapter · bridge · composite · composite-view · composite-entity · converter · decorator · delegation · facade · flyweight · front-controller · marker-interface · private-class-data · proxy · twin · virtual-proxy · dynamic-proxy
callback · chain-of-responsibility · collection-pipeline · command · commander · combinator · currying · fluent-interface · function-composition · interpreter · iterator · mediator · memento · null-object · observer · servant · specification · state · strategy · template-method · trampoline · visitor
active-object · async-method-invocation · backpressure · balking · double-checked-locking · fork-join · guarded-suspension · half-sync-half-async · leader-election · leader-followers · lockable-object · map-reduce · monitor · optimistic-offline-lock · poison-pill · polling-publisher · producer-consumer · promise · reactor · thread-pool-executor · thread-specific-storage
model-view-controller · model-view-presenter · model-view-viewmodel · model-view-intent · layered-architecture · hexagonal-architecture · clean-architecture · onion-architecture · monolithic-architecture · event-driven-architecture · event-sourcing · command-query-responsibility-segregation · saga · api-gateway · backends-for-frontends · circuit-breaker · client-session · service-locator · service-layer · business-delegate · gateway · ambassador · strangler · anti-corruption-layer · separated-interface · page-controller · service-to-worker · presentation-model · templateview · view-helper · intercepting-filter · 以及 microservices-* 系列(aggregrator / api-gateway / bulkhead / client-side-ui-composition / distributed-tracing / idempotent-consumer / load-shedding / log-aggregation / messaging / self-registration)
data-access-object · data-bus · data-locality · data-mapper · data-transfer-object · domain-model · dirty-flag · identity-map · metadata-mapping · object-mother · parameter-object · partial-response · registry · repository · serialized-entity · serialized-lob · server-session · service-stub · session-facade · single-table-inheritance · special-case · table-inheritance · table-module · transaction-script · transactional-outbox · unit-of-work · value-object · version-number
immutable · mute-idiom · type-object · role-object · extension-objects · monad · money · property · context-object · localization · event-aggregator · event-queue · event-based-asynchronous · publish-subscribe · notification · execute-around · filterer · curiously-recurring-template-pattern · bytecode · resource-acquisition-is-initialization · double-dispatch · update-method · game-loop
模式从不承诺「写出更好的代码」,它只承诺「当我们用同一个名字说话时,误解会少一点」。
从 GoF 的 23 到今天的 192,数字增长的背后是软件所操心的事越来越大:从「一个对象怎么生、怎么合、怎么说话」,一路爬升到「一群服务怎么协作、怎么在故障中存活」。模式不是知识的终点,它是一张共享词汇表——让你在评审会上说「这里用装饰器就够」,而不必解释装饰器是什么。
但请记住那条贯穿本文的底线:模式是权衡,不是银弹。把它放进「道(为什么)—法(怎么做)—术(落到手)—器(用什么)—势(横贯的客观规律)」的全景里,它才有意义。脱离上下文地套用,只会收获更厚的间接层与更慢的团队。
Open/java-design-patterns,192 个模式均含可运行 Java 实现)。本文以仓库真实模式为骨架、以两站点为权威印证,对 GoF 三族做了深度剖析并向上延伸到并发与架构的长尾;分组与「道法术器势」映射为本次整理的增值视角,具体模式归属可跨类讨论。