上下文最小化原则
CMP 的正式表述,以及让它从一句口号变成软件设计比较框架的四个约束。
本文为中文版,根据英文原版改写,而非逐句直译。
如果软件开发是一条连续不断的修改流,而好的设计让这些修改获取充分上下文的成本最小,那么这个直觉值得拥有一个名字。我们称之为上下文最小化原则(Context Minimization Principle,CMP)。把它表述精确,它就成了一条可以拿来争论的原则:
对于系统现实中必须支持的那些修改,正确修改所需的充分上下文越容易获取,设计就越好。
真正起作用的是其中的四个约束。
现实中必须支持
CMP 不在抽象层面给设计排座次,而是拿系统必须支持的、可能出现的未来修改流来评估设计。由于这条修改流是不确定的,“现实中必须支持”并不意味着为每一种可以想象的未来做准备,而是要从产品、领域、架构和组织出发,推断哪些改动足够可信,值得影响设计。
在一个经常新增第三方集成的产品里,插件架构是合理的;在一个小型的内部 CRUD 工具里,它就站不住脚。同一个设计手法,在一种情境下可能非常高明,在另一种情境下却是过度设计,完全取决于它所预期的修改是否真的会到来。
充分上下文
充分性是一个信息条件,相对于下面的正确性标准而言:掌握足够的事实,以消除修改者对改动是否正确的不确定性。CMP 并不降低这个门槛,它只要求同一组充分信息更容易获取,并避开无关的上下文。
正确修改
标准是正确性:改动是完整的,保持了不变量,并产生了预期的行为。
更容易获取
“容易”有两个维度:规模与可发现性。一组充分信息包含的事实越少、这些事实越能直接到达,获取它就越便宜。
一个设计如果缩小了信息集合,却让它仍然隐藏着,就没有降低成本;一个设计如果把信息集合散布在整个代码库里,即便每条事实在局部都很简单,也同样没有降低成本。上下文规模是理论上的量,上下文获取成本才是操作中的量。
这就是为什么单靠“整洁代码”不足以作为设计目标。一个代码库可以在局部一尘不染——每个函数都很短,每个名字都很清楚,每个模块都很专注——却依然带来巨大的上下文成本,因为这些整洁部件之间的关系是分散的、隐性的,或者难以可靠地追踪。
合在一起,这四个约束让 CMP 从一句口号变成了一个比较框架:它以现实中的修改为范围,以充分性为基础,以正确性为约束,并以上下文成本而非单纯可见的规模来度量。
有了 CMP,软件设计中那些熟悉的争论——DRY 与简单、抽象与直接、提早建立结构与 YAGNI——就不再是品味之争,而成为可以用同一种货币比较的权衡:为安全修改而获取充分上下文的成本。
剩下的问题是,上下文成本究竟是如何增长的。在接下来的章节中,我们会看到它沿着两种反复出现的模式扩张:深度(Depth)与广度(Breadth)。大多数经典设计原则,都可以重新理解为对其中一者或两者进行削减、迁移或索引的动作。