设计模式公理化推演体系·完备性宣言

5 公理 · 7 范式 · 2 判据 · 31 模式 · 明确边界

本书定位:本书是《设计模式公理化推演体系·增补修正版》的续篇,也是整个"设计模式公理化"系列的完备性宣言

在前两本书中,我们完成了从 7-6-23 到 5-7-23 的体系精炼,并识别了工程有效性判据。本书在此基础上,完成三件事:

  1. 演绎:将 GoF 之后 8 个极其流行的模式纳入基础/交叉范式,使体系从 23 扩展到 31;
  2. 概括:给出 5-7-2 框架的完整总览,证明其内部自洽;
  3. 边界:明确声明体系不覆盖什么,以及为什么不覆盖——公理化体系的尊严来自对其边界的诚实。

如果你已经阅读过前两本书,本书将帮助你建立最终的"模式地图";如果你直接阅读本书,建议先通读附录 A 的"5 分钟速览",再进入正文。


目录


前言:为什么需要一份完备性宣言

0.1 从经验到公理的三级跳

设计模式的学习通常经历三个阶段:

阶段一:背诵阶段

“单例模式保证全局唯一实例,工厂方法将实例化延迟到子类…”
问题:23 个模式是孤立的工具,不知道何时用、为什么用。

阶段二:归纳阶段

“创建型模式解决对象创建问题,结构型模式解决对象组合问题…”
问题:GoF 的三分法是按"代码角色"分类,不是按"思维模型"分类。观察者模式和中介者模式都在"行为型",但一个是一对多广播,一个是多对多中转——思维完全不同。

阶段三:演绎阶段

“从 5 条不可再约的存在公理出发,推导出 7 大思维范式,再实例化为 31 个模式…”
问题:公理化体系是否完备?是否遗漏了重要的流行模式?是否把不属于体系的东西硬塞了进来?

本书回答这三个问题。

0.2 完备性的两个维度

完备性不是"覆盖一切"。数学上,完备性有两种含义:

  1. 相对完备(Relative Completeness):在给定边界内,所有合法命题都可被证明或证伪。
  2. 绝对完备(Absolute Completeness):不存在边界,覆盖全部可能命题——哥德尔第一不完备定理已经证明,任何足够强的形式系统都不可能绝对完备。

5-7-2 框架追求的是相对完备

在"面向对象系统结构设计"这个边界内,从 5 条核心存在公理出发,通过严格的逻辑推导,可以覆盖所有主流的对象结构设计模式。

本书将证明这个命题,并明确边界在哪里。


第一章:体系总览——5-7-2 框架的最终形态

1.1 5 条核心存在公理

公理只描述"系统必然面对什么",不描述"你应该怎么做"。

编号 命名 核心描述 学科源头
A1 实体化公理 软件实体必然经历生成→存在→消亡的连续过程 本体论、系统论
A2 分层公理 复杂交互必然呈现层次结构,层次是复杂性的必然产物 系统论、认知科学
A3 组合公理 结构必然由更小单元组合而成,组合操作对结构集合封闭 离散数学、本体论
A4 行为可变公理 同一结构在不同上下文下必然表现出不同行为 形式逻辑、运筹学
A5 信息拓扑公理 信息传递必然选择某种拓扑结构,拓扑决定耦合度 信息论、图论

1.2 2 个工程有效性判据

判据不参与推导,只判决推导结果是否工程可用。

编号 命名 判决内容 拦截示例
V1 内聚性判据 推导结果的职责集中度必须高于阈值 拦截"上帝创建器"——一个类负责所有对象的创建
V2 演化性判据 推导结果必须支持通过新增实体来扩展 拦截"修改源码才能新增类型"的简单工厂

1.3 7 大思维范式

编号 命名 推导来源 推导类型 包含模式数
P1 创建控制范式 A1 基础推导 8
P2 接口统一范式 A2 基础推导 6
P3 递归组合范式 A3 基础推导 3
P4 行为可变范式 A4 基础推导 4
P5 信息流转范式 A5 基础推导 5
P6 结构操作范式 A3 × A4 交叉推导 4
P7 领域叠加范式 A1~A5 + 领域公理 叠加推导 1+

总计:31 个核心模式(23 GoF + 8 新增)

1.4 三层推导关系图

┌─────────────────────────────────────────┐
│      第一层:5 条核心存在公理(描述性)     │
│   A1 实体化  A2 分层  A3 组合  A4 行为可变  A5 信息拓扑  │
└─────────────────────────────────────────┘
              ↓ 基础推导              ↓ 交叉推导
┌─────────────────────────────────────────┐
│      第二层:7 大思维范式(结构性)         │
│  P1 创建控制  P2 接口统一  P3 递归组合      │
│  P4 行为可变  P5 信息流转  P6 结构操作      │
│              P7 领域叠加                   │
└─────────────────────────────────────────┘
              ↓ 实例化
┌─────────────────────────────────────────┐
│      第三层:31 个核心模式(实例性)        │
│   23 GoF 模式 + 8 个新增流行模式           │
└─────────────────────────────────────────┘

┌─────────────────────────────────────────┐
│      判决层:2 个工程有效性判据            │
│     V1 内聚性判据    V2 演化性判据         │
│  (不参与推导,只判决推导结果是否可用)      │
└─────────────────────────────────────────┘

第二章:新模式的纳入推导——从 23 到 31

2.1 纳入原则

新增模式必须满足三个条件:

  1. 流行度:工业级代码中天天在用,不是学术玩具;
  2. 推导性:可以从 A1–A5 直接推导或交叉推导,无需诉诸领域公理;
  3. 非冗余:不是已有模式的简单语法变体,而是独立的约束响应。

2.2 归入 P1 创建控制的 3 个新模式

2.2.1 对象池模式(Object Pool)

流行度:⭐⭐⭐ 极高。数据库连接池、线程池、游戏对象池、HTTP 连接池——几乎所有高性能系统都在用。

是什么

对象池模式的核心思想是:预先创建一组对象,放入"池"中统一管理;当客户端需要对象时,从池中"租借"一个已存在的对象;使用完毕后"归还"到池中,而非销毁。对象的创建成本(时间或资源)远高于重置成本时,对象池成为必然选择。

对象池与单例、原型的本质区别在于:

  • 单例:全局只有 1 个实例,所有人共享同一个对象
  • 原型:通过复制已有对象来创建新对象
  • 对象池:预先创建 N 个实例(N ≥ 1),循环复用,控制总数量上限

几时出现的

对象池的概念最早出现在 1990 年代中期的数据库连接池(如 JDBC Connection Pool)和线程池(如 Java ThreadPoolExecutor)中。2000 年代随着 Java EE 应用服务器的普及,连接池成为标准配置。2010 年代后,对象池思想扩展到游戏开发(Unity 对象池、对象池管理器)、网络编程(Netty 的 ByteBuf 池、HTTP 连接池)、以及高性能计算(内存池、缓冲区池)。

哪里用的多

