4.2 里氏替换原则

4.2 里氏替换原则里氏替换原则

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.类图

4.2 里氏替换原则

2.相关代码

/ * 里氏替换原则示例 * @author xuezhihui * @date 2020/11/25 19:46 */ object Liskov { @JvmStatic fun main(args: Array 
  
    ) { 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.总结

里氏替换原则核心思想就是建立抽象,通过抽象建立规范,具体的实现在运行时替换掉抽象,保证系统的扩展性,

灵活性,开闭原则与里氏替换原则一般是相伴而生的,通过里氏替换来达到对扩展开放, 对修改关闭的效果;

关于里氏替换原则的例子,最有名的是“正方形不是长方形”。当然,生活中也有很多类似的例子,例如,企鹅、鸵鸟和几维鸟

从生物学的角度来划分它们属于鸟类;但从类的继承关系来看,由于它们不能继承“鸟”会飞的功能,故它们不能定义成“鸟”的子类。

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/227520.html原文链接:https://javaforall.net

(0)
上一篇 2026年3月16日 下午9:08
下一篇 2026年3月16日 下午9:08


相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号