引入里氏替换原则(LiskovSubstitution Principle,LSP)的目的就是为了解决继承中父子类之间产生的强耦合性。

2.1 爱恨纠葛的父子关系

继承

在面向对象的语言中,继承是必不可少的、非常优秀的语言机制

继承的优点

● 代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性;

● 提高代码的重用性;

● 子类可以形似父类,但又异于父类,"龙生龙,凤生凤,老鼠生来会打洞"是说子拥有父的"种","世界上没有两片完全相同的叶子"是指明子与父的不同;

● 提高代码的可扩展性,实现父类的方法就可以"为所欲为"了,君不见很多开源框架的扩展接口都是通过继承父类来完成的;

● 提高产品或项目的开放性。

继承的缺点

● 继承是侵入性的。只要继承,就必须拥有父类的所有属性和方法;

● 降低代码的灵活性。子类必须拥有父类的属性和方法,让子类自由的世界中多了些约束;

● 增强了耦合性。当父类的常量、变量和方法被修改时,需要考虑子类的修改,而且在缺乏规范的环境下,这种修改可能带来非常糟糕的结果——大段的代码需要重构。

Java使用extends关键字来实现继承,它采用了单一继承的规则,C++则采用了多重继承的规则,一个子类可以继承多个父类。

从整体上来看,利大于弊,怎么才能让"利"的因素发挥最大的作用,同时减少"弊"带来的麻烦呢?

解决方案是引入里氏替换原则(LiskovSubstitution Principle,LSP)。

里氏替换原则

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

  1. 第一种定义:

虽然比较正宗,但比较晦涩,了解即可

  1. 第二种定义:

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

解释:【】

只要父类能出现的地方子类就可以出现,而且替换为子类也不会产生任何错误或异常,使用者可能根本就不需要知道是父类还是子类。

但是,反过来就不行了,有子类出现的地方,父类未必就能适应。

2.2 纠纷不断,规则压制

里氏替换原则为良好的继承定义了一个规范,一句简单的定义包含了4层含义。

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

我们在做系统设计时,经常会定义一个接口或抽象类,然后编码实现,调用类则直接传入接口或抽象类,其实这里已经使用了里氏替换原则。

// 抽象枪支public abstract class AbstractGun {public abstract void shoot();}public class HandGun extends AbstractGun {@Overridepublic void shoot() {System.out.println("手枪开枪:啪啪啪");}}public class Rifle extends AbstractGun {@Overridepublic void shoot() {System.out.println("步枪开枪:叭叭叭");}}public class MachineGun extends AbstractGun {@Overridepublic void shoot() {System.out.println("机枪开枪:哒哒哒");}}public class Soldier {// 有一把枪private AbstractGun ag;public AbstractGun getAg() {return ag;}public void setAg(AbstractGun ag) {this.ag = ag;}// 杀敌public void killEnemy(){System.out.println("士兵上战场杀敌");ag.shoot();}}public class Client {public static void main(String[] args) {// 创建士兵Soldier s = new Soldier();// 给一支枪//s.setAg(new HandGun());//s.setAg(new Rifle());s.setAg(new MachineGun());// 上阵杀敌s.killEnemy();System.out.println("--------------");// 创建狙击手对象Snipper sanMao = new Snipper();// 给狙击手狙击步枪sanMao.setAug(new AUG());// 创建父类向下转型作为参数//sanMao.setAug((AUG)(new Rifle())); // java.lang.ClassCastException:根本就没有多态何来向下转型之说呢// 杀死敌人sanMao.killEnemy();}}

在编写程序时Solider士兵类根本就不用知道是哪个型号的枪(子类)被传入。

注意事项1

在类中调用其他类时务必要使用父类或接口,如果不能使用父类或接口,则说明类的设计已经违背了LSP原则。

继承就是告诉你拥有父类的方法和属性,然后你就可以重写父类的方法。

但是在具体应用场景中就要考虑下面这个问题了:子类是否能够完整地实现父类的业务。

注意事项2

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

2.子类可以有自己的个性

子类当然可以有自己的行为和外观了,也就是方法和属性。

有时候需要直接方法上的参数需要直接传递子类进来,可能不需要多态,不过这种情况比较少。

从里氏替换原则来看,就是有子类出现的地方父类未必就可以出现。

3.覆盖或实现父类的方法时输入参数可以被放大

方法中的输入参数称为前置条件,"契约优先"的原则。

子类中方法的前置条件必须与超类中被覆写的方法的前置条件相同(重写)或者更宽松(重载)。

4. 覆写或实现父类的方法时输出结果可以被缩小

父类的一个方法的返回值是一个类型T,子类的相同方法(重载或覆写)的返回值为S,

那么里氏替换原则就要求S必须小于等于T,也就是说,要么S和T是同一个类型,要么S是T的子类