领域 典型实现 为什么必须池化
数据库访问 HikariCP、Druid、C3P0 数据库连接创建需要 TCP 握手 + 认证,耗时数百毫秒
线程管理 Java ThreadPoolExecutor、.NET ThreadPool 线程创建涉及内核态切换,成本极高
游戏开发 Unity Object Pool、Unreal Object Pool 每帧创建/销毁游戏对象导致 GC 压力,帧率暴跌
网络通信 Netty ByteBuf Pool、OkHttp Connection Pool 内存分配和 TCP 连接创建都是高频高成本操作
图形渲染 GPU 纹理池、顶点缓冲区池 GPU 资源创建需要驱动交互,延迟极高

与 P1 内现有模式的关系

  • 单例管控"实例数量为 1"
  • 原型管控"通过复制创建"
  • 对象池管控"预创建 + 循环复用 + 上限控制"

三者是 A1(实体化公理)在不同成本约束下的三个分支:

  • 当"只需要 1 个" → 单例
  • 当"复制比新建快" → 原型
  • 当"预创建 + 复用比即时创建快" → 对象池

为什么不是 P7(领域叠加)?

对象池不是"并发领域专属"。它的核心逻辑与线程无关——任何创建成本高昂且可复用的实体都应该池化:字符串构建器、图形资源、网络句柄。即使在单线程程序中,数据库连接池仍然是必需品。

推导链

A1: 实体必然经历创建→存在→消亡
    ↓
约束: 创建时间成本 >> 复用时间成本,且实体状态可重置
    ↓
P1-规则: 创建过程必须预分配 + 租借归还,而非即时创建即时销毁
    ↓
模式: 对象池(Object Pool)

工业级案例

  • HikariCP:目前 Java 世界最快的数据库连接池,核心思想就是"池化 + 轻量级管理"
  • Netty PooledByteBufAllocator:Netty 的高性能秘诀之一,通过池化 ByteBuf 避免 JVM GC 压力
  • Unity Object Pool:Unity 2021 官方引入的 ObjectPool API,游戏对象池化成为标准实践
  • Go sync.Pool:Go 语言标准库中的对象池,用于减少 GC 压力,在高并发服务中广泛使用

2.2.2 DI / IoC(依赖注入 / 控制反转)

流行度:⭐⭐⭐ 极高。Spring、ASP.NET Core、Angular、Unity、Laravel——现代框架的基石。没有 DI,就没有现代企业级开发。

是什么

依赖注入(Dependency Injection, DI)和控制反转(Inversion of Control, IoC)是同一枚硬币的两面:

  • IoC 是原则:对象的依赖关系不由对象自己创建和管理,而是由外部容器控制
  • DI 是实现:容器通过构造函数注入、Setter 注入、接口注入等方式,将依赖对象"注入"到需要它的对象中

核心思想极其简单:不要自己 new 依赖,让外面传进来

// 没有 DI:对象自己创建依赖,耦合死了
class OrderService {
    private UserRepository repo = new UserRepository(); // 硬编码!
}

// 有 DI:依赖由外部传入,对象只声明"我需要什么"
class OrderService {
    private final UserRepository repo;
    OrderService(UserRepository repo) { // 构造函数注入
        this.repo = repo;
    }
}

几时出现的

  • 2002 年:Martin Fowler 发表《Inversion of Control Containers and the Dependency Injection pattern》,正式命名 DI 模式
  • 2004 年:Spring Framework 1.0 发布,IoC 容器成为 Java 企业开发的标配
  • 2008 年:.NET 引入 Unity Container、Castle Windsor,DI 思想进入 .NET 生态
  • 2010 年代:Angular(2010)、Laravel(2011)、ASP.NET Core(2016)将 DI 作为框架核心机制,而非可选插件
  • 2020 年代:DI 已成为所有现代框架的"空气"——你几乎意识不到它的存在,但没有它系统无法呼吸

哪里用的多

框架/平台 DI 实现 注入方式
Java/Spring Spring IoC Container 注解(@Autowired/@Inject)+ 构造函数
.NET Core Microsoft.Extensions.DependencyInjection 构造函数注入(原生支持)
JavaScript/Angular Angular Injector 构造函数 + @Injectable 装饰器
PHP/Laravel Laravel Service Container 构造函数 + 服务提供者
Unity 游戏引擎 Unity Dependency Injection 构造函数 + 属性注入
Go wire、fx 代码生成 + 构造函数

为什么从 P7 回归 P1?

原书将 DI/IoC 放在范式 6(架构综合),理由是"需要同时满足分层、解耦、可扩展等多重约束"。但这过度复杂化了。

DI/IoC 的本质只有一个:对象不自己创建它的依赖,创建职责外置给容器。这是纯粹的生命周期管理问题——A1(实体化公理)的核心关切。

  • 对象 A 依赖对象 B → A1 要求 B 必须被创建
  • A 自己 new B → 创建职责分散在业务代码中,生命周期混乱
  • 容器统一管理 B 的创建、缓存、销毁 → 创建职责集中,生命周期受控

与工厂方法的区别

  • 工厂方法:客户端主动选择具体工厂(new ConcreteFactory().createProduct()
  • DI/IoC:客户端不选择,容器根据配置自动装配container.get(OrderService.class) 自动注入所有依赖)

两者都是"创建控制",只是控制权的粒度不同:工厂是"类级",DI 是"系统级"。

推导链

A1: 实体必然经历创建→存在→消亡
    ↓
约束: 对象之间存在依赖关系,手动创建导致耦合且生命周期混乱
    ↓
P1-规则: 创建逻辑必须与使用逻辑分离,且依赖的创建由外部容器统一管理
    ↓
模式: 依赖注入 / 控制反转(DI / IoC)

工业级案例

  • Spring Framework:全世界最经典的 IoC 容器实现,管理所有 Bean 的创建、依赖注入、生命周期回调、AOP 代理包装
  • ASP.NET Core:.NET 的 DI 容器是框架内核的一部分,从 WebHost 启动到 Controller 实例化,全程依赖注入
  • Angular:组件的依赖通过构造函数注入,服务通过 @Injectable 标记为可注入,模块通过 providers 数组注册
  • Laravel:服务容器(Service Container)是 Laravel 的"心脏",路由、控制器、中间件的所有依赖都由容器自动解析

2.2.3 流式建造者(Fluent Builder)

流行度:⭐⭐ 高。Java 的 StringBuilder、Lombok 的 @Builder、C# 的 StringBuilder、JavaScript 的链式 API(jQuery、Lodash)——无处不在。

是什么

流式建造者(Fluent Builder / Method Chaining)是建造者模式的可读性进化版。它的核心思想是:通过 return this 让建造者的每个配置方法都返回自身,从而支持链式调用

// 传统建造者:分步调用,冗长
Pizza pizza = new Pizza.Builder()
    .setDough("thin")
    .build();
pizza.setSauce("tomato");
pizza.setTopping("cheese");

