用途:为设计或重构代码提供统一的词汇与原则。目标是为调用方带来杠杆效应、为维护者带来局部性,并让所有人获得可测试性。
核心概念:术语需严格使用,不要替换成 component、service、API 或 boundary。模块指任何具有接口和实现的东西,刻意与规模无关,可以是函数、类、包或跨层切片。接口指调用方正确使用模块所必须知道的一切,包括类型签名、不变量、顺序约束、错误模式、必需配置和性能特征,比 API 或 signature 更宽。实现指模块内部的代码体。深度指接口处的杠杆:调用方或测试每学习一单位接口所能驱动的行为量,大量行为藏在小型接口之后即深,接口几乎与实现一样复杂即浅。接缝(Michael Feathers)指无需在该处编辑即可改变行为的位置,也就是模块接口所在之处。适配器是满足接缝处接口的具体事物,描述角色而非内容。杠杆是调用方从深度获得的东西,局部性是维护者从深度获得的东西。
设计接口时应问:能否减少方法数量?能否简化参数?能否隐藏更多复杂度?
原则:深度是接口的属性而非实现的属性;深模块内部可以由小的、可模拟的、可替换的部分组成,只是它们不属于接口。删除测试:想象删除该模块,如果复杂度消失,它只是直通;如果复杂度在 N 个调用方处重新出现,它在创造价值。接口即测试面:调用方与测试穿过同一接缝。一个适配器意味着假设的接缝,两个适配器才是真实的接缝,不要在没有实际变化的地方引入接缝。
面向可测试性设计:接受依赖而不是创建依赖,例如 processOrder(order, paymentGateway);返回结果而不是产生副作用,例如 calculateDiscount(cart): Discount;保持小表面积,方法越少所需测试越少,参数越少测试设置越简单。
被拒绝的说法:不以实现行数与接口行数之比衡量深度;接口不限于 TypeScript 的 interface 关键字或类的公开方法;避免使用 boundary,因为它与 DDD 的限界上下文混淆。
深入阅读:加深一组模块可参阅 DEEPENING.md;探索替代接口可参阅 DESIGN-IT-TWICE.md。限制:未提供具体安装命令、运行方式、收费或平台信息。