里氏替换原则(Liskov Substitution Principle ,LSP):

指的是任何基类可以出现的地方,子类一定可以出现。

定义1

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

定义2

所有引用基类的地方必须能透明地使用其子类对象。

问题由来

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

解决方案

当使用继承时,遵循里氏替换原则。类B继承类A时,除添加新的方法完成新增功能P2外,尽量不要重写父类A的方法,也尽量不要重载父类A的方法。

里氏替换原则包含了四层含义

1.子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法。

实践,以枪为例,看一下类图

枪支类图

枪支的抽象类:

public abstract class AbstractGun {public abstract  void shoot();
}

手枪,步枪实现类:

public class HandGun extends AbstractGun {public void shoot() {System.out.println("手机射击");     }
}
public class Rifle extends AbstractGun {public void shoot() {System.out.println("步枪射击");     }
}

士兵实现类:

public class Soldier {private AbstractGun gun;public void setGun(AbstractGun gun) {this.gun = gun;}public void killEnemy() {System.out.println("士兵杀敌人");gun.shoot();}
}

场景类:

  public class Client {public static void main(String[] args) {Soldier sanMao = new Soldier();sanMao.setGun(new Rifle());sanMao.killEnemy();}
}

注意

在类中调用其他类时务必要使用父类或者接口(例如Solider类的setGun(AbstractGun gun)方法),否则说明类的设计已经违背了LSP原则。

现在有个玩具枪该怎么定义?直接继承AbstractGun类吗?如下:

public class ToyGun extends AbstractGun {@Overridepublic void shoot() {//玩具枪不能像真枪杀敌,不实现}
}

现在的场景类:

  public class Client {public static void main(String[] args) {Soldier sanMao = new Soldier();sanMao.setGun(new ToyGun());sanMao.killEnemy();}
}

在这种情况下,士兵拿着玩具枪杀敌,发现业务调用类已经出现了问题,正常的业务逻辑运行结果是不正确的。(因为玩具枪并不能杀敌)ToyGun应该脱离继承,建立一个独立的类,可以与AbstractGun建立关联委托关系。类图如下:

玩具枪与真实枪分离

按照继承的原则,ToyGun继承AbstractGun是没有问题的,但是在具体应用场景中就需考虑:子类是否能够完整地实现父类的业务,否则就会出现上面的情况拿玩具枪杀敌。

注意

如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中发生重写或者重载,则建议断开父子继承关系,采用依赖、聚集、组合等关系代替继承。

2 子类中可以增加自己特性

子类当然可有自己的方法和属性。里氏替换原则可以正着用,但是不能反着用,在子类出现的地方,父类未必可以胜任。

再说下面两层含义之前先要明白 重载 重写(覆盖) 的区别:

重写(覆盖)的规则:

重载的规则:

3 类的方法重载父类的方法时,方法的前置条件(形参)要比父类方法的输入参数更宽松.

实例:

public class Father {public Collection doSomething(HashMap  map){System.out.println("父类被执行了");return map.values();}
}
public class Son  extends Father{public Collection doSomething(Map  map){System.out.println("子类被执行了");return map.values();}
}
public class Client{public static void main(String[] args) {invoker();}public  static void invoker(){Son son = new  Son();//子类对象HashMap  map=new HashMap<>();son.doSomething(map);}
}

运行是”父类被执行了”,这是正确的,父类方法的参数是HashMap类型,而子类的方法参数是Map类型,子类的参数类型范围比父类大,那么子类的方法永远也不会执行。

4 覆写或者实现父类的方法时输出结果(返回值)可以被缩小

父类的一个方法的返回值是一个类型T,子类的相同方法(重载或者重写)的返回值为S,那么里氏替换原则就要求S必须小于等于T。

总结

有子类出现的地方父类未必就可以出现

设计模式:里氏替换原则相关推荐

  1. 设计模式 里氏替换原则

    设计模式 里氏替换原则 继续里氏替换原则. 上回栗子为正方形不能被长方形继承,这次,具体 子类必须实现父类的方法 类图 对具体代码进行实践 // 定义抽象类枪 public abstract clas ...

  2. 设计模式-里氏替换原则

    设计模式-里氏替换原则 优点 面向对象的语言继承必不可少的,有如下优点 代码共享,减少创建类的工作量 提高代码的重用性 提高代码的可扩展性 提高代码的可扩展性 提高产品代码的开放性 继承侵入性 只要继 ...

