知识卡片
LSP扩展到架构层面:用配置驱动隔离不可替换的接口实现
内容
[[LSP的可替换性定义与正方形长方形问题]]最初只被理解为指导继承关系的原则,但它的适用范围其实更广——任何用户依赖某种接口(Java式带多实现的接口、Ruby式共享方法签名的类、甚至多个服务响应同一REST接口)、并期待各实现之间可以互相替换的场景,LSP都适用。一个出租车调度系统的反面案例说明了违反这一点在架构层面的代价:系统靠调用各出租车公司提供的REST接口(携带pickupAddress、pickupTime、destination字段)来调度车辆,所有公司理应遵守同一份接口约定;但当地最大的Acme出租车公司程序员把destination字段错写成了dest,最简单粗暴的应对是加一条if (driver.getDispatchUri().startsWith("acme.com")) …——但任何称职的架构师都不该允许这种写法进入系统:把公司名硬编码进逻辑分支埋下各种难以预料的错误隐患甚至安全问题,而且一旦Acme收购了Purple出租车公司、双方系统合并却各自保留品牌名,是不是还要再加一条”purple”特例?正确的架构解法是创建一个专门的调度请求生成组件,让它读取一个保存了各家URI组装格式的配置数据库,把”不同实现方不遵守统一接口”这个现实问题隔离在配置层面处理,而不是散落成代码里星罗棋布的特例判断。这个例子印证了本章结论:一旦系统里的可替换性被违背,架构就不得不为此增添大量复杂的应对机制来弥补——LSP因此不只是继承层面的语法建议,而是应当在软件架构层面被认真应用的原则。
参考来源
- 位置:《架构整洁之道》第9章《LSP:里氏替换原则》"LSP与软件架构""违反LSP的案例""本章小结"(源文件:_epub-src/text/part0012_split_003.html)
- 结论依据:原文说明LSP适用于任何接口与其多个实现的可替换场景,用出租车调度系统中Acme公司字段命名不一致导致需要硬编码if特例的案例,说明正确解法是用配置数据库隔离URI组装格式差异,并总结违反可替换性会迫使架构增加大量复杂应对机制,直接支撑本卡片结论。
- 原始内容:LSP逐渐演变成了一种更广泛的、指导接口与其实现方式的设计原则……软件架构师应该创建一个调度请求创建组件,并让该组件使用一个配置数据库来保存URI组装格式……一旦违背了可替换性,该系统架构就不得不为此增添大量复杂的应对机制。