里氏替换原则定义
里式替换原则,英文名Liskov Substitution Principle 简称LSP,由麻省理工学院计算机科学实验室的Liskov女士提出来的。
提出来的大意就是:所有引用基类的地方必须能透明地引用其子类的对象,即子类可以拓展父类的功能,但不能修改父类已有的功能。
里氏替换原则是面向对象设计的基本原则,是继承复用的基石。
里氏替换原则约束
如果要符合里氏替换原则的设计,大致需要满足如下约束。
简要分为5大约束:
- 第一:子类对象可以完全替代父类对象;
- 第二:应把基类设计成抽象类而非具象类,应从抽象类派生子类,而不应从具象类继承;
- 第三:子类应该实现父类的抽象方法,而不应该重写父类的已实现方法;
- 第四:子类可以扩充新的功能,而不应该改变父类已有的功能;
- 第五:子类不能增添任何父类没有的附加约束;
里氏替换原则经典示例
接下来通过代码的方式举例说明,看个里氏替换原则经典示例:《鸵鸟不是鸟》,更容易理解里氏替换原则。
首先,我们看下生物学中对于鸟类的定义,这样好抽象设计鸟类。
鸟的定义:恒温动物 卵生,全身披有羽毛 身体呈流线形,前肢退化成翼有四趾。
大多数的鸟类在人们的印象中都是会飞的,所以我们给鸟类设计了一个名字为fly的方法,还给出了与飞行相关的一些属性等。
1.设计鸟类Bird
class Bird { double velocity; public void fly() { //I am flying; } public void setVelocity(double velocity) { this.velocity = velocity; } public double getVelocity() { return this.velocity; } }
2.设计鸵鸟类Ostrich
从生物学角度来看,鸵鸟肯定是一种鸟。
我们设计一个与鸟有关的系统,鸵鸟类顺理成章地由鸟类派生,鸟类所有的特性和行为都被鸵鸟类继承。
class Ostrich extends Bird { public void fly() { //I do nothing; } public void setVelocity(double velocity) { this.velocity = 0; } public double getVelocity() { return 0; } }
3.测试类TestBird
好了,所有的类都设计完成了,我们来测试下。
class TestBird { public calcFlyTime(Bird bird) { try{ double riverWidth = 3000; System.out.println(riverWidth / bird.getVelocity()); }catch(Exception err){ System.out.println("An error occured!"); } }; }
如果我们拿一种飞鸟来测试这段代码,没有问题,结果正确,符合我们的预期。
如果我们再拿鸵鸟来测试这段代码,结果代码发生了系统除零的异常,明显不符合我们的预期。
我们一讲到鸟就认为它能飞,有的鸟确实能飞,但不是所有的鸟都能飞(问题就是出在这里)。
可以看出,Ostrich类和Bird类之间的继承关系违反了里氏代换原则,它们之间的继承关系不成立,鸵鸟不是鸟。
里氏替换原则总结
里氏替换原则是实现抽象化的一种规范,只要能做到子类可以完全替代其父类的行为,那么新增加的具体子类在重写父类的行为时,不会对客户代码产生任何不良的影响。
里氏替换原则是使代码符合开闭原则的一个重要保证,子类尽量不要重写父类的方法,这就是里氏替换原则的核心精髓。
陈睿mikechen
10年+大厂架构经验,资深技术专家,就职于阿里巴巴、淘宝、百度等一线互联网大厂。
关注「mikechen」公众号,获取更多技术干货!
后台回复【面试】即可获取《史上最全阿里Java面试题总结》,后台回复【架构】,即可获取《阿里架构师进阶专题全部合集》