第一部分:设计模式六大原则解读——什么是里氏替换

本文详细阐述了设计模式中的里氏替换原则(LSP),包括其定义、目的及四个核心要点,强调了如何通过规范继承关系减少代码耦合性和侵入性问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

设计模式六大原则解读——什么是里氏替换

author:陈镇坤27

创建日期:2022年1月26日


——————————————————————————————

这本书让我印象糟糕的原因,来自于这一章节——解释内容貌合神离,无关痛痒,没有直击要害,让我费了老大功夫。

一、里氏替换(Liskov Substitution Principle)

1、里氏替换的定义

If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T,the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T

PS:(这句话原文《设计模式之禅》的作者翻译错误了,请读者知悉,如果按照作者的翻译理解,则与作者后面文章的解释自相矛盾)
a be subsituted for b
意思为a代替b,而不是a被b代替
所以正确的翻译应该是:对每个S类型的对象O1而言,都有一个T类型的对象O2,在被T类型界定的P程序中,都可以用O1代替O2,那么说O1的类型是O2的类型的子类。

简单来说,里氏替换要求我们:引用某个父类的地方也能引用该父类的子类

换言之,就是建议我们在调用某个类时,尽量选择使用其父类作为表面类型。

2、里氏替换的目的

主要为规范继承,减少其不良影响。

并非意味着解决继承问题吧

继承的优点是代码共享——子类都拥有父类的属性和方法。

继承的缺点同样来由于其代码共享特性,表现为代码侵入性——父类有的子类必须有、代码耦合性——父类修改对其继承者子类来说也可能产生连带的修改风险(比如子类调用的是父类的方法,父类的该方法修改后,子类可能编译错误);

3、里氏替换——四点须知

3.1、子类必须完全实现父类的方法

(前置知识:抽象类可以被继承,子类必须实现抽象类的抽象方法。调用表面父类类型的实例实际上是在执行其子类的覆写方法。)

我们操作鼠标键盘,使用步枪向屏幕对面的敌人开枪。这个过程中发生了以下几个调用;

首先,客户端实例化“步枪”,用“枪”接收

其次,调用士兵的“开枪”方法,将“步枪”传入方法的入参“枪”中;

然后,方法中,“枪”调用自己的shoot方法,实际执行“步枪”的shoot重写方法,开枪成功;

如下图所示:
在这里插入图片描述

在上述例子中,为了提高可拓展性,我们拟定了一个“枪”的抽象类来进行开枪射击,然后让各种“能击发伤害性武器”来实例化它,当我们切换武器的时候,只要是开枪,客户端那一头的操作始终是调用开枪方法,唯一的区别只是传入方法的实际对象不同而已。

在这其中,LSP的特性要求调用时父类可以被子类替代,这要求子类必须实现父类的所有方法,而当新增子类,并且在该子类中某些内容发生了畸变时,则最好断开两者的父子类关系(作者的这个例子,实际上最核心的意义就在此),改为其他关系进行处理,否则会造成歧义。

例如玩具枪也可以射击,与枪十分类似,如果继承了上述的“枪”抽象类,在别的地方被引用貌似无伤大雅,但在士兵引用的时候,就会出现歧义。

下面是书中两句原话:

*注意 在类中调用其他类时务必要使用父类或接口,如果不能使用父类或接口,则说明类的设计已经违背了LSP原则*
*注意 如果子类不能完整地实现父类的方法,或者父类的某些方法在子类中已经发生“畸变”,则建议断开父子继承关系,采用依赖、聚集、组合等关系代替继承。*
3.2、子类可以有自己的个性,子类被父类替换,有可能不行

在类的关系上,以父类强转子类为例,有可能会出现报错异常。父类的引用可以被子类替换,但子类的引用不一定能被父类替换。

3.3、覆盖或实现父类的方法时输入参数的范围应该等价或放大

里氏替换原则要求父类的引用能被子类替换,并且程序的逻辑行为不发生改变。也就是说,父类调用的方法,被子类替换后,实质上也是在执行父类的方法。如果子类的同名方法的输入参数范围比父类要小,则该方法为独属该子类的 重载方法(继承就是“我”拥有了父类的所有方法),与父类无关,存在违背里氏替换要求的风险。

可能有人说,“那我就是想要换个新逻辑,但不引起旧的程序的报错啊”。如果是这样子,干嘛还要和旧的父类建立父子关系呢。直接在调用端引入就可以了。

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

这是覆写的要求。

4、建议

子类避免有个性。因为有个性的子类,在替换父类的时候,里氏替换要求程序的行为逻辑不变,则意味着个性用不到。如果把该具有个性的子类脱离父类使用,则缺乏规范的代码约束——类替换标准。

5、总结

建议子类与父类的方法一一对应,尽量减少重载。

逻辑不变,则意味着个性用不到。如果把该具有个性的子类脱离父类使用,则缺乏规范的代码约束——类替换标准。

5、总结

建议子类与父类的方法一一对应,尽量减少重载。

里氏替换的思考,主要出现在abstract的父子类和普通父子类之间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈镇坤27

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值