用途:为设计“深模块”提供共享词汇与原则,在设计与重构代码时使用,目标是给调用方带来 leverage、给维护者带来 locality,并让所有人易于测试。
主要能力与核心概念:
- Module:任何具有接口与实现的东西,刻意不限尺度(函数、类、包或跨层切片)。
- Interface:调用方正确使用模块所必须知道的一切,包括类型签名之外的不变量、顺序约束、错误模式、必需配置与性能特征。
- Implementation:模块内部的代码主体,与 Adapter 区分。
- Depth:接口层面的 leverage,即调用方(或测试)每学习一单位接口能行使的行为量。接口小、行为多即深;接口几乎和实现一样复杂即浅。
- Seam(Michael Feathers):可以在不修改该处的情况下改变行为的位置,即模块接口所在之处,放在哪里是独立的设计决策。
- Adapter:在 seam 上满足接口的具体事物,描述角色而非内容。
- Leverage 与 Locality:分别指调用方从深度获得的能力,以及维护者获得的变更、缺陷、知识与验证的集中。
使用方式(设计时自问):能否减少方法数量?能否简化参数?能否把更多复杂性藏在内部?
原则:深度是接口的属性而非实现的属性;模块内部可有小、可 mock、可替换的部件以及内部 seam。删除测试:设想删除该模块,若复杂性消失则是直通,若复杂性在 N 个调用方重现则值得保留。接口即测试面,调用方与测试穿过同一个 seam。一个 adapter 意味着假想 seam,两个 adapter 才是真 seam;没有实际变化就不要引入 seam。
可测试性:接受依赖而非自行创建,例如 function processOrder(order, paymentGateway) {} 优于内部 new StripeGateway();返回结果而非产生副作用,例如 function calculateDiscount(cart): Discount {} 优于直接修改 cart.total;保持小表面积,更少方法与参数意味着更少测试与更简单准备。
限制与边界:术语需严格使用,不要替换为 component、service、API 或 boundary;接口不限于 TypeScript interface 关键字或类公有方法;不采用深度等于实现行数与接口行数之比的框架。关于深化依赖簇与探索替代接口,资料提及 DEEPENING.md 与 DESIGN-IT-TWICE.md,但未提供其内容细节。