  3. 北风设计模式课程---里氏替换原则(Liskov Substitution Principle)

    北风设计模式课程---里氏替换原则(Liskov Substitution Principle) 一.总结 一句话总结: 当衍生类能够完全替代它们的基类时:(Liskov Substitution P ...

  4. 《设计模式》杂记之里氏替换原则

    在这篇博文中,我想把自己学习过的里氏替换原则一些好知识点分享给大家.首先我想把继承的一下优缺点给大家分享一下,然后再引出里氏替换原则吧!<?xml:namespace prefix = o ns ...

  5. 里氏替换原则——举例说明Java设计模式中的里氏替换原则

    里氏替换原则--举例说明Java设计模式中的里氏替换原则 1. 前言 官方定义: 2. 举例说明 2.1 例子介绍 2.2 反例 2.2.1 类图说明 2.2.2 代码说明 2.2.3 测试 2.2. ...

  6. 设计模式六大原则之里氏替换原则、依赖倒置原则详解

    设计模式六大原则--里氏替换原则.依赖倒置原则详解 1.里氏代换原则(Liskov Substitution Principle) 概念 顾名思义,该原则用于经常发生替换的地方,在Java中指的是实现 ...

  7. Java设计模式(03) -- 里氏替换原则

    六大设计原则 单一职责原则定义:约定一个类应该有且仅有一个改变类的原因: 开闭原则定义:规定软件中的对象.类.模块和函数对扩展应该是开放的,但对于修改是封闭的,核心思想也可以理解为面向抽象编程. 里氏 ...

  8. 《设计模式》——里氏替换原则

    先扯两句 原本是不想扯了的,因为很久没扯了也不知道该说写什么,可是这里氏替换原则东西实在是太多了,我看过都快一周了,但是每次想写博客的时候,都写几个字就扔下了,倒不是说书中的内容不够详细,只是如果都是 ...

  9. java设计模式3,里氏替换原则

    目录 一.里氏替换原则定义 二.里氏替换原则的作用 三.违背原则场景 四.里氏替换原则改变代码 1.抽象人物类 2.哪吒子类 3.敖丙子类 五.关注公众号哪吒编程,回复1024,获取Java学习资料, ...

  10. 「设计模式」六大原则之三:里氏替换原则小结

    文章目录 1.里式替换原则定义 2. 举例说明 示例1: 示例2: 3. 哪些代码明显违背了 LSP? 子类违背父类声明要实现的功能 子类违背父类对输入.输出.异常的约定 子类违背父类注释中所罗列的任 ...

最新文章

  1. 安利 10 个 Intellij IDEA 实用插件
  2. 「AHOI / HNOI2018」转盘 解题报告
  3. Hadoop学习之Hadoop集群的定制配置(一)
  4. 使用RMAN验证备份的有效性
  5. 自己动手开发SAP Spartacus focus Directive的单元测试
  6. eclipse加速之禁用 JS、jsp 等文件的语法验证
  7. Flex(flash)检测摄像头的3种状态(是否被占用,没安装摄像头,正常)
  8. Glassnode:比特币正迎来多年以来最大的流动性枯竭
  9. 一个 8 岁的“前端老人”
  10. javaWeb:相关监听方法汇总
  11. 工作中线程池使用不当的问题记录(get是阻塞式的)
  12. 讲教资备考时间和精力
  13. Win10 LTSC 2019进入桌面时假死的拆中处理方法
  14. 51CTO学院三周年
  15. FPS能重塑英雄联盟的辉煌吗?Valorant公测半年有什么优势和不足?
  16. 流媒体(视频)开发常用调试工具
  17. php html注释多行,css多行注释怎么写
  18. 元数据管理-技术元数据解决方案
  19. OCX控件的注册卸载,以及判断是否注册
  20. php 按位与运算,PHP 按位与()运算符应用实践

热门文章

  1. 【图像处理】——傅里叶变换、DFT以及在图像上的应用
  2. 在Windows系统中配置Google AddressSanitizer
  3. oracle使用loop将增加十天,使用loop循环操作DML语句
  4. irq4中断子程序c语言写法,AVR汇编程序参考
  5. JAVA mysql存数组_JAVA数组怎么存放数据库的元素
  6. Potato的暑期训练day#1题解 ——毒瘤构造
  7. laravel框架中引入Workerman
  8. Scrapy将爬取的段落整合为字符串
  9. 对其他组所提建议的回复(第一阶段)
  10. 向anna学习系统结构和测试流程