七、LSP 里氏替换原则

子类的对象提供了父类的所有行为,且加上子类额外的一些东西(可以是功能,可以是属性)。当程序基于父类实现时,如果将子类替换父类而程序不需修改,则说明符合LSP原则。

这个解释看的似懂非懂,再看下面更进一步的解释:

函数使用指向父类的指针或引用时,必须能够在不知道子类类型的情况下使用子类的对象。

子类必须能够替换成它们的父类

这其中存在这样的概念:方法调用者(C)和方法提供者(P)。C调用P提供的方法,P的方法返回给C处理的结果。中间的过程C是不需要知道,也不会知道的。当C调用P的方法时,如果将P替换成sub_P的子类,那么C得到的输出结果也是正确的,C无需修改自身。

这里还有个问题是如何满足LSP原则,或者说,什么情况下子类才可以替换父类?要满足LSP原则,需要遵守以下原则:

  1. sub_P必须实现或继承P的所有公共方法,否则C调用P中有,而sub_P中没有的方法,那么运行时就会出错。
  2. sub_P每个方法的输入参数必须和P一样,否则调用父类的代码不能调用子类。
  3. sub_P每个方法的输出必须不比P少,否则基于父类的输出做的处理就无法完成。

第三条中所说的“不比父类少”,是指子类的输出可以比父类多,也就是父类方法的输出是子类方法的子集。

面向对象语言提供的继承,已经天生的满足了1,2两条。除非是子类重写父类的方法,那么就要依照1,2的原则来重写。(重写与重载的区别:重写要求函数名,传参个数类型,返回值类型必须相同,访问修饰符子类的必须大于父类的。仅函数名相同,其他不同,则为重载。重载是多态的一种实现 详解传送门

从上面的三条原则中可以看出约定只是约束了父类、子类的输入输出,并没有约束中间的处理过程。

经典的LSP例子:长方形与正方形。数学概念中正方形是长方形的特殊情况,也就是说正方形是长方形的子类。但是在面向对象领域中,正方形是不能作为长方形的子类的。因为正方形设置了高就等于设置了宽,设置了宽就等于设置了高。那么按照长方形的计算规则,最终面积将是最后一次设置高或宽的数值的乘积。长方形高5,宽4,面积为20。替换成正方形后,最后一次设置的如果是高的值,那么面积将是25;如果是宽,那么面积将是16。都不等于20。那么就不能用正方形来替换长方形,否则C调用的结果会不同。

原文地址:https://www.cnblogs.com/mysic/p/8660827.html