最简单直接地理解Java软件设计原则之里氏替换原则

理论性知识

定义

里氏替换原则,Liskov Substitution principle(LSP)。

抽象定义是下面这样的

如果对每一个类型为T1的对象O1,都有类型为T2的对象O2,使得以T1定义的所有程序P在所有的对象O1都替换成O2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。

通俗地解释一下

一个软件实体如果适用一个父类的话,那一定适用于其子类,所有引用父类的地方必须能透明地使用其子类的对象,子类对象能够替换父类对象,而程序逻辑不变。

引申一下,如果子类替换父类,还必须保证程序逻辑不变,就必须满足以下几点。

  • 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
  • 子类中可以增加自己特有的方法。
  • 当子类的方法重载父类的方法时,方法的前置条件(即方法的输入,入参)要比父类方法的输入参数更宽松。
  • 当子类的方法实现父类的方法时(重写,重载或实现抽象方法),方法的后置条件(方法的输出,返回值)要比父类更严格或相等。

优点

加强程序的健壮性,同时变更时可以做到非常好的兼容性。

提高程序的维护性,扩展性,降低需求变更时引入的风险。

代码实战1

实现一个正方形继承长方形的场景,因为正方形是一种特殊的长方形。

定义一个长方形,如下图

定义一个正方形,如下图,重写了父类的方法。

最后执行测试方法,当宽大于等于高时,就改变宽和高。如下图

当用子类正方形替换父类时,程序就会进入无限循环,如下图

根据以上得出,子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法

代码实战2

首先定义一个简单的父类,如下图

然后定义一个子类,子类的入参类型Map比父类的入参HashMap宽松,如下图

最后测试如下图

由测试结果可以看出,child类完全可以替换base类去执行。遵循了里式替换原则,

即当子类的方法重载父类的方法时,方法的前置条件(即方法的输入,入参)要比父类方法的输入参数更宽松。

代码实战3

还是定义一个简单的父类,方法返回类型为HashMap,如下图

定义一个子类,实现父类抽象方法时,返回值类型为Map,编译器就会直接报错,如下图

根据以上可以得出结论

当子类的方法实现父类的方法时(重写,重载或实现抽象方法),方法的后置条件(方法的输出,返回值)要比父类更严格或相等。

个人总结

一开始学习里式替换原则时,也是不明白。最后也是慢慢理解透的。总结起来就是

子类可以替换父类,替换后,不能改变程序的运行逻辑。

也欢迎大家在评论区留下自己的看法!

(0)

相关推荐