知识卡片

LSP的可替换性定义与正方形/长方形问题

普通读书笔记卡

内容

Barbara Liskov(1988年)对子类型的定义抓住了里氏替换原则的精髓:如果对每个类型为S的对象o1,都存在一个类型为T的对象o2,能让操作T类型的程序P在用o2替换o1后行为保持不变,那么S就是T的子类型——这句话的重心不在继承语法,而在”替换后行为不变”这个可验证的行为契约。一个符合LSP的例子是License类衍生出PersonalLicenseBusinessLicense两个子类型,各自用不同算法计算授权费用,但调用方Billing应用程序的行为完全不依赖于具体用的是哪个衍生类,两者可以随意互换。正方形/长方形问题是违反LSP的经典反例:Square类不是Rectangle类的子类型,因为Rectangle的宽高可以分别独立修改,Square的宽高却必须同步变化——User类始终以为自己在操作Rectangle,如果代码里先setW(5)setH(2),期望面积是10,但如果传入的实际是Square对象,这个断言就会失败。要防范这种情况,唯一的办法是在User类里加if语句去区分RectangleSquare——但这恰恰说明可替换性已经被破坏了:一旦User类的行为需要依赖它具体使用的是哪个类,这两个类就不再能互相替换,LSP的整个意义也就荡然无存。

参考来源

- 位置:《架构整洁之道》第9章《LSP:里氏替换原则》"继承的使用指导""正方形/长方形问题"(源文件:_epub-src/text/part0012_split_003.html) - 结论依据:原文引用Liskov"用o2替换o1后程序P行为保持不变"的子类型定义,用License类衍生类的正面例子和Square/Rectangle的经典反例说明可替换性的判定标准,并指出需要if语句区分具体类型本身就证明可替换性已被破坏,直接支撑本卡片结论。 - 原始内容:如果对于每个类型是S的对象o1都存在一个类型为T的对象o2,能使操作T类型的程序P在用o2替换o1时行为保持不变,我们就可以将S称为T的子类型……如果想要防范这种违反LSP的行为,唯一的办法就是在User类中增加用于区分Rectangle和Square的检测逻辑……这两个类就不能互相替换了。