里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

LSP讲的是基类和子类的关系。只有当这种关系存在时,里氏代换关系才存在。如果两个具体的类A,B之间的关系违反了LSP的设计,(假设是从B到A的继承关系)那么根据具体的情况可以在下面的两种重构方案中选择一种。

-----创建一个新的抽象类C,作为两个具体类的超类,将A,B的共同行为移动到C中来解决问题。

-----从B到A的继承关系改为委派关系。

LSP,Liskov Substitution Principle:

1) If for each object s of type S, there is an objectt of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when s is substituted fort when S is a subtype of T.

2) Functions that use pointers or references to base classes must be able to user objects of derived classes without knowing it.

所有引用基类的地方,都能透明地替换成其子类对象。只要父类能出现的地方,子类就可以出现。

引入里氏替换原则能充分发挥继承的优点、减少继承的弊端。

继承的优点:

  • 代码共享,减少创建类的工作量;每个子类都有父类的方法和属性;
  • 提高代码重用性;
  • 子类可以形似父类,但又异于父类;
  • 提高代码可扩展性;
  • 提高产品开放性。

继承的缺点:

  • 继承是侵入性的——只要继承,就必须拥有父类的属性和方法;
  • 降低代码的灵活性——子类必须拥有父类的属性和方法,让子类自由的世界多了些约束;
  • 增强了耦合性——当父类的属性和方法被修改时,必须要考虑子类的修改。

示例(继承的缺点):

原有类A,实现减法功能:

  1. class A{
  2. public int func1(int a, int b){
  3. return a-b;
  4. }
  5. }
  6. public class Client{
  7. public static void main(String[] args){
  8. A a = new A();
  9. System.out.println("100-50="+a.func1(100, 50));
  10. System.out.println("100-80="+a.func1(100, 80));
  11. }
  12. }

新增需求:新增两数相加、然后再与100求和的功能,由类B来负责

  1. class B extends A{
  2. public int func1(int a, int b){
  3. return a+b;
  4. }
  5. public int func2(int a, int b){
  6. return func1(a,b)+100;
  7. }
  8. }
  9. public class Client{
  10. public static void main(String[] args){
  11. B b = new B();
  12. System.out.println("100-50="+b.func1(100, 50));
  13. System.out.println("100-80="+b.func1(100, 80));
  14. System.out.println("100+20+100="+b.func2(100, 20));
  15. }
  16. }

OOPS! 原本运行正常的相减功能发生了错误。原因就是类B在给方法起名时无意中重写了父类的方法!

问题由来:

有一功能P1,由类A完成。现需要将功能P1进行扩展,扩展后的功能为P,其中P由原有功能P1与新功能P2组成。新功能P由类A的子类B来完成,则子类B在完成新功能P2的同时,有可能会导致原有功能P1发生故障。

解决方案:

LSP为继承定义了一个规范,包括四层含义:

        1)子类必须完全实现父类的方法

如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生畸变;则建议不要用继承,而采用依赖、聚集、组合等关系代替继承。

例如:父类AbstractGun有shoot()方法,其子类ToyGun不能完整实现父类的方法(玩具枪不能射击,ToyGun.shoot()中没有任何处理逻辑),则应该断开继承关系,另外建一个AbstractToy父类。

        2)子类可以有自己得个性

即,在子类出现的地方,父类未必就能替代。

        3)重载或实现父类方法时,输入参数可以被放大(入参可以更宽松)

否则,用子类替换父类后,会变成执行子类重载后的方法,而该方法可能“歪曲”父类的意图,可能引起业务逻辑混乱。

        4)重写或实现父类方法时,返回类型可以被缩小(返回类型更严格)

建议:

在实际编程中,我们常常会通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的几率非常大。

父类中凡是已经实现好的方法(相对于抽象方法而言),实际上是在设定一系列的规范和契约,虽然它不强制要求所有的子类必须遵从这些契约,但是如果子类对这些非抽象方法任意修改,就会对整个继承体系造成破坏。而里氏替换原则就是表达了这一层含义。

里氏替换原则通俗的来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。