// 流式建造者:一气呵成,像读自然语言
Pizza pizza = new Pizza.Builder()
    .withDough("thin")
    .withSauce("tomato")
    .withTopping("cheese")
    .withTopping("mushroom")
    .build();

流式建造者不是语法糖,而是认知科学在 API 设计中的落地:人类工作记忆一次只能处理 7±2 个信息块,链式调用将多步配置压缩为一句连贯的"句子",大幅降低认知负荷。

几时出现的

  • 2004 年:Martin Fowler 在《Fluent Interface》一文中正式命名"Fluent Interface",以 Java 的 StringBuilder(Java 1.5, 2004)为典型案例
  • 2006 年:jQuery 发布,$("#id").addClass("active").fadeIn().delay(500) 将链式调用推向主流,成为 JavaScript API 设计的黄金标准
  • 2010 年代:Lombok(2010)的 @Builder 注解自动生成流式建造者;C# 的 StringBuilder 和 LINQ 方法链成为标准;RxJava/RxJS 的 Observable 链式操作将流式思想扩展到异步编程
  • 2020 年代:几乎所有现代 API(从 ORM 到 HTTP 客户端到配置库)都提供流式接口作为默认或首选调用方式

哪里用的多

场景 典型 API 链式调用示例
字符串构建 Java StringBuilder sb.append("a").append("b").append("c")
HTTP 请求 OkHttp / Retrofit client.newCall(request).enqueue(callback)
ORM 查询 JPA Criteria / QueryDSL query.select(entity).where(condition).orderBy(field)
测试断言 AssertJ / Hamcrest assertThat(user).hasName("Tom").hasAge(25)
配置构建 Lombok @Builder / Immutables User.builder().name("Tom").age(25).build()
流式处理 Java Stream / LINQ list.stream().filter(x -> x > 0).map(x -> x * 2).collect(toList())

为什么不是 P7?

流式接口不是"语法糖领域专属"。它是建造者模式在可读性约束下的必然进化:当对象有 10 个可选参数时,new Builder().setA().setB().setC().build() 比 10 个参数的构造函数更符合人类认知。

