其他分享
首页 > 其他分享> > 3.里氏替换原则 透彻

3.里氏替换原则 透彻

作者:互联网

文章目录

一、定义

  视频链接:https://www.bilibili.com/video/BV1E7411A7mk?p=4&share_source=copy_web

  所有引用基类(父类)的地方必须能够透明地使用子类的对象。也就是能用子类替换父类,替换之后程序的行为没有发生改变。
在这里插入图片描述

二、里氏原则分析

  (1)里氏原则可以通俗地表述为:在软件中如果能够使用基类对象,那么一定能够使用其子类对象。 把基类都替换成它的子类,程序将不会产生任何错误和异常;

  反过来则不成立,如果一个软件实体使用的是一个子类的话,那么它不一定能够使用基类。就比如说,老鼠爸爸会打洞,老鼠儿子也会打洞还会抽烟喝酒烫头,催债公司想招人,这时候老鼠爸爸就不能够替代儿子了。因为子类有可能会在父类的基础上扩展一些父类没有的功能。

  (2)里氏替换原则是实现开闭原则的重要方式之一,由于使用基类对象的地方都可以使用子类对象,因此在程序中尽量使用基类类型来对对象进行定义,而在运行时再确定其子类类型,用子类对象来替换父类。

三、实例1

在这里插入图片描述
在这里插入图片描述

1.存在问题

在这里插入图片描述
在这里插入图片描述

2.里氏替换原则修改

  (1)会发现 DataOperator 类增加或者修改加密算法的时候,源码里面会出现变化点,也就是setCipherA(),setCipherB()和 encrvpt() 。

  根据开闭原则,应该要把变化点,具体的东西进行抽象化,将具体的东西隔离开来,使得它们稳定

  (2)修改如下图:

在这里插入图片描述
  (3)根据里氏替换原则,子类可以替换父类并且对程序的功能没有影响。那么就有了解决问题的思路:在加密算法类里面选出一个父类,其他的作为子类。

  上图的解决办法就是在 DataOperator 类中将加密算法A类声明为父类(抽象类),并将其他的加密算法去掉,只留下父类。其他的加密算法作为子类去继承父类。

  (4)加密算法是一个变化点,客户端选择哪种加密算法是另一个变化点,也要将这个变化点分离出去。之前写代码的时候,把客户端想使用的加密算法放到 main() 里面,想使用A算法,就 new A 类算法,想用 B 算法,就 new B 类算法。如下图:
在这里插入图片描述
  现在就将客户端可能要使用的加密算法作为参数,放到系统外面的一个配置文件中。在配置文件中定义了要可能要使用的算法,在 main() 里面写一个读取XML的方法去读取XML文件里面的节点值。

在这里插入图片描述
  (5)那么 main() 里面的步骤就变成了下图。
在这里插入图片描述
  首先声明一个加密变量,加密变量的类型是A(也就是所有加密算法的父类)

  接着在这里插入图片描述
  然后在这里插入图片描述
注意每一个子类变量都可以赋值给父类变量。

  子类变量赋值给父类变量后,ca 的值就是子类的了,这里就是用基类类型来对对象进行定义,而在运行的时候再确定子类类型
在这里插入图片描述

  如果代码里面有很多的 if else 语句可以考虑用里氏替换原则去重构代码

四、实例2

  员工工资管理系统,不同员工发工资的方式不一样:月薪,日薪,时薪

在这里插入图片描述
  Employee 是抽象类(基类),calPay(A) 工资计算,是一个抽象的方法。发月薪的员工,发日薪的员工继承了基类

  Timecard 是时间卡片用来计算工作时长的(计算时薪用到)

1、引出问题

  假如现在公司来了一批志愿者,为了方便管理也是要录入系统,但是不发工资,该如何处理。

2.处理问题

  (1)处理方法1:增加一个志愿者类,去继承员工类。不发工资的话,那么在实现父类的发工资方法的时候就直接 return 0。如下图:
在这里插入图片描述
  但是这样直接返回0,什么也不干,有意义吗,有业务价值吗。

  (2)处理方法2:对于志愿者的处理,在实现发工资的方法时不计算,抛出异常
在这里插入图片描述
  但是这样又会出现问题:
在这里插入图片描述
  会造成什么影响呢?如果没有志愿者类(抛出异常)之前,发工资的代码如下图,调用者是可以很放心地去调用的,不用小心翼翼的。

在这里插入图片描述
  但是有了志愿者类(抛出异常)之后,必须要对其进行特殊处理,代码也变臭了。
在这里插入图片描述
  (3)处理方法3:将抛出异常的解决方法改变一下:

在这里插入图片描述
  在计算工资之前判断一下是不是志愿者类,如果不是的话就计算工资。这段代码看起来比上一段的简单,但是现在这个种解决方法更糟糕。

  因为这段代码暴露要小心翼翼处理的子类 (志愿者类),而前面的并没有暴露。
在这里插入图片描述

3.问题总结

  (1)上述的解决志愿者类的方法都不是很好,
在这里插入图片描述
  里氏替换原则要求的是,子类替换了父类之后,行为不会发生变化。如果用上述的方法解决志愿者类,行为发生了改变,就是要增加对特殊情况的特殊处理。

  (2)非法使用改变了行为,像增加了特殊处理;
退化的:就是什么也没有实现,比如计算工资的时候 return 0 ,或者抛出异常。
在这里插入图片描述
  (3)继承的真正含义是继承行为,是子类继承父类中声明的行为,并且是具有意义、业务价值地继承

  在父类里面说所有的员工要计算工资,那么所有继承的子类就应该去继承计算工资的行为,并且这个行为应该是具有意义的,具有业务价值的,而不是什么也不干返回0,或抛出异常。

  现在的志愿者类的处理方法没有业务价值,改变了父类的行为,它不应该作为员工类的子类。

五、实例3

  有一个银行账户管理系统,Account 是存款类用户账号,SavingAccount 是长期存款类的用户,CheckingAccount 是短期存款类的用户。interestsRate() 是计算利息方法,将钱存放到银行,用户会获得利息。
在这里插入图片描述
  随着业务的扩展,增加汽车贷款类的用户账户,也就是用户向银行贷款买汽车,每个月要给银行支付利息( interestRate())。

  汽车贷款类用户的账户作为子类继承用户账号类( Account()),但是汽车贷款类的计算利息的行为发生了改变:

短期、长期存款是银行给用户支付利息,而贷款是用户给银行支付利息。对于用户而言,存款时,自己的账户的钱是 +,而贷款是 - 。

六、总结

在这里插入图片描述
  里氏替换原则中,子类继承不只是从语义上面继承父类的行为,还要继承父类的行为的操作、目的或者性质。继承了之后不要产生错误和异常,或者增加特殊处理。

  就比如说爸爸在吃饭,并且是用筷子吃的,吃饭是爸爸的行为,儿子继承了爸爸的行为也去吃饭。在里氏替换原则中,子类要真正继承父类的行为,儿子不仅仅是去吃饭,还要用筷子去吃(和爸爸的行为方式一样),本来爸爸那里只有筷子,不能要求给儿子特别待遇专门去准备一个勺子。

  像上面的例子,志愿者类继承员工类,父类的行为操作是真正计算工资然后发工资给员工,而志愿者类是去处理异常,这并不是真正继承。汽车贷款账户,父类的行为操作是给账户计算利息,发放给账户,而汽车的是计算给银行的利息这些是不一样的。

在这里插入图片描述

标签:继承,里氏,透彻,子类,父类,替换,加密算法
来源: https://blog.csdn.net/qq_43403759/article/details/117445434