1.里斯替换原则的定义
0.里氏替换原则(Liskov Substitution Principle, LSP) 由麻省理工学院计算机科学实验室的里斯科夫(Liskov)女士在1987年的面向对象技术的高峰会议(OOPSLA)上发表的一篇文章《数据抽象和层次》里提出:继承必须确保超类所拥有的性质在子类中仍然成立。
1. 继承包含这样一层含义:父类中凡是已实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有子类必须遵循这些契约,
但如果子类对这些已实现的方法任意修改,就会对整个继承体系造成破坏;
2.继承在给程序设计带来便利的同时,也带来了弊端,如使用继承会给程序带来侵入性,程序的可移植行降低, 增加对象间的耦合性,
若一个类被他类继承,则当该类需修改时,必须考虑到其所有子类,且父类修改后,所有涉及到的子类的功能都有可能产生故障;
3.问题提出:在编程中,如何正确使用继承? =>里氏替换原则:所有引用基类的地方必须透明地使用其子类的对象
核心思想:子类继承父类时尽量不重写父类的方法, 如果不能做到子类中所有父类方法不被改变,则可通过聚合,组合,依赖来解决,
即将两者共同部分再提升一个类,这个类只写两者相同部分; (构建扩展性更好的系统)
2.里式替换原则的作用
1.里式替换原则是实现开闭原则的重要方式之一。
2.它克服了继承中重写父类造成的可复用性变差的缺点;
3.它是动作正确性的保证,即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性。
4.加强程序的健壮性,同时变更时可做到非常好的兼容性,提高程序的维护性、可扩展性、降低需求变更时引入的风险;
1.优点
*1.代码重用,减少创建类的工作量,每个子类都拥有父类的方法和属性;
*2.提高代码的延展性,实现父类的方法就可以了,许多开源框架接口都是通过继承父类来完成的;
*3.提高产品或项目的开放性;
2.缺点
*1.继承是侵入性的,只要继承,即必须拥有父类所有的方法和属性;
*2.造成子类代码冗余,降低代码的灵活性,子类必须拥有父类的所有方法和属性,让子类有了一些约束;
*3.增加了耦合性,当父类的常量,变量和方法被修改时,需要考虑子类的修改,重构大量代码;
3.里式替换原则的实现方式
里氏替换原则通俗来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。
*1.子类必须完全实现父类的方法;(定义一个接口或抽象类,然后编码实现,调用类则直接传入接口或抽象类)
*2.子类可以有自己的特性(子类可以定义其他的方法或属性);
*3.当子类方法重载父类方法时 方法的前置条件(即方法的输入参数) 取值范围 或 类族范围一定要大于等于父类中该参数的范围;
由于父类能出现的地方,子类必然能出现,如果子类实现的父类方法中的参数取值范围缩小,可能就会越界;
*4.当子类的方法重写/重载或实现父类方法时,方法的后置条件(方法的输出/返回值) 类族范围要小于等于父类;(子类可替换父类)
4.示例
1.类图
2.相关代码
/**
* 里氏替换原则示例
* @author xuezhihui
* @date 2020/11/25 19:46
*/
object Liskov {
@JvmStatic
fun main(args: Array<String>) {
val a: A = A()
val b: B = B()
println("11-3=" + a.func1(11, 3))
//因为B类不在继承A,所以b再调用func1时,一定是用自己的
println("11+3=" + b.func1(11, 3))
//使用组合仍然可以使用A类的func1方法
println("11-3="+b.func3(11,3))
}
}
//创建一个更加基础的基类
open class Base {
//把更加基础的方法 和 成员写到 Base类
}
open class A : Base() {
//返回两个数的差
open fun func1(num1: Int, num2: Int): Int {
return num1 - num2
}
}
//增加了一个新功能,完成两个数相加再加9
class B : Base() {
//如果B需要使用A类的方法,使用组合关系
//这里,重写了A类的方法,可能是无意识的
var a: A = A()
fun func1(num1: Int, num2: Int): Int {
return num1 + num2
}
fun func2(a: Int, b: Int): Int {
return func1(a, b) + 9
}
//我们仍然使用A的方法
fun func3(a: Int, b: Int): Int {
return this.a.func1(a, b) //使用组合关系调用a的方法
}
}
5.总结
里氏替换原则核心思想就是建立抽象,通过抽象建立规范,具体的实现在运行时替换掉抽象,保证系统的扩展性,
灵活性,开闭原则与里氏替换原则一般是相伴而生的,通过里氏替换来达到对扩展开放, 对修改关闭的效果;
关于里氏替换原则的例子,最有名的是“正方形不是长方形”。当然,生活中也有很多类似的例子,例如,企鹅、鸵鸟和几维鸟
从生物学的角度来划分它们属于鸟类;但从类的继承关系来看,由于它们不能继承“鸟”会飞的功能,故它们不能定义成“鸟”的子类。