软件设计原则(三)里氏替换原则 -Liskov Substitution Principle相关推荐

  1. 软件设计原则之里氏替换原则、依赖倒置原则

    系列文章目录 软件设计原则之单一职责原则.开闭原则 软件设计原则之里氏替换原则.依赖倒置原则 软件设计原则之接口隔离原则.合成复用原则.迪米特原则 文章目录 系列文章目录 一.里氏替换原则 什么是里氏 ...

  2. 七大设计原则之里氏替换原则应用

    目录 1 里氏替换原则 2 里氏替换原则应用 1 里氏替换原则 里氏替换原则(Liskov Substitution Principle,LSP)是指如果对每一个类型为 T1 的对象 o1,都有类型为 ...

  3. 6大设计原则之里氏替换原则

    面对对象中的继承 优点如下: 代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性 提高代码的重用性 子类可以形如父类,但又异于父类 提高代码的可扩展性,很多开源框架的扩展接口都是通过继承父类 ...

  4. 深入理解开闭原则、里氏替换原则

    开闭原则(Open-Closed Principle)里氏替换原则 开闭原则(Open-Closed Principle) What 什么是开闭原则? Why 为什么要使用开闭原则和When 什么时候 ...

  5. 开闭原则与里氏替换原则

    1.开闭原则 是面向对象设计的基本原则之一,是"可复用设计"的基础,它的主要原则是:对扩展开放,对修改关闭:意思就是我们改变一个软件时.应该通过扩展方式来改变软件,而不是修改原有的 ...

  6. python里氏替换原则_设计模式六大原则之里氏替换原则

    这是设计模式6 大原则系列的第二篇文章,附上前一篇文章地址:设计模式六大原则之单一职责原则.本文主要讲解设计模式的里氏替换原则. 肯定有不少人跟我刚看到这项原则的时候一样,对这个原则的名字充满疑惑.其 ...

  7. java solid设计原则_六大设计原则之里氏替换原则(LSP)

    一.SOLID 设计模式的六大原则有: Single Responsibility Principle:单一职责原则 Open Closed Principle:开闭原则 Liskov Substit ...

  8. java里氏替换原则例子_java 设计原则(六)里氏替换原则

    定义:如果对每一个类型T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都替换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型. 定义扩展:一个软件 ...

  9. 里氏替换原则_春辉带你了解面相对象设计第二原则(里氏替换原则)

    里氏替换原则的定义 里氏替换原则(Liskov Substitution Principle,LSP)由麻省理工学院计算机科学实验室的里斯科夫(Liskov)女士在 1987 年的"面向对象 ...

  10. 设计原则之里氏替换原则详解

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

最新文章

  1. HTML文本错位,DIV+CSS网页错位诊断和解决方法
  2. TCP/IP / 网关和路由器的区别
  3. wince6.0开机自启动应用程序
  4. python中文件基本操作命令及注意事项
  5. ubuntu下搭建android开发环境(转载)
  6. 【软件测试】黑盒测试の因果图法
  7. Khronos EGL and Apple EAGL
  8. qt 的mysql的库
  9. 华为海思Hi1620芯片发布在即 7nm制程ARM架构最高可达3.0GHz
  10. 修改 Chrome浏览器主页被劫持 chrome 主页被篡改成hao.qquu8.com的解决方案
  11. python 等值面_ANSYS Fluent快速添加等值面/等值线 源代码-教育代码类资源
  12. Process finished with exit code-1073740791(0xC0000409)
  13. oracle12c创建监听,Oracle 12c为PDB创建专用监听
  14. 活动图、类图、顺序图、状态图
  15. 218. 天际线问题
  16. 卡券、直充订单列表接口(post 表单提交)
  17. 使用Python将多个单独的Excel文件整合到一个Excel文件的不同工作表里
  18. 【学习笔记】大三集中实训做的一个微信小程序之点餐系统(静态页面不包含java后台逻辑)
  19. css设置竖排字体时letter-spacing无法有效设置字体间距
  20. 我想去 河南省 郑州春苑置业有限公司 上班 待遇好不好

热门文章

  1. Web前端开发笔记——第三章 CSS语言 第二节 CSS选择器
  2. 银行应用_讲解人脸识别在银行的应用
  3. l2正则化python_机器学习入门之机器学习之路: python线性回归 过拟合 L1与L2正则化...
  4. 卡巴斯基安全浏览器_360安全DNS正式推出DoH安全解析服务,打造安全上网“金钟罩”...
  5. python笔记之for循环
  6. linux 抓包文件 导出,tcpdump抓包和scp导出以及Wireshark查看
  7. python调用c 接口_python调用C接口
  8. 基于python的查重系统_答案在这!如何快速的通过论文查重检测?
  9. oracle查看密码设置,如何查看oracle账号权限设置密码Oracle事务的开始与结束
  10. mysql微服务查询问题_微服务架构下,Mysql读写分离后,数据库CPU飙升卡壳问题解析...