重写

父类和子类的同名方法的输入参数是相同的,两个方法的范围值子类方法返回值S小于等于父类方法返回值T。

重载

要求方法的输入参数类型或数量不相同,子类的输入参数宽于或等于父类的输入参数

保证父类的地方替换成子类,不会调用子类与父类的同名方法。

总结

采用里氏替换原则的目的就是增强程序的健壮性,版本升级时也可以保持非常好的兼容性。即使增加子类,原有的子类还可以继续运行。

在实际项目中,每个子类对应不同的业务含义,使用父类作为参数,传递不同的子类完成不同的业务逻辑,非常完美!

2.3 最佳实践

在项目中,采用里氏替换原则时,尽量避免子类的"个性",一旦子类有"个性",

这个子类和父类之间的关系就很难调和了,把子类当做父类使用,子类的"个性"被抹杀——委屈了点;

把子类单独作为一个业务来使用,则会让代码间的耦合关系变得扑朔迷离——缺乏类替换的标准。

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

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

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

  2. 设计模式之里氏替换原则示例

    设计模式之里氏替换原则示例 里氏替换原则强调的是设计和实现要依赖于抽象而非具体:子类只能去扩展基类,而不是隐藏或者覆盖基类,它包含4层含义. 一.里氏替换4原则 1.子类可以实现父类的抽象方法,但不能 ...

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

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

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

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

  5. 【设计模式】里氏替换原则

    里氏替换原则 OO(Object Oriented,面向对象程式设计)中的继承性的思考和说明 继承包含这样一层含义∶父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有
的子类必 ...

  6. 设计模式:里氏替换原则

    里氏替换原则(Liskov Substitution Principle ,LSP): 指的是任何基类可以出现的地方,子类一定可以出现. 定义1 如果对每一个类型为T1的对象o1,都有类型为T2的对象 ...

  7. 设计模式之 里氏替换原则

    里氏替换原则(Liskov Substitution Principle, LSP):所有引用父类的地方必须能使用其子类的对象. 简单来说就是 子类拥有父类所拥有的一切,并可以在此基础上进而扩展其他的 ...

  8. 设计模式:里氏替换原则(详解)

    里氏替换原则 前言 一. 介绍 二.代码演示 1.版本一:原始版本 2.版本二:里氏替换原则 前言 本博主将用CSDN记录软件开发求学之路上亲身所得与所学的心得与知识,有兴趣的小伙伴可以关注博主!也许 ...

  9. 嘻哈说:设计模式之里氏替换原则

    1.定义 按照惯例,首先我们来看一下里氏替换原则的定义. 所有引用基类(父类)的地方必须能透明地使用其子类的对象. 通俗的说,子类可以扩展父类功能,但不能改变父类原有功能. 核心思想是继承. 通过继承 ...

最新文章

  1. 搞机器学习的人,必须要知道的大牛都有谁?
  2. 专访iOS开发框架BeeFramework作者郭虹宇
  3. 一段代码看出JS的的解析到执行的顺序规则
  4. 51nod1227-平均最小公倍数【杜教筛,欧拉函数】
  5. mysql数据库引擎怎么看,值得收藏!
  6. 最佳实践 | 数据库迁云解决方案选型 流程全解析
  7. MongoDB Replication
  8. Python机器学习:SVM006什么是核函数?
  9. 下单送奖励金的实现思路
  10. jQuery – 6.选择器
  11. SQL 删除重复记录,并保留其中一条
  12. java反编译工具Java Decompiler
  13. sir模型matlab案例_直播案例 | 人群接触网络中的 SIR 疫情模拟
  14. linux查看 网卡信息
  15. google地图距离算法,如何计算谷歌地图中两点之间的距离?
  16. 什么叫51单片机最小系统
  17. android 微信分享多张图片大小,Android 关于微信分享图片过大失败的解决方案
  18. 小心啦!水上有“天眼”,查你没商量!
  19. linux编程闹钟小程序,如何做一个番茄闹钟小程序
  20. 2019年中科院信工所夏令营考核内容

热门文章

  1. 一个学习式的mogoLink框架介绍
  2. 使用easyExcel实现浏览器下载Excel。
  3. 计划驱动的软件过程:waterfall model(瀑布模型,或称之为SDLC模型)
  4. 高山流水 (余秋雨)
  5. 荣耀magic3pro和华为p50pro对比哪个好
  6. 四百元左右有什么降噪耳机推荐?高性价比降噪耳机推荐
  7. layui实现空气日历动态日期变色
  8. python 爬虫 网易云音乐 抓取
  9. 会员付费超前点播模式争议背后,我们该怎么看待在线视频的未来?
  10. 全球著名化妆品品牌--SEPHORA丝芙兰验厂