与建造者模式的区别

  • 建造者:Director 调用 Builder 的方法,导演与建造者分离(如 director.construct() 内部调用 builder.buildPartA()
  • 流式建造者:导演就是建造者本身,通过 return this 实现链式调用,调用者自己"导演"构建过程

两者是 P1 在"可读性"约束下的两个层次。

推导链

A1: 实体必然经历创建→存在→消亡
    ↓
约束: 复杂对象参数多,且创建过程需要符合人类语言的顺序阅读习惯
    ↓
P1-规则: 创建过程必须分步进行,且每步的调用方式符合自然语言的链式表达
    ↓
模式: 流式建造者(Fluent Builder / Method Chaining)

工业级案例

  • Java StringBuilder:JDK 1.5 引入,替代线程安全的 StringBuffer,通过链式 append() 成为字符串拼接的标准方式
  • Lombok @Builder:通过注解在编译期自动生成流式建造者代码,使 Java 开发者无需手写冗长的 Builder 样板代码
  • OkHttp:HTTP 客户端的配置完全通过流式 API 完成,new OkHttpClient.Builder().connectTimeout(10, SECONDS).build()
  • RxJava/RxJS:异步数据流的全部操作(map/filter/merge/delay)都是链式调用,将流式思想从对象创建扩展到数据变换

2.3 归入 P2 接口统一的 2 个新模式

2.3.1 动态代理 / AOP(Dynamic Proxy / Aspect-Oriented Programming)

流行度:⭐⭐⭐ 极高。Spring AOP、Castle DynamicProxy、Java 动态代理(java.lang.reflect.Proxy)、CGLIB——横切逻辑的工业标准。没有 AOP,企业级系统的日志、事务、权限、监控将淹没在业务代码中。

是什么

动态代理(Dynamic Proxy)和面向切面编程(Aspect-Oriented Programming, AOP)是代理模式(Proxy)的自动化进化版

  • 动态代理:在运行时通过反射或字节码技术,自动生成一个与原对象实现相同接口的代理类,无需手写代理代码
  • AOP:在动态代理的基础上,将横切关注点(日志、事务、权限、缓存、监控)从业务代码中剥离,通过"切面"(Aspect)统一注入

核心思想:你写业务代码,框架在运行时自动帮你包一层代理,把横切逻辑织入进去

// 你写的:纯业务代码
@Service
public class OrderService {
    public void createOrder(Order order) { ... }
}

// Spring 在运行时自动做的:生成代理类,织入事务
public class OrderService$$SpringCGLIB$$0 extends OrderService {
    public void createOrder(Order order) {
        TransactionAspect.openTransaction();  // 自动织入
        super.createOrder(order);
        TransactionAspect.commit();             // 自动织入
    }
}

几时出现的

  • 1996 年:Gregor Kiczales 在 Xerox PARC 提出 AOP 概念,试图解决"横切关注点散射"问题
  • 2001 年:AspectJ 1.0 发布,首个工业级 AOP 实现,通过编译期字节码织入实现切面
  • 2004 年:Spring Framework 1.2 引入基于代理的 AOP(Spring AOP),与 AspectJ 注解兼容但实现更简单
  • 2006 年:Java 动态代理(java.lang.reflect.Proxy)成为 JDK 标准;CGLIB 通过字节码生成实现类代理(无需接口)
  • 2010 年代:AOP 思想渗透到所有框架——Spring Boot 的 @Transactional、ASP.NET Core 的 Middleware、Django 的装饰器、Rails 的 Concern
  • 2020 年代:AOP 从"显式切面"演化为"隐式代理"——服务网格(Service Mesh)通过 Sidecar 代理实现分布式 AOP,Kubernetes 的 Admission Webhook 实现基础设施级切面

哪里用的多

横切关注点 AOP 实现 典型注解/API
事务管理 Spring AOP @Transactional
日志记录 Spring AOP / LogAspect @LogOperation
权限控制 Spring Security AOP @PreAuthorize
缓存 Spring Cache AOP @Cacheable
性能监控 Micrometer / Prometheus AOP @Timed
重试机制 Spring Retry AOP @Retryable
限流熔断 Sentinel / Hystrix AOP @SentinelResource
分布式追踪 OpenTelemetry AOP 自动织入 Trace ID

为什么从 P7 回归 P2?

AOP 不是"框架领域专属"。它的核心逻辑与 Spring 无关——在运行时自动生成代理对象,保持接口一致性,同时注入横切逻辑。这是代理模式(P2)在技术演进后的自然形态。

  • 代理模式(GoF):手写代理类,编译时确定,一个代理对应一个目标
  • 动态代理/AOP:自动生成代理类,运行时确定,一个切面可以代理任意目标

两者的底层约束完全相同(A2 分层公理:需要在访问对象时添加额外控制,且保持接口一致),只是代理的生成方式从"人工"进化为"自动"。

与静态代理的区别

  • 静态代理:手写代理类,编译时确定,一对一(一个代理类代理一个具体类)
  • 动态代理:运行时通过反射/字节码生成代理类,一对多(一个切面可以代理所有匹配的方法)

两者都是 P2 的"透明控制"规则,只是代理的创建时机复用范围不同。

推导链

A2: 复杂交互必然分层,层间通过接口通信
    ↓
约束: 需要在访问对象时自动添加横切逻辑(日志、事务、权限),且接口在编译时未知或频繁变化
    ↓
P2-规则: 必须创建与原对象具有相同接口的代理对象,但代理的生成过程自动化
    ↓
模式: 动态代理 / AOP(Dynamic Proxy / Aspect-Oriented Programming)

工业级案例

  • Spring AOP:基于 JDK 动态代理(接口代理)和 CGLIB(类代理),通过 @Aspect 注解定义切面,@Around/@Before/@After 定义通知,实现声明式事务、缓存、日志
  • Castle DynamicProxy:.NET 世界最成熟的动态代理库,NHibernate、Moq、FakeItEasy 都基于它实现代理
  • gRPC / Envoy:服务网格通过 Sidecar 代理实现分布式 AOP——日志、追踪、认证、限流全部在 Sidecar 中完成,业务代码零侵入
  • Java Agent + ByteBuddy:通过 JVM Agent 在类加载时修改字节码,实现无注解的透明 AOP(如 SkyWalking 的自动追踪)

2.3.2 仓储(Repository)

流行度:⭐⭐ 高。领域驱动设计(DDD)的核心模式,Spring Data、Entity Framework、Hibernate、Django ORM——数据访问层的标准抽象。没有仓储,业务逻辑将直接耦合 SQL 语句。

是什么

仓储模式(Repository Pattern)的核心思想是:为领域对象提供统一的集合接口,屏蔽底层持久化细节(SQL、NoSQL、文件、缓存),使业务逻辑只与"对象集合"打交道,不关心数据从哪来、怎么存

// 仓储接口:对业务层暴露的是"集合操作"
public interface OrderRepository {
    Order findById(Long id);
    List<Order> findByCustomerId(Long customerId);
    Order save(Order order);
    void delete(Long id);
}

// 业务层:完全不知道底层是 MySQL、MongoDB 还是内存
@Service
public class OrderService {
    private final OrderRepository repo; // 构造函数注入
    
    public void cancelOrder(Long orderId) {
        Order order = repo.findById(orderId); // 像操作内存集合一样操作数据库
        order.cancel();
        repo.save(order);
    }
}

仓储不是 DAO(Data Access Object)。DAO 是数据表级别的抽象(一个 DAO 对应一张表),仓储是领域对象级别的抽象(一个仓储对应一个聚合根)。DAO 暴露的是 insert/update/delete,仓储暴露的是 find/save/remove——语义更接近内存集合。

几时出现的

  • 2003 年:Eric Evans 在《领域驱动设计》(Domain-Driven Design)一书中正式定义仓储模式,作为"聚合根的持久化抽象"
  • 2006 年:Spring Data JPA 的前身(Spring ORM + JPA)开始普及,仓储思想进入 Java 主流
  • 2008 年:.NET Entity Framework 引入 DbSet<T>,本质上是仓储的框架级实现
  • 2010 年代:Spring Data(2011)通过 JpaRepositoryMongoRepository 等接口将仓储模式标准化——开发者只需定义接口,框架自动生成实现
  • 2020 年代:仓储思想渗透到所有现代 ORM 和数据库访问层,从关系型数据库扩展到 Elasticsearch、Redis、Neo4j 等异构存储

哪里用的多

框架/平台 仓储实现 典型用法
Java/Spring Spring Data JPA / MongoDB / Redis interface UserRepo extends JpaRepository<User, Long>
.NET Entity Framework Core DbSet<Order> Orders
PHP/Laravel Eloquent Repository Pattern OrderRepository::find($id)
Python/Django Django ORM Manager Order.objects.filter(status='pending')
Node.js/NestJS TypeORM / Prisma Repository this.orderRepo.findOne({ where: { id } })
Go GORM / Ent client.Order.Query().Where(...).All(ctx)

为什么不是 P7?

仓储不是"DDD 领域专属"。任何需要屏蔽持久化细节、对外提供统一集合接口的系统都可以使用:文件系统、缓存、NoSQL、关系数据库。

  • 外观模式(Facade):简化复杂子系统的入口(如简化一个包含 20 个类的报表子系统)
  • 适配器模式(Adapter):转换不兼容接口(如把旧系统的 XML API 适配为新系统的 JSON API)
  • 仓储:统一持久化接口 + 屏蔽存储细节——是 P2 在"数据持久化"场景下的自然延伸

与外观 + 适配器的关系

  • 外观:简化复杂子系统的入口
  • 适配器:转换不兼容接口
  • 仓储:统一持久化接口 + 屏蔽存储细节——是 P2 在"数据持久化"场景下的自然延伸

推导链

A2: 复杂交互必然分层,层间通过接口通信
    ↓
约束: 业务逻辑需要操作对象集合,但持久化方式(SQL/NoSQL/文件)可能变化
    ↓
P2-规则: 必须提供统一接口屏蔽底层持久化细节,使业务层不依赖具体存储技术
    ↓
模式: 仓储(Repository)

工业级案例

  • Spring Data JPA:开发者只需定义 interface OrderRepository extends JpaRepository<Order, Long>,Spring 自动生成 CRUD、分页、排序、动态查询的实现
  • Entity Framework CoreDbContext 中的 DbSet<T> 就是仓储的框架级实现,context.Orders.Where(o => o.Status == "Pending").ToList() 屏蔽了 SQL 生成细节
  • CQRS 中的读仓储:在 CQRS 架构中,读模型和写模型使用不同的仓储实现(写模型用关系型数据库保证一致性,读模型用 Elasticsearch 优化查询),仓储接口统一了这种异构性
  • 缓存仓储CachedOrderRepository 装饰 OrderRepository,先查 Redis 再查数据库——仓储接口的一致性使得缓存层可以透明插入

2.4 归入 P4 行为可变的 1 个新模式

2.4.1 空对象(Null Object)

流行度:⭐⭐ 高。Collections.emptyList()NoOpLogger、默认策略——消灭 if (obj != null) 的利器。每一个防御性编程的 if (x != null) 都是空对象模式的候选替换者。

是什么

空对象模式(Null Object Pattern)的核心思想是:"什么都不做"是一种合法的行为策略,它应该被封装为与其他行为相同的接口,而不是通过 null 值和条件检查来特殊处理

// 没有空对象:到处防御性编程,代码臃肿且容易遗漏
public void log(String message) {
    if (logger != null) {  // 每次调用都要检查!
        logger.log(message);
    }
}

// 有空对象:无需检查,直接调用
public void log(String message) {
    logger.log(message);  // logger 永远不会是 null,它可能是 NoOpLogger
}

// 空对象实现:和其他 Logger 实现相同的接口,但方法体为空
public class NoOpLogger implements Logger {
    @Override
    public void log(String message) {
        // 什么都不做——这就是它的"行为"
    }
}

空对象不是"偷懒",而是将"无操作"提升为一等公民——它和其他策略一样,实现相同的接口,遵循相同的契约,只是语义是"跳过"。

几时出现的

  • 1997 年:Bobby Woolf 在《The Null Object Pattern》一文中正式命名该模式,作为 GoF 策略模式的特例
  • 2004 年:Java 5 引入 Collections.emptyList() / emptyMap() / emptySet(),空对象思想进入标准库
  • 2008 年:SLF4J 的 NOPLogger(No-Operation Logger)成为 Java 日志框架中空对象的典范
  • 2010 年代:函数式编程的 Option/Maybe 类型(Scala None、Haskell Nothing、Rust None)与空对象模式思想相通——"空"不是异常,是一种合法状态
  • 2020 年代:空对象思想渗透到所有现代 API:Java 的 Stream.empty()、JavaScript 的 noop 函数、React 的 Fragment、Go 的 io.Discard

哪里用的多

场景 空对象实现 消灭的 null 检查
日志系统 NoOpLoggerNOPLogger if (logger != null) logger.log(...)
集合操作 Collections.emptyList() if (list != null) for (item : list)
动画/回调 NoOpAnimationListener if (listener != null) listener.onStart()
策略选择 NoOpStrategy(默认策略) if (strategy != null) strategy.execute()
图形渲染 NullShapeNullBrush if (shape != null) shape.draw()
配置读取 NullConfigSource if (config != null) config.getString(...)

为什么不是 P7?

空对象不是"函数式编程专属"。它的核心逻辑极其纯粹:"什么都不做"是一种合法的行为策略

在 P4(行为可变范式)的框架下:

  • 策略模式:算法族可以互相替换,客户端选择其中一个
  • 空对象:策略族中包含一个"什么都不做"的策略,客户端无需知道它选的是"真策略"还是"空策略"

空对象的价值不在于"它做了什么",而在于**“它消灭了 null 检查”**——将运行时的 NullPointerException 风险转化为编译时的类型安全。

与策略模式的关系

  • 空对象是策略模式的特例——策略族中包含一个"什么都不做"的策略
  • 但它独立成模式的价值在于:消灭 if (obj != null) 的防御性编程,将 null 从"异常状态"提升为"合法行为"

推导链

A4: 同一结构在不同上下文下必然表现出不同行为
    ↓
约束: 某些操作在特定条件下需要"什么都不做",但客户端不应频繁进行空值检查
    ↓
P4-规则: "无操作"必须封装为与其他行为相同的接口,作为行为变体之一
    ↓
模式: 空对象(Null Object)

工业级案例

  • Java Collections FrameworkCollections.emptyList()emptyMap()emptySet() 返回不可变的空集合,避免返回 null 导致调用方 NPE
  • SLF4J NOPLogger:当项目中没有绑定具体日志实现时,SLF4J 自动使用 NOPLogger(No-Operation Logger),业务代码无需知道日志是否启用
  • Android NoOpAnimatorListener:动画监听器有四个方法(onStart/onEnd/onCancel/onRepeat),如果只需要监听一个,继承 NoOpAnimatorListener 只需重写一个方法
  • Spring NullMailSender:在测试环境中,用 NullMailSender 替代真实邮件发送器,所有 send() 调用静默忽略,测试代码无需 mock

2.5 归入 P5 信息流转的 1 个新模式

2.5.1 事件总线 / Pub-Sub(Event Bus / Publish-Subscribe)

流行度:⭐⭐⭐ 极高。Android EventBus、RxJS、Node.js EventEmitter、Kafka、RabbitMQ、Redis Pub/Sub——系统级解耦的标准方案。从微服务到前端组件通信,事件总线是"解耦"的代名词。

是什么

事件总线(Event Bus)/ 发布-订阅(Publish-Subscribe)模式的核心思想是:引入一个中央中介(事件总线),发布者(Publisher)不直接持有订阅者(Subscriber)的引用,而是将事件发送到总线;订阅者向总线注册自己感兴趣的事件类型;总线负责将事件分发给所有匹配的订阅者

// 没有事件总线:发布者直接持有观察者引用,耦合在对象级
class OrderService {
    private List<OrderObserver> observers = new ArrayList<>();
    public void createOrder(Order order) {
        // ... 创建订单
        for (OrderObserver o : observers) o.onOrderCreated(order); // 直接调用
    }
}

// 有事件总线:发布者和订阅者完全解耦,通过事件名间接通信
class OrderService {
    private EventBus eventBus;
    public void createOrder(Order order) {
        // ... 创建订单
        eventBus.publish(new OrderCreatedEvent(order)); // 发给总线,不关心谁接收
    }
}

// 订阅者:向总线注册,不关心事件从哪来
class InventoryService {
    @Subscribe  // 或 @EventListener
    public void onOrderCreated(OrderCreatedEvent event) {
        // 扣减库存
    }
}

事件总线与观察者模式的本质区别在于耦合粒度

  • 观察者模式:Subject 直接持有 Observer 的引用列表,对象级耦合(Subject 知道 Observer 的接口类型)
  • 事件总线:Publisher 和 Subscriber 通过**事件名/主题(Topic)**间接耦合,系统级解耦(Publisher 不知道谁订阅了,Subscriber 不知道谁发布了)

几时出现的

  • 1987 年:观察者模式(Observer Pattern)在 GoF 成书前已在 Smalltalk 中广泛使用,但仅限于对象级的一对多通知
  • 2003 年:Enterprise Integration Patterns(Hohpe & Woolf)正式定义"Publish-Subscribe Channel",将 Pub-Sub 提升为企业集成模式
  • 2007 年:Node.js 发布,内置 EventEmitter 模块,emitter.on('event', callback) 将事件总线思想带入前端/服务端 JavaScript
  • 2010 年:Android 开源库 EventBus(greenrobot)发布,@Subscribe 注解让 Android 组件间通信彻底解耦
  • 2012 年:RxJava/RxJS 发布,Observable 流将事件总线升级为"响应式事件流",支持过滤、变换、合并、背压
  • 2014 年:Apache Kafka 成为大数据领域的事实标准事件总线,支持高吞吐、持久化、分区的事件发布-订阅
  • 2020 年代:事件驱动架构(EDA)成为微服务标准,EventBridge、NATS、Redis Pub/Sub、RabbitMQ 都是事件总线的工业实现

哪里用的多

领域 事件总线实现 典型场景
前端开发 RxJS、Vue Event Bus、Redux Actions 组件间通信、状态变更通知
Android greenrobot EventBus、LiveData Activity/Fragment/Service 间解耦通信
Java 后端 Spring ApplicationEvent、Axon Framework 领域事件发布、 Saga 编排
微服务 Kafka、RabbitMQ、NATS 跨服务异步通信、事件溯源
游戏开发 Unity EventManager、Unreal Delegates 游戏事件系统(击杀、拾取、任务完成)
物联网 MQTT Broker(Pub/Sub 协议) 设备状态上报、指令下发

为什么不是 P7?

事件总线不是"分布式领域专属"。一个进程内的 EventBus(如 Android EventBus、Node.js EventEmitter)同样是事件总线。它的核心逻辑与网络无关——一对多广播拓扑在系统级的实现

  • 观察者模式(GoF):微观层面的一对多通知,Subject 和 Observer 在同一个模块内
  • 事件总线:宏观层面的系统级广播,Publisher 和 Subscriber 可以跨模块、跨服务、甚至跨进程

两者是 P5(信息流转范式)在不同规模约束下的两个层次:观察者是"微观",事件总线是"宏观"。

与观察者模式的区别

  • 观察者:Subject 直接持有 Observer 引用,对象级耦合(Subject 必须知道 Observer 的接口)
  • 事件总线:发布者和订阅者通过事件名/主题间接耦合,系统级解耦(Publisher 不知道谁订阅了,Subscriber 不知道谁发布了)

推导链

A5: 信息传递必然有拓扑,拓扑决定耦合度
    ↓
约束: 一个事件需要通知多个订阅者,但订阅者数量动态变化且可能跨模块
    ↓
P5-规则: 信息流转拓扑应为"一对多"发布-订阅,且订阅关系动态管理
    ↓
模式: 事件总线 / 发布-订阅(Event Bus / Pub-Sub)

工业级案例

  • Kafka:高吞吐分布式事件总线,支持分区、持久化、消费者组,成为微服务事件驱动架构的事实标准
  • RxJS:前端响应式编程的核心,Observable 流将事件总线升级为可组合、可变换、可背压控制的异步数据流
  • Spring ApplicationEvent:Spring 框架内置的事件总线,ApplicationEventPublisher.publishEvent() 实现模块间解耦通信
  • MQTT:物联网领域的标准 Pub/Sub 协议,Broker 作为事件总线,设备作为 Publisher/Subscriber,实现低功耗、高并发的消息分发

2.6 归入 P3 × P5 交叉推导的 1 个新模式

2.6.1 中间件管道 / 拦截器链(Middleware Pipeline / Interceptor Chain)

流行度:⭐⭐⭐ 极高。.NET Core app.UseXxx()、Express app.use()、Servlet Filter、Koa 洋葱模型、Spring Interceptor、Django Middleware——Web 框架的基石。没有中间件管道,就没有现代 Web 框架的可扩展架构。

是什么

中间件管道(Middleware Pipeline)/ 拦截器链(Interceptor Chain)的核心思想是:将请求的处理过程拆分为一系列独立的处理单元(中间件),每个单元实现统一的接口,按固定顺序串联成链;请求从链头进入,依次经过每个中间件的处理,最终到达目标处理器;响应沿链反向返回,每个中间件可以在返回路径上执行后置处理

// Express 中间件管道:每个中间件处理请求,然后调用 next() 传递给下一个
app.use(loggingMiddleware);      // 第1步:记录请求日志
app.use(authenticationMiddleware); // 第2步:验证用户身份
app.use(authorizationMiddleware);  // 第3步:检查权限
app.use(rateLimitMiddleware);      // 第4步:限流检查
app.use('/api/orders', orderHandler); // 第5步:业务处理

// 每个中间件的统一结构
function loggingMiddleware(req, res, next) {
    console.log(`${req.method} ${req.url}`); // 前置处理
    next(); // 调用下一个中间件
    console.log(`Response: ${res.statusCode}`); // 后置处理(next 返回后执行)
}

中间件管道的核心特征是**“洋葱模型”:请求像穿过一层层洋葱皮,进入时逐层前置处理,返回时逐层后置处理。每个中间件必须**调用 next()(或等效机制)将控制权传递给下一个节点——这与责任链(“匹配则终止”)和装饰器(“嵌套调用”)都不同。

几时出现的

  • 1998 年:Java Servlet 2.3 规范引入 Filter 链,doFilter(request, response, chain) 成为最早的工业级中间件管道实现
  • 2004 年:Ruby on Rails 引入 before_filter / after_filter,将拦截器思想带入 MVC 框架
  • 2009 年:Node.js + Express 发布,app.use() 将中间件管道推向主流,成为 JavaScript Web 开发的黄金标准
  • 2010 年:Spring 3.1 引入 HandlerInterceptor 接口,preHandle / postHandle / afterCompletion 三阶段拦截成为 Java 生态的标准
  • 2016 年:.NET Core 发布,中间件管道作为框架核心架构,IApplicationBuilder.Use() 成为所有 .NET Web 应用的起点
  • 2017 年:Koa 2.0 发布,async/await 中间件将洋葱模型推向极致,中间件可以等待下游完成后再执行后置逻辑
  • 2020 年代:中间件管道思想渗透到所有框架——从 HTTP 请求处理到 GraphQL Resolver 到 gRPC 拦截器到消息队列消费者

哪里用的多

框架/平台 中间件实现 典型中间件
Node.js/Express app.use(middleware) 日志、CORS、BodyParser、认证、限流
Node.js/Koa app.use(async (ctx, next) => { ... }) 错误处理、JWT 验证、响应压缩
.NET Core app.UseXxx() 路由、认证、静态文件、异常处理、HSTS
Java/Spring HandlerInterceptor / Filter 登录校验、权限检查、日志记录、事务
Python/Django MIDDLEWARE 列表 CSRF 保护、认证、消息框架、点击劫持保护
Python/FastAPI app.add_middleware() CORS、GZip、TrustedHost、认证
gRPC UnaryInterceptor / StreamInterceptor 认证、日志、重试、监控
GraphQL Schema Directive / Middleware 权限校验、数据加载、缓存

为什么不是 P7?

中间件管道不是"Web 领域专属"。任何需要按序处理 + 动态增删处理步骤的系统都可以使用:日志管道、数据处理管道、编译器前端、音频处理链、图像滤镜链。

  • 装饰器模式(Decorator):动态叠加功能,结构是嵌套树(A 装饰 B,B 装饰 C,形成 A(B(C))
  • 责任链模式(Chain of Responsibility):请求逐级传递,直到被处理(某个节点处理后就停止传递)
  • 中间件管道:每个节点都处理请求,且必须调用 next() 继续传递——是装饰器(全部执行)和责任链(线性传递)的交叉产物

与装饰器 + 责任链的关系

  • 装饰器:动态叠加功能,结构是嵌套树
  • 责任链:请求逐级传递,直到被处理
  • 中间件管道:每个节点都处理请求,且必须调用 next() 继续传递——是装饰器(全部执行)和责任链(线性传递)的交叉产物

推导链

A3: 结构必然由单元组合而成,组合操作封闭
A5: 信息传递必然有拓扑
    ↓
约束: 请求需要经过一系列处理步骤,步骤可动态增删重排
    ↓
P3-规则: 每个处理单元实现统一接口,可递归组合(A3)
P5-规则: 信息按固定顺序流经处理单元链(A5)
    ↓
交叉推导: 中间件管道 / 拦截器链(Middleware Pipeline / Interceptor Chain)

工业级案例

  • .NET Core 中间件管道IApplicationBuilderUse()Map()Run() 方法构建完整的请求处理管道,从 Kestrel 接收 HTTP 请求到路由到 Controller 到返回响应,全程由中间件链处理
  • Express 中间件生态body-parsercorshelmetmorgancompression 等数千个中间件包,通过 app.use() 动态组合,无需修改框架源码
  • Spring Security Filter Chain:Spring Security 的全部功能(认证、授权、CSRF、CORS、会话管理)都通过 SecurityFilterChain 实现,开发者可以自定义 Filter 插入到任意位置
  • gRPC Interceptor:客户端和服务端的拦截器链统一处理元数据、认证、重试、监控,实现跨语言的横切逻辑注入
  • Koa 洋葱模型async/await 让中间件可以"等待"下游全部处理完成后再执行后置逻辑(如统计响应时间、记录最终状态码),这是传统同步中间件无法实现的

2.7 仍然留在 P7 的(真正的领域叠加)

以下模式虽然也很流行,但它们的推导必须依赖领域公理,无法从 A1–A5 直接导出:

模式 为什么只能是 P7 领域公理
断路器 Circuit Breaker 需要"网络调用可能失败"的分布公理 分布公理 A:网络不可靠
Saga 模式 需要"分布式事务需要异步协调"的分布公理 分布公理 C:长事务协调
CQRS 需要"读/写负载分离"的分布公理 + 性能约束 分布公理 B:CAP 权衡
事件溯源 Event Sourcing 需要"状态=事件序列折叠"的时序公理 时序公理:状态可追溯
读写锁 需要"读可并行、写需独占"的并发公理 并发公理 B:原子性序列
MVC/MVP/MVVM 需要"界面展示逻辑与业务逻辑变化频率不同"的 UI 公理 UI 公理:变化频率差异

这些模式的共同特征是:去掉领域约束(网络、并发、时序、UI),它们就不存在。而前面纳入的 8 个模式,去掉领域约束后仍然存在——它们是纯公理的必然产物。


第三章:边界声明——体系不覆盖什么

3.1 边界声明的必要性

公理化体系的尊严,来自于它对自己边界的诚实。

欧几里得几何不覆盖非欧几何,牛顿力学不覆盖量子力学,但它们在自己的边界内是完备的、严密的、有用的。5-7-2 框架的价值不在于"覆盖一切",而在于:

在"面向对象系统结构设计"这个边界内,从 5 条不可再约的存在公理出发,可以严密推导出 31 个核心模式,且推导链的每一步都有工程判据校验。

超出这个边界,请使用其他工具。

3.2 与框架推导关系极弱 / 毫无关系的 4 类模式

即使在纯 OOP 领域内,仍有 4 类模式与 5-7-2 框架坐标系不同,无法被推导。

3.2.1 第一类:元模型层模式——需要额外的"类型-身份"公理

5-7-2 框架建立在**“类/类型是设计时的静态结构”**这一隐含假设之上。以下模式挑战的正是这个假设:

ECS(Entity-Component-System)

  • 流行度:⭐⭐⭐ 游戏开发绝对主流(Unity DOTS、Unreal Mass、Bevy)
  • 核心:Entity = 纯 ID,Component = 纯数据,System = 纯行为
  • 为什么无关:ECS 撕毁了 OOP 的核心契约(数据与行为封装在一起)。A1 的"实体"是有行为的对象,ECS 的 Entity 没有行为;A3 的"组合"是对象树,ECS 的 Component 是平铺数组;A4 的"行为可变"不存在,行为在 System 中。
  • 结论:ECS 不是"OOP 模式",它是用 OOP 语言实现的非 OOP 架构(Data-Oriented Design)。

Type Object(类型对象)

  • 核心:用实例替代子类(new Monster(MonsterType.DRAGON)
  • 为什么无关:5-7-2 不覆盖**“类型层级如何扁平化”**的问题。A1–A5 假设了"类/类型"是设计时的静态结构,不是运行时可替换的数据。
  • 结论:需要额外的"类型即数据"公理。

Role Object(角色对象)

  • 核心:对象在运行时动态附加 Role(一个人同时是员工、客户、管理员)
  • 为什么无关:A4 假设"一个对象有一个当前行为",Role Object 假设"一个对象有多个并行的行为集合"。
  • 结论:需要额外的"身份-角色分离"公理。
3.2.2 第二类:谓词逻辑层模式——需要"谓词可组合"公理

Specification(规格模式)

  • 核心:将业务规则封装为可组合的对象(isGoldMember AND orderAmount > 1000
  • 为什么关系牵强:结构上看像 P3(递归组合),但语义完全不同。P3 的组合是"物理结构的组合"(文件夹包含文件),Specification 的组合是"逻辑谓词的组合"(布尔代数)。
  • 结论:需要额外的"谓词可组合"公理(来自命题逻辑/布尔代数),这不是 A1–A5 的内容。
3.2.3 第三类:纯实现惯用法——不是问题,是手法

以下模式不是"设计模式"(Design Patterns),而是**“编程惯用法”**(Idioms)。GoF 明确区分了这两者:模式解决"反复出现的设计问题",惯用法解决"特定语言的实现技巧"。

惯用法 本质 与 5-7-2 的关系
RAII C++ 资源获取即初始化 生命周期绑定语法技巧,不是 A1 的创建策略
Pimpl 编译防火墙 编译期头文件隔离,不是 A2 的运行时分层
CRTP C++ 静态多态 模板元编程技巧,没有运行时对象结构
双分派实现技巧 多态分发优化 访问者模式的实现手段,不是独立设计问题
3.2.4 第四类:特定技术栈的架构约定——上下文太重

以下模式在特定技术栈中极其重要,但把它们从特定技术栈中抽离,它们就不存在了。而 5-7-2 框架中的模式是技术栈无关的

模式 技术上下文 为什么无关
BFF(Backend for Frontend) 微服务 + 多端适配 组织架构与技术栈的耦合产物,不是 A2 的抽象分层
Sidecar Kubernetes / 服务网格 进程级部署模式,不是代理模式的对象级接口控制
Redux / Flux React 前端生态 前端状态管理约定,不是 P4 的状态模式(没有状态类封装行为)
GraphQL Resolver API 层 查询解析与执行的绑定方式,不是解释器模式(语法是固定的)

3.3 边界总图

┌─────────────────────────────────────────────────────────────┐
│                    软件工程全领域                             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  组织/流程层  │  Scrum, Kanban, DevOps, 康威定律              │
│  (不覆盖)   │                                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  算法/数据层  │  MapReduce, Fork-Join, Pipeline, ETL          │
│  (不覆盖)   │                                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  语言/语法层  │  RAII, Pimpl, CRTP, async/await              │
│  (不覆盖)   │                                               │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  技术/架构层  │  BFF, Sidecar, Redux, GraphQL Resolver        │
│  (部分覆盖) │  (仅当抽象为通用结构时,如事件总线可归 P5)      │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  其他范式层  │  Monad, Lens, Unification, Continuation        │
│  (不覆盖)   │  (FP/LP/过程式有自己的公理系统)                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  元模型层    │  ECS, Type Object, Role Object, Specification   │
│  (不覆盖)   │  (需要额外的"类型-身份-谓词"公理)             │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ★ 本框架覆盖 │  对象结构设计模式:创建、结构、行为、信息流转     │
│  ★ 的边界    │  (31 个核心模式,5-7-2 框架内完备)            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

第四章:完备性证明与自洽性

4.1 完备性的定义

在 5-7-2 框架中,完备性定义为:

对于任何面向对象系统设计问题,如果其约束条件可以被 A1–A5 中的至少一条公理所描述,那么其最优解必然落在 P1–P7 中的某个范式之内,且该范式内的具体模式可以通过场景细分唯一确定(在工程判据 V1/V2 的校验下)。

4.2 完备性的证明思路

4.2.1 公理的穷尽性

A1–A5 覆盖了 OOP 系统设计的全部基本维度:

设计问题 对应公理 说明
对象从哪来、怎么创建 A1 实体化公理
对象之间如何交互、如何分层 A2 分层公理
对象如何组合成更大结构 A3 组合公理
对象的行为如何变化 A4 行为可变公理
对象之间如何传递信息 A5 信息拓扑公理

任何 OOP 系统设计问题,必然涉及上述至少一个维度。如果一个问题不涉及任何维度(例如"如何优化 SQL 查询"),则它不在本框架的边界内。

4.2.2 范式的封闭性

对于每个公理维度,P1–P5 提供了该维度下的完备模式族

  • A1 → P1:创建控制的全部策略(唯一实例、委托创建、分步创建、复制创建、预创建复用、外部容器管理、链式可读创建)
  • A2 → P2:接口统一的全部策略(适配、桥接、简化、代理、动态代理、持久化抽象)
  • A3 → P3:递归组合的全部策略(部分-整体、功能叠加、共享复用)
  • A4 → P4:行为可变的全部策略(状态驱动、外部选择、骨架固定、无操作变体)
  • A5 → P5:信息流转的全部策略(广播、星型中转、链式传递、对象化封装、系统级解耦)

P6(结构操作)覆盖了 A3 和 A4 的交叉场景:结构稳定后的操作外置。
P7(领域叠加)覆盖了基础范式在特定领域约束下的演化。

4.2.3 判据的过滤性

V1/V2 确保推导结果在工程上可用:

  • V1 拦截职责不集中的推导(如上帝创建器)
  • V2 拦截扩展需要修改源码的推导(如简单工厂 switch-case)

4.3 自洽性检验

自洽性要求:体系内部不存在矛盾。

检验一:公理独立性
A1–A5 之间不存在推导关系:

  • A1(实体生命周期)不蕴含 A2(分层)
  • A2(分层)不蕴含 A3(组合)
  • A3(组合)不蕴含 A4(行为可变)
  • A4(行为可变)不蕴含 A5(信息拓扑)

检验二:范式完备性
P1–P7 覆盖了 A1–A5 的所有组合:

  • 单公理:P1–P5
  • 双公理交叉:P6(A3 × A4)
  • 全部公理 + 领域:P7

检验三:模式无遗漏
31 个核心模式覆盖了 OOP 结构设计的主流场景,且每个模式有明确的范式归属。

检验四:判据不矛盾
V1 和 V2 之间不存在矛盾:一个结构可以同时满足高内聚和高演化性(如策略模式)。

4.4 不完备性的诚实声明

5-7-2 框架不完备于以下问题:

  1. 非 OOP 架构(ECS、函数式架构)
  2. 元模型设计(类型对象、角色对象)
  3. 谓词逻辑组合(规格模式)
  4. 算法与数据结构(MapReduce、Pipeline)
  5. 组织与流程(Scrum、DevOps)

这些不完备不是缺陷,而是边界——就像欧几里得几何不覆盖非欧几何,但它在欧氏空间内是完备的。


附录 A:5 分钟速览

A.1 5 条公理(记住关键词)

公理 关键词
A1 实体化 对象从哪来、怎么没
A2 分层 复杂必然分层
A3 组合 结构可拆可组
A4 行为可变 行为随上下文变
A5 信息拓扑 信息传递有拓扑

A.2 7 个范式(记住对应关系)

范式 来源 关键词
P1 创建控制 A1 对象创建
P2 接口统一 A2 层间接口
P3 递归组合 A3 结构组合
P4 行为可变 A4 行为切换
P5 信息流转 A5 消息传递
P6 结构操作 A3 × A4 结构稳定、操作外置
P7 领域叠加 A1~A5 + 领域 基础范式 × 领域约束

A.3 2 个判据(记住作用)

  • V1 内聚性:职责是否单一?
  • V2 演化性:扩展是否需要改源码?

A.4 使用流程

  1. 遇到设计问题 → 判断属于哪个公理维度
  2. 定位对应范式 → 在范式内选择具体模式
  3. V1/V2 校验 → 是否工程可用?

附录 B:31 模式完整归属总表

范式 范式名称 推导来源 包含模式 模式数 累计
P1 创建控制范式 A1 单例、工厂方法、抽象工厂、建造者、原型、对象池DI/IoC流式建造者 8 8
P2 接口统一范式 A2 适配器、桥接、外观、代理、动态代理/AOP仓储 6 14
P3 递归组合范式 A3 组合、装饰器、享元 3 17
P4 行为可变范式 A4 状态、策略、模板方法、空对象 4 21
P5 信息流转范式 A5 观察者、中介者、责任链、命令、事件总线/Pub-Sub 5 26
P6 结构操作范式 A3 × A4 访问者、迭代器、备忘录、中间件管道 4 30
P7 领域叠加范式 A1~A5 + 领域公理 解释器、MVC/MVP/MVVM、并发/分布式族 1+ 31+

总计:31 个核心模式(23 GoF + 8 新增)


附录 C:新旧体系三版对照

维度 原书第一版 增补修正版 完备性宣言
公理 7 条(含 2 条万能横切) 5 条核心 + 2 个判据 同增补修正版
范式 6 个(含 1 个双核心混杂 + 1 个剩余类别) 7 个(5 基础 + 1 交叉 + 1 叠加) 同增补修正版
模式 23 GoF 23 GoF 31 个(+8 新增)
边界 未明确声明 初步声明 明确声明 4 类不覆盖模式
完备性 未讨论 未讨论 给出完备性定义与证明思路

全书完

三本书共同构成一个完整的认知闭环:

  • 《23个设计模式精讲全集》:自下而上,建立模式直觉
  • 《设计模式公理化推演体系》:自上而下,构建公理逻辑
  • 《完备性宣言》:精炼体系,纳入新知,明确边界

愿每一位读者都能从"知道模式"走向"理解模式",最终达到"推演模式"的境界——并清楚知道,推演之外,还有广阔天地。

——作者

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