ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

3.里氏替换原则 透彻

2021-06-01 21:01:12  阅读:174  来源: 互联网

标签:继承 里氏 透彻 子类 父类 替换 加密算法


文章目录

一、定义

  视频链接: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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有