贯穿设计模式第四话--里氏替换原则
🥳🥳🥳 茫茫人海千千万万,感谢这一刻你看到了我的文章,感谢观赏,大家好呀,我是最爱吃鱼罐头,大家可以叫鱼罐头呦~🥳🥳🥳
从今天开始,将开启一个专栏,
【贯穿设计模式】
,设计模式是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案,是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。为了能更好的设计出优雅的代码,为了能更好的提升自己的编程水准,为了能够更好的理解诸多技术的底层源码, 设计模式就是基石,万丈高楼平地起,一砖一瓦皆根基。 ✨✨欢迎订阅本专栏✨✨
🥺 本人不才,如果文章知识点有缺漏、错误的地方 🧐,也欢迎各位人才们评论批评指正!和大家一起学习,一起进步! 👀
❤️ 愿自己还有你在未来的日子,保持学习,保持进步,保持热爱,奔赴山海! ❤️
💬 最后,希望我的这篇文章能对你的有所帮助! 🍊 点赞 👍 收藏 ⭐留言 📝 都是我最大的动力!
📃 前言回顾
🔥【贯穿设计模式】第一话·设计模式初介绍和单一职责原则🔥
🔥【贯穿设计模式】第二话·设计模式的七大原则之开闭原则🔥
🔥【贯穿设计模式】第三话·设计模式的七大原则之依赖倒转🔥
在第三篇文章中,我们了解设计模式的七大原则中第三个原则:依赖倒转原则;
我们来回顾下,它的定义:高层模块不应该依赖低层模块,两者都应该依赖其抽象;抽象不应该依赖细节,细节应该依赖抽象;依赖倒转原则要求我们在程序代码中传递参数时或在关联关系中,尽量引用层次高的抽象层类。
并且我们通过上学时每个学期可能上课的不同导致如果需要再学习一门新课程的需求导致代码的修改等问题,值得注意的是:在实现依赖倒转原则时,需要针对抽象层编程,而将具体类的对象通过依赖注入的方式注入其他对象中。依赖注入是指当一个对象要与其他对象发生依赖关系时,通过抽象来注入所依赖的对象。而常用的注入方式有3种:接口注入、构造注入和Setter注入。
⭐ 前提需要
在面向对象的语言中,继承是必不可少的,它主要有以下几个优点:
- 代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性;
- 提高代码的可重用性;
- 提高代码的可扩展性;
- 提高产品或项目的开放性。
相应的,继承也存在缺点,主要体现在以下几个方面:
- 继承是入侵式的。只要继承,就必须拥有父类的所有属性和方法;
- 降低代码的灵活性。子类必须拥有父类的属性和方法,使子类受到限制;
- 增强了耦合性。当父类的常量、变量和方法修改时,必须考虑子类的修改,这种修改可能造成大片的代码需要重构。
- 从整体上看,继承的“利”大于“弊”,然而如何让继承中“利”的因素发挥最大作用,同时减少“弊”所带来的麻烦,这就需要引入“里氏替换原则”。
🍊 里氏替换原则
今天我们学习的是里氏替换原则,任何基类可以出现的地方,子类一定可以出现。
🫓 概述
-
该原则是指任何基类可以出现的地方,子类一定可以出现,即所有引用基类的地方都必须能够透明的使用其子类;里氏替换原则是继承与复用的基石,只有当子类可以替换掉基类,且系统的功能不受影响时,基类才能被复用,而子类也能够在基础类上增加新的行为;所以里氏替换原则指的是任何基类可以出现的地方,子类一定可以出现;
-
里氏替换原则是对开闭原则的补充,实现开闭原则的关键步骤就是抽象化,而基类与子类的继承关系就是抽象化的具体实现,所以里氏替换原则是对实现抽象化的具体步骤的规范;
-
简单理解就是子类继承父类时,除添加新的方法完成新增功能外,尽量不要重写父类的方法,如果子类强制要重写父类的方法,那么可以再抽象一个基类,为他们的公共父类,或采用依赖、组合、聚合的方式来实现;
-
比如有一个基类A有个实现了的方法,其子类B、C等都可以完全替换A类来实现,但不能影响原有的代码功能,如果子类B、C需要重写父类的方法的话,就会导致子类B、C不能完全替换基类A来使用了,此时应该可以再抽象一个基类,为他们的公共父类,或采用依赖、组合、聚合的方式来实现。
🧇 特点
里氏替换原则是实现开闭原则的重要方式之一,通过里氏替换可以使系统有以下优点 :
- 解决了继承中重写父类造成的可复用性变差的问题;
- 是动作正确性的保证,即类的扩展不会给已有的系统引入新的错误,降低了代码出错的可能性;
- 加强程序的健壮性,同时变更时可以做到非常好的兼容性,提高程序的维护性、可扩展性,降低需求变更时引入的风险。
🧀 问题引出
在上Java相关课程时,学习面向对象的时候,老师都会提动物、猫、狗之间的关系,都会说他们之间的抽象关系或者继承关系,那接下来就以动物的例子来形象的讲解里氏替换原则吧。
1. 定义一个鸟类Bird:
对于鸟类来说,我们对此的第一印象就是鸟类都是能飞的。
package com.ygt.principle.lsp;/*** 新建一个鸟类* 鸟类有个方法是可以非的方法*/
public class Bird {// 鸟的名字private String name;// 构造方法public Bird(String name) {this.name = name;}public void fly(){System.out.println(this.name + "开始飞啦~~~");}
}
2. 定义一个百灵鸟类Lark并继承鸟类的飞行的方法:
package com.ygt.principle.lsp;/*** 创建一个百灵鸟类,继承鸟类*/
public class Lark extends Bird{public Lark(String name) {super(name);}
}
3. 建立一个测试类LiskovSubstitutionTest测试一下百灵鸟的飞行:
package com.ygt.principle.lsp;/*** 测试里氏替换原则*/
public class LiskovSubstitutionTest {public static void main(String[] args) {// 创建百灵鸟,并让其有飞的动作Lark lark = new Lark("百灵鸟");lark.fly();}
}
4. 得到的结果:
百灵鸟开始飞啦~~~
5. 现在新建一个鸵鸟类Ostrich并继承鸟类:
package com.ygt.principle.lsp;/*** 建立一个鸵鸟类,并继承鸟类*/
public class Ostrich extends Bird{public Ostrich(String name) {super(name);}
}
6. 测试鸵鸟的飞行功能:
package com.ygt.principle.lsp;/*** 测试里氏替换原则*/
public class LiskovSubstitutionTest {public static void main(String[] args) {// 创建百灵鸟,并让其有飞的动作Lark lark = new Lark("百灵鸟");lark.fly();// 创建鸵鸟,测试其飞行功能Ostrich ostrich = new Ostrich("鸵鸟");ostrich.fly();}
}
7. 得到的结果:
百灵鸟开始飞啦~~~
鸵鸟开始飞啦~~~
可是现在有个问题,我们知道普遍的鸟类动物都是善于飞翔的,但是也有些鸟类是不会飞行的,就如鸵鸟、企鹅一般的鸟类是不会飞行的,那此时我们在代码中将其继承鸟类,是不合理的,因为如果要在鸵鸟类中重写修改飞行方法的话,这就务必导致违反了里氏替换原则了,我们将不能使用鸵鸟类来替换成鸟类来使用了。下面我们就一起来看看解决方案吧。
🍕 解决方案
在上面的时候说过,当然我们也可以重写飞行的方法,使鸵鸟的飞行功能是无的,但是这就破坏了里氏替换原则,也会导致整个系统的可复用性变差;这时常用的解决方案就是取消原来的继承关系,重新设计他们之间的关系,即使原来的父类(鸟类)和子类(鸵鸟类)都继承一个更通俗的基类(动物类),这样原来的继承关系去掉,最后采用依赖,聚合,组合等关系代替。
1. 定义一个动物类Animal:
package com.ygt.principle.lsp;/*** 定义一个动物类*/
public class Animal {// 动物的名称public String name;public Animal(String name) {this.name = name;}
}
2. 修改鸟类、鸵鸟类和测试类:
package com.ygt.principle.lsp;/*** 新建一个鸟类* 鸟类有个方法是可以非的方法* 修改如下,继承自动物类*/
public class Bird extends Animal {public Bird(String name) {super(name);}public void fly(){System.out.println(super.name + "开始飞啦~~~");}
}
package com.ygt.principle.lsp;/*** 建立一个鸵鸟类,并继承鸟类* 修改如下,不继承鸟类,改继承动物类*/
public class Ostrich extends Animal{// 如果还想使用鸟类的属性以及方法,可以采用依赖方式private Bird bird;public Ostrich(String name) {super(name);}public void run(){System.out.println(name + "开始奔跑****");}
}
package com.ygt.principle.lsp;/*** 测试里氏替换原则*/
public class LiskovSubstitutionTest {public static void main(String[] args) {// 创建百灵鸟,并让其有飞的动作Lark lark = new Lark("百灵鸟");lark.fly();// 创建鸵鸟,测试其飞行功能Ostrich ostrich = new Ostrich("鸵鸟");ostrich.run();}
}
得到的结果:
百灵鸟开始飞啦~~~
鸵鸟开始奔跑****
这样我们通过使原来的鸟类和鸵鸟都继承一个新的基类Animal类后,这就排除了修改鸟类中的飞行方法了,如果还需要使用鸟类中的功能,可以通过依赖、聚合,组合等关系代替。
而我们在实际编程中,常常会通过重写父类的方法来完成新的功能,这样写起来虽然简单,但是整个继承体系的可复用性会比较差,特别是运用多态比较频繁时,程序运行出错的几率非常大。
🌸 完结
相信各位看官看到这里大致都对设计模式中的其中一个原则有了了解吧,里氏替换原则指任何基类可以出现的地方,子类一定可以出现,即所有引用基类的地方都必须能够透明的使用其子类,里氏替换原则是继承与复用的基石,里氏替换原则是对实现抽象化的具体步骤的规范。
学好设计模式,让你感受一些机械化代码之外的程序设计魅力,也可以让你理解各个框架底层的实现原理。最后,祝大家跟自己能在程序员这条越走越远呀,祝大家人均架构师,我也在努力。 接下来期待第五话:接口隔离原则。 💪💪💪
文章的最后来个小小的思维导图:
🧐 本人不才,如有什么缺漏、错误的地方,也欢迎各位人才们评论批评指正!🤞🤞🤞
🤩 当然如果这篇文章确定对你有点小小帮助的话,也请亲切可爱的人才们给个点赞、收藏下吧,非常感谢!🤗🤗🤗
🥂 虽然这篇文章完结了,但是我还在,永不完结。我会努力保持写文章。来日方长,何惧车遥马慢!✨✨✨
💟 感谢各位看到这里!愿你韶华不负,青春无悔!让我们一起加油吧! 🌼🌼🌼
💖 学到这里,今天的世界打烊了,晚安!🌙🌙🌙
相关文章:

贯穿设计模式第四话--里氏替换原则
🥳🥳🥳 茫茫人海千千万万,感谢这一刻你看到了我的文章,感谢观赏,大家好呀,我是最爱吃鱼罐头,大家可以叫鱼罐头呦~🥳🥳🥳 从今天开始,将…...

6501: 鸡兔同笼
描述 一个笼子里面关了鸡和免子(鸡有两只脚,兔子有4只脚,没有例外)。已经知道了笼子里面脚的总数a,问笼子里面至少有多少只动物,至多有多少只动物。 输入 一个正整数a(a<32768)。 输出 包含两个正整数,第一个是最少的动物数,第二个是最多的…...

Linux项目自动化构建工具-make/makefile 介绍及使用
使用背景 在工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义一系列 规则来指定什么文件需要先编译,什么文件需要后编译,哪些文件需要重新编译,或者更复杂 的功能操作 makefile带来的好处…...

【云原生|Docker】06-dokcerfile详解
目录 前言 Dockerfile基础示例 Dockerfile简介 1. Dockerfile概念 2. Dokcer镜像分层理解 3. Doker build构建原理 Dockerfile参数解析 1. Dokcerfile组成 2. 指令说明 2.1 FROM引入基础镜像 2.2 LABEL 2.3 ENV 2.4 RUN 2.5 COPY 2.6 ADD 2…...

【SCL】博图——先入先出排序法
使用博图SCL语言来实现先入先出排序 前言 使用SCL完成一个先入先出排序 具体要求:最先输入的一个数值,最先输出出来,下面的数自动向前填充; 注:这里可能有两种理解:一是第一个输入的第一个出来ÿ…...

OSPF----特殊区域
目录 OSPF----特殊区域 第一大类----末梢区域(Stub Area) 完全末梢区域((Totally Stub Area) 第二大类特殊区域----非完全末梢区域(NSSA) OSPF----特殊区域 第一大类----末梢区域(Stub Area)…...

JVM-类加载
1:类加载机制: 加、验、准、解、初、使、卸 加、烟、准、姐、初、湿、鞋 加载、将class 文件转化为二进制流加载 JVM 内存中并生成一个该类的Class对象验证、Class 文件的字节流中包含的信息是否符合当前虚拟机的要求准备、在方法区中分配这些变量所…...

超详细讲解C语言文件操作!!
超详细讲解C语言文件操作!!什么是文件文件名文件的打开和关闭文件指针文件的打开和关闭文件的顺序读写文件的随机读写fseekftellrewind文本文件和二进制文件文件读取结束的判定文件缓冲区什么是文件 磁盘上的文件是文件。但是在程序设计中,我…...

linxu学习之进程
文章目录进程程序和进程产生进程销毁进程多进程高并发设计孤儿僵尸守护进程孤儿进程:守护进程(重点)僵尸进程:进程 程序和进程 操作系统可以运行多个程序,那他是如何运行的?实际上,CPU的执行是很快的,而待…...

蓝桥杯真题2
[蓝桥杯 2013 省 B] 连号区间数 题目描述 小明这些天一直在思考这样一个奇怪而有趣的问题: 在 111 ~ NNN 的某个全排列中有多少个连号区间呢?这里所说的连号区间的定义是: 如果区间 [L,R][L, R][L,R] 里的所有元素(即此排列的…...

PWM互补输出,以及死区时间计算
本文基于野火例程进行解说 实验内容 本次实验输出一对互补的pwm波,且进行死区时间的计算说明。 代码 互补输出对应的定时器初始化代码: bsp_advance_tim.c /********************************************************************************* fi…...

基于深度学习的海洋动物检测系统(Python+YOLOv5+清新界面)
摘要:基于深度学习的海洋动物检测系统使用深度学习技术检测常见海洋动物,识别图片、视频和实时视频中的海洋动物,方便记录、展示和保存结果。本文详细介绍海洋动物检测系统,在介绍算法原理的同时,给出Python的实现代码…...

C# 计算方差
50,100,100,60,50 计算他们的方差 为了计算这些数的方差,需要进行以下步骤: 1. 计算平均值,即将这些数相加,然后除以它们的数量。 平均值 (50 100 100 60 50) / 5 72 2. 计…...

HJZS电源监视继电器HJZS-E202 AC220V
系列型号: HJZS-E202断电延时继电器 HJZS-E002断电延时继电器 一 应用 HJZS-E202电源监视继电器用于直流或交流操作的各种保护和自动控制的装置中,用以增加触点数量。 二 安装结构 导轨安装9壳体结构,具体尺寸参阅外型尺寸图。 三 产品型号…...

dolphinscheduler 2.0.6 资源中心改造方案二:通过NFS挂载共享目录
目录调度资源中心存储概要安装NFS服务器客户端调度验证关闭SFTP开关(可忽略)重新上传资源文件worker执行任务验证服务器woker客户端worker其它nfs共享目录的配置文件/etc/exports说明调度资源中心存储概要 针对现有的单机存储可以做哪些扩展?…...

基于集成学习的用户流失预测并利用shap进行特征解释
基于集成学习的用户流失预测并利用shap进行特征解释 小P:小H,如果我只想尽可能的提高准确率,有什么好的办法吗? 小H:优化数据、调参侠、集成学习都可以啊 小P:什么是集成学习啊,听起来就很厉害的…...

【Java版oj 】 day17杨辉三角形的变形、计算某字符出现次数
目录 一、杨辉三角形的变形 (1)原题再现 (2)问题分析 (3)完整代码 二、计算某字符出现次数 (1)原题再现 (2)问题分析 (3)完整代…...

智能驾驶芯片赛道混战:如何看待5类玩家的竞争格局?
智能驾驶芯片赛道,一直是业内关注的焦点。 高工智能汽车注意到,针对L0-L2,业内基本采用智能前视一体机(IFC)方案;要实现高速NOA、城市NOA等更为高阶的智驾功能等,则基本采用域控制器方案。从IF…...

vue antd table表格的增删改查(三)input输入框根据关键字模糊查询【后台管理系统 使用filter与indexOf嵌套】
vue antd table表格的增删改查(三)input输入框根据关键字查询【后台管理系统filter与indexOf嵌套】知识回调场景复现利用filter和indexOf方法实现模糊查询1.查询对象为单层的数组元素2.查询对象为多层的数组元素(两层为例)3.查询对…...

【计组】性能指标——速度
衡量计算机性能的指标之一——速度,是指计算机执行完所有指令所耗费时间的长短。 一、概念: 引出了如下概念:机器字长:指计算机一次能处理的二进制位数,也就是我们通常说的32位64位计算机中的位。 机器字长决定了计算…...

【PC自动化测试-4】inspect.exe 详解
1,inspect.exe图解" 检查 "窗口有几个主要部分:● 标题栏。 显示" 检查 HWND (窗口句柄) 。● 菜单栏。 提供对 检查功能 的访问权限。● 工具 栏。 提供对 检查功能 的访问权限。● 树视图。 将 UI 元素的层次结构呈现为树视图控件&…...

比肩ChatGPT的国产AI:文心一言——有话说
🔗 运行环境:chatGPT,文心一言 🚩 撰写作者:左手の明天 🥇 精选专栏:《python》 🔥 推荐专栏:《算法研究》 #### 防伪水印——左手の明天 #### 💗 大家好&am…...

【第13届蓝桥杯】C/C++组B组省赛题目+详解
A.九进制转十进制 题目描述 九进制正整数(2022)9转换成十进制等于多少? 解: 2*9^02*9^12*9^321814581478; B.顺子日期 题目描述 小明特别喜欢顺子。顺子指的就是连续的三个数字:123、456等。顺子日期指的就是在日期的yyyymmdd表示法中&a…...

STM32 KEI 调试新手注意事项
记录一下解决问题的经过:1,用STM32 cubeMX 生成的MKD工程,默认的代码优化级别是level3 , 这个级别 会把一些代码给优化掉,造成一些意想不到的结果,最直观的就是 被优化的语句不能打断点调试,当你打了断点 ,…...

Windows权限提升—令牌窃取、UAC提权、进程注入等提权
Windows权限提升—令牌窃取、UNC提权、进程注入等提权1. 前言2. at本地命令提权2.1. 适用范围2.2. 命令使用2.3. 操作步骤2.3.1. 模拟提权2.3.2. at配合msf提权2.3.2.1. 生成木马文件2.3.2.2. 设置监听2.3.2.3. 设置反弹2.3.2.4. 查看反弹效果3. sc本地命令提权3.1. 适用范围3.…...

不做孔乙己也不做骆驼祥子
对教书育人的探讨前言一、为什么要“育人”1.育人为先2.育人是快乐的二、怎么“育人”前言 借着本次师德师风建设的主题,跟各位老师谈一谈对于“育人”的一些观点,和教育的一些看法。本文仅代表自己的观点,有不到位的地方,大家可以…...

ChatGPT原理解析
文章目录Transformer模型结构构成组件整体流程GPT预训练微调模型GPT2GPT3局限性GPT4相关论文Transformer Transformer,这是一种仅依赖于注意力机制而不使用循环或卷积的简单模型,它简单而有效,并且在性能方面表现出色。 在时序模型中&#…...

常用算法实现【必会】:sort/bfs/dfs
文章目录常用排序算法实现(Go版本)BFS 广度优先遍历,利用queueDFS 深度优先遍历,利用stack前序遍历(根 左 右)中序遍历(左根右)后序遍历(左 右 根)BFS/DFS 总…...

瑟瑟发抖吧——用了这款软件,我的开发效率提升了50%
一、前言 开发中,一直听到有人讨论是否需要重复造轮子,我觉得有能力的人,轮子得造。但是往往开发周期短,用轮子所节省的时间去更好的理解业务,应用到业务中,也能清晰发现轮子的利弊,一定意义上…...

笔记本只使用Linux是什么体验?
个人主页:董哥聊技术我是董哥,嵌入式领域新星创作者创作理念:专注分享高质量嵌入式文章,让大家读有所得!近期,也有朋友问我,笔记本只安装Linux怎么样,刚好我也借此来表达一下我的感受…...