首页/开发/代码质量与评审速查/里氏替换原则 LSP(Liskov Substitution Principle)
设计原则与模式62 条

里氏替换原则 LSP(Liskov Substitution Principle)

子类型必须能替换其基类型而不破坏程序正确性(Liskov 与 Wing 的表述):子类不收紧前置条件、不放松后置条件,调用方对基类成立的推理对子类同样成立。

里氏替换原则是什么

里氏替换原则(LSP)要求子类型能替换基类型而不破坏程序正确性,这是 Liskov 与 Wing 给出的行为子类型表述:子类不得加强前置条件、不得削弱后置条件,也不得破坏基类的不变量。它约束的是行为契约,不是语法上的继承关系——能编译通过不代表能安全替换。

基本信息

提出Barbara Liskov(1987 年主题演讲,1994 年与 Wing 形式化)
契约规则前置条件不加强、后置条件不削弱、不变量保持
经典反例正方形继承矩形:改宽会顺带改高
检查方式调用方对基类成立的推理,在子类上是否仍然成立

典型使用场景

  • 子类为父类方法抛出不支持异常:契约被截断
  • 子类收紧了参数校验:替换后在边缘输入上崩溃
  • 子类改变返回值语义:调用方的断言随之失效
  • 为了复用实现而继承:本该是组合的位置用了继承
  • 第三方类型无法修改:改用适配器或组合对象暴露接口

契约违背示例

契约违背示例
// 矩形:改宽不影响高,调用方据此推理
Rect r = new Square();
r.setWidth(5);
r.setHeight(4);
assert r.area() == 20;   // 在 Square 下不成立,契约被破坏

// 替代方案:让两者平级,各自实现接口
interface Shape { int area(); }

常见违背形式

违背形式表现处理方向
抛出不支持异常子类只实现了部分接口拆细接口,或改用组合
加强前置条件子类额外要求参数非空把校验上移到基类契约中说明
削弱后置条件返回值范围比基类更大保持或收窄返回语义
破坏不变量子类改变了状态之间的关系重新审视继承关系是否成立
LSP 是行为子类型而非语法子类型:编译通过只说明类型兼容,并不代表替换之后语义安全。
协变返回与逆变参数这类语言层规则只是必要条件,真正的判据仍是调用方能否保持原有的推理。

易错点

  • 子类覆写后抛出不支持异常:调用方的契约被截断,错误被推到远离根因的位置
  • 子类悄悄收紧参数校验:替换后在边缘输入上崩溃,且只在特定路径触发
  • 为复用代码而继承:基类不变量被破坏,改动一处影响面难以预估
  • 把编译通过当作检查通过:行为契约的违背不会在编译期暴露出来

数据来源与参考

  • Barbara Liskov 与 Jeannette Wing《A Behavioral Notion of Subtyping》(1994)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