架构整洁之道-06 设计原则-里氏代换LSP

527 阅读2分钟

这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战

里氏替换

里氏替换原则 Liskov Substitution Principle,LSP

Inheritance should ensure that any property proved about supertype objects also holds for subtype objects

继承必须确保超类所拥有的性质在子类中仍然成立。

里氏替换原则,主要在阐述关于继承的一些原则。什么时候使用继承,什么时候不应该使用继承。主要反映基类和子类之间的关系,是对开闭原则的补充,是对实现抽象化的具体步骤的规范。

通过以下Demo来分析,继承时应该避免出现的问题:

class Bird {
  private flyTime: number;
  constructor(flyTime:number){
    this.flyTime = flyTime;
  }
  getFlyTime(){
    console.log(`鸟的飞行时间:${this.flyTime}`);
  }
}
​
class Ostrich extends Bird {
  constructor(fltTime:number){
    super(fltTime)
  }
​
  getFlyTime(){
    console.log(`鸵鸟不会飞`);
  }
}
​
(()=>{
  const ostrich = new Ostrich(12);
  ostrich.getFlyTime();
})();

以上程序是没有问题,但在设计上有问题:Ostrich类继承了Bird类,同时也重写了getFlyTime方法,改变了父类使用该方法是为了计算飞行时间的,这样的调用存在着风险,违反了里氏替换原则。因为里氏替换原则是实现开闭原则的重要方式之一,所以修改了父类的方法,是没有进行扩展,而是进行修改的操作。

解决方案是,去掉他们之间的继承关系。可以新建一个基类,增加计算飞行时间方法和输出特性方法,Bird和Ostrich类分别继承新的基类,实现各自的功能。

如何正确使用继承

  • 子类可以扩展父类的功能,但尽量不改变父类原有的功能
  • 子类的方法实现父类的方法时,条件可以增强但是功能应该是对等的,
  • 子类可以增加自己特有的方法

里氏替换原则作用

  • 它避免了因继承父类造成可复用性降低的缺点
  • 它是动作正确性保证,即父类要实现的功能还是保持一致,避免引入新的错误。
  • 加强程序的健壮性,同时变更时可以做到非常好的兼容性

参考资料