13丨软件设计的里氏替换原则:正方形可以继承长方形吗?
李智慧
该思维导图由 AI 生成,仅供参考
我们都知道,面向对象编程语言有三大特性:封装、继承、多态。这几个特性也许可以很快就学会,但是如果想要用好,可能要花非常多的时间。
通俗地说,接口(抽象类)的多个实现就是多态。多态可以让程序在编程时面向接口进行编程,在运行期绑定具体类,从而使得类之间不需要直接耦合,就可以关联组合,构成一个更强大的整体对外服务。绝大多数设计模式其实都是利用多态的特性玩的把戏,前面两篇学习的开闭原则和依赖倒置原则也是利用多态的特性。正是多态使得编程有时候像变魔术,如果能用好多态,可以说掌握了大多数的面向对象编程技巧。
封装是面向对象语言提供的特性,将属性和方法封装在类里面。用好封装的关键是,知道应该将哪些属性和方法封装在某个类里。一个方法应该封装进 A 类里,还是 B 类里?这个问题其实就是如何进行对象的设计。深入研究进去,里面也有大量的学问。
继承似乎比多态和封装要简单一些,但实践中,继承的误用也很常见。
里氏替换原则
关于如何设计类的继承关系,怎样使继承不违反开闭原则,实际上有一个关于继承的设计原则,叫里氏替换原则。这个原则说:若对每个类型 T1 的对象 o1,都存在一个类型 T2 的对象 o2,使得在所有针对 T2 编写的程序 P 中,用 o1 替换 o2 后,程序 P 的行为功能不变,则 T1 是 T2 的子类型。
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
- 深入了解
- 翻译
- 解释
- 总结
软件设计中的里氏替换原则是面向对象编程中的重要设计原则之一。该原则要求子类能够替换其父类并且不影响程序的功能。本文通过讨论多态、封装和继承的关系,以及里氏替换原则的具体应用和违反情况,深入解释了里氏替换原则的重要性和实际应用。文章还通过具体例子,如马的继承设计和正方形继承长方形的情况,帮助读者理解了里氏替换原则在实际开发中的应用。通过这些例子,读者可以更好地理解里氏替换原则,并在实际开发中避免违反该原则。文章最后提出了解决违反里氏替换原则的方法,为读者提供了实用的建议。通过本文的阐述,读者可以更好地理解和应用里氏替换原则,提高软件设计的质量和可维护性。文章还强调了在实践中,继承一个父类仅仅是为了复用父类中的方法时,很可能会违反里氏替换原则,建议使用组合而非继承。最后,文章留下了一个思考题,引发读者对里氏替换原则的深入思考。
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《后端技术面试 38 讲》,新⼈⾸单¥59
《后端技术面试 38 讲》,新⼈⾸单¥59
立即购买
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
登录 后留言
全部留言(35)
- 最新
- 精选
- 俊杰BException应该是AException的子类,否则当使用子类替换父类后,抛出的BException无法被catch(AException e)语句捕获
作者回复: √
2019-12-2044 - yang满满的干货 子类不能抛出比父类无法catch的异常-因此应该要是AException子类 现实开发中往往经常看到,不同的子类实现了不同的具体方法,而父类只是一个抽象方法。 在方法入口处传入用父类作为形参来接受参数,而在其中又调用父类.abstractMethod(); class abstracr A{ void abstract f(); } class X extneds A { void f(){ pribt("x"); } } class Y extends A { void f(){ print("y"); } } // test(A a); void test(A a){ a.f(); } 老师,这个test(A a); 的使用 ,或者这两个类 X Y, 有违反里氏替换原则吗? (手机输入的)
作者回复: 不违法,抽象方法的正确用法。
2020-01-182 - 靠人品去赢老师你看一下,能不能长方形继承正方形,既然正向不行,那就反向操作。 正方形作为父类,它更严格,长方形作为继承,正方形获取边长getLong(),长方形是getLong(String type)。
作者回复: 亲,不建议继承具体类呢,优先考虑组合而不是继承具体类~
2019-12-2352 - pinteressante这个概念从理解上来说还是比较混沌和违反直觉的. 子类这里的子从字面上理解就是小, 又很容易联想到子集的概念,而一个集合的子集是小于等于自己的. 那么里氏替换原则讲到可以用"子类不能比父类更严格"就会让人在理解上产生困惑: 1. 如果可以替换我干嘛要用子类呢?换句话说,如果只是同级别的类干嘛要产生父子关系,直接定义不就好了?或者说,定义了一些类,抽取他们的共性变成父类,这不就又成了里氏替换原则的反例了么? 2. 如果我目的就是为了缩小范围而不是扩大范围或者范围不变,我定义子类难道还"犯错了"吗? 3. 常见的场景是什么呢?
作者回复: 继承的关键词是extends,就是“扩展”的意思。
2021-02-221 - Peter在类的继承中,如果父类方法的访问控制是 protected,那么子类 override 这个方法的时候,可以改成是 public,但是不能改成 private。因为 private 的访问控制比 protected 更严格,能使用父类 protected 方法的地方,不能用子类的 private 方法替换,否则就是违反里氏替换原则的。 想问下,这个针对protected继承或者private继承也适用的吗?
作者回复: protected 是的。 PS:private方法子类不可见,也就不存在override。
2021-06-19 - 雨天老师,请教两个问题 1.里氏替换原则是关注程序能正常运行,还是系统的逻辑不变(行为功能不变);如果只是程序能正常运行的话,基本上可以说里氏替换原则很难被违反;毕竟基类能被调用的方法,子类一定有;但是如果逻辑不变的话,则子类不能重写父类的非抽象方法? 2.void drawShape(Shape shape) { shape.draw();}中如果用Square直接替换Shape肯定也是不行的;这样的话,就不能接受Circle,即没有多态;这是不是和里式替换有点矛盾?
作者回复: 1 违反原则很可能意味着你的程序设计不良(也不是绝对),而不是不能运行。 2 子类替换父类是用来验证继承设计是否良好,不是真的要把代码中的父类替换了。。。没有意义啊。。。
2021-02-252 - BIZ_DATA_3"这两个类都是从 JDK1 就已经存在的,我想,如果能够重新再来,JDK 的工程师一定不会这样设计。这也从另一个方面说明,不恰当的继承是很容易就发生的,设计继承的时候,需要更严谨的审视。" 李老师能否能够给一些更合理的设计,这样更容易让读者理解
作者回复: 小结里讲了,使用适配器模式,用组合而不是继承
2020-11-06 - 第一装甲集群司令克莱斯特栈stack的数据存储特点是FILO,先进后出吧。
作者回复: 笔误,谢谢指正~
2020-02-013 - 奕里氏替换原则 要求子类可以无缝的替换父类,比父类更松。 但是在实际的开发中,往往是子类比父类更加严格,细化到适合使用在某一应用场景下,目的性越来越明确 父类的设计只是一个比较宽松的限制,子类继承然后重写在某一具体场景下的逻辑2019-12-21715
- 不记年子类 根据里氏变换,父类比子类更严格 => 子类的方法严格性小于父类的 => AException 严格性 大于 BException => AException 是 BException的父类2020-02-016
收起评论