OTAKU 是一个首字母缩写,每一个字母都是一条约束。四十条规则让它可度量;一个指标决定一个方法是否复杂到不该存在。
这套哲学有坚硬的核心和柔软的边缘。核心——五个支柱及其可度量的阈值——不可谈判。边缘——语言、框架、风格、工具——自由。
五个支柱的顺序是有意的。纪律在先,因为没有它其余都无法存活;契约在最后,因为它是外部世界唯一能看到的东西。
O
自律
对自己严格。
限制是通往自由的唯一道路。复杂度、规模和命名都靠度量,而不是争论。
T
组合的工具
像 Unix 一样组合。
小而清晰的输入输出部件。应用是一条数据管道,副作用留在边缘。
A
装甲核心
保护业务核心。
依赖只向内。实体与用例永远不知道框架的名字。
K
让基础设施保持沉默
基础设施只是细节。
持久化像内存集合一样工作。失败是显式的,状态默认不可变。
U
通用语言与契约
说用户的语言。
把通用语言写进设计先行的契约。原始类型被包装;每个公有方法都有契约。
核心指标
圈复杂度——Thomas J. McCabe 提出的方法独立路径数——是这套哲学的主要仪器。圈复杂度超过 5 的方法必须被拒绝,无论它看起来多易读。五条路径已经是读者必须同时持有的五种推理。
纪律部分的其余内容都是压低这个数字的手段:一层缩进、不用 ELSE、不用 switch/case、卫语句、抽取。这个指标在本书的每种语言里都可度量,每个生态都有报告它的工具。
四十条规则
每条规则都有可度量的阈值。拿不准时,选择更严格的选项。
40 条规则 · 7 组 · 5 个支柱
O复杂度
- 每个方法的圈复杂度不得超过 5。
- 每个方法最多一层缩进。
- 不要使用 ELSE 关键字(使用卫语句或提前返回)。
- 不要使用 switch/case(使用多态、match 表达式或策略模式)。
- 避免魔法数字和魔法字符串(使用常量或枚举)。
O规模
- 每个类最多 50 行(仅类体中的代码)。
- 每个方法最多 20 行(仅可执行代码)。
- 每个命名空间或包最多 10 个类。
- 类中不得有超过两个实例变量。
- 方法最多两个参数。
- 构造函数最多两个参数。
- 接口最多两个方法。
O命名
- 不要缩写,使用完整、描述性的名称。
- 布尔方法以 is、has、can 或 should 开头。
- 方法名以动词开头。
- 常量使用 UPPER_SNAKE_CASE。
- 属性和方法使用 camelCase。
T模块化
- 一等集合:将数组包装在专用类中。
- 每行一个点;为可读性避免方法链。
- 禁止静态方法,工厂和纯函数除外。
- 命令与查询分离:有返回值的方法不得有副作用。
- 尽可能使用纯函数:相同输入产生相同输出,无副作用。
- 不得使用全局状态或单例;显式注入依赖。
- 偏好小而专注的函数;一个方法做多件事时就抽取。
A保护
- 不要 getter、setter 或公有属性;使用方法表达行为。
- 属性默认私有;仅框架继承时使用 protected。
- 类默认 final;通过接口进行扩展。
- 除框架基类外禁止继承;优先使用组合。
- 在边界处(构造函数和公有方法)验证所有输入。
- 快速失败:一旦违反前置条件就抛出异常。
- 不要修改输入参数;将其视为不可变。
K副作用与失败
- 优先不可变:只读属性,返回新实例。
- 返回集合的方法应返回空集合,绝不返回 null。
- 可能失败的方法返回 Result/Either 或抛出异常;绝不返回 null。
- 显式优于隐式:不得有隐藏依赖、魔术方法或隐式转换。
- 绝不将基础设施细节(SQL、HTTP、文件路径)泄漏到领域层。
U契约
- 将所有原始类型和字符串包装为值对象。
- 除显式序列化方法外,不得使用泛型类型(mixed、array、object)。
- 每个公有方法都必须在接口中声明。
- 注释解释原因,属于契约,而非实现。
支柱如何相互支撑
纪律使组合成为可能:300 行的类无法替换。组合使核心可防卫:单体无法隐藏依赖。受保护的核心让基础设施真正可选。契约是这一切能从外部验证的唯一原因。
去掉一根支柱,其余就沦为仪式。保留五根,系统会变得无聊——就像桥梁的无聊。