04.里氏替换原则(Liskov Substitution Principle)

暴论:一般的,如果一个富二代不想着证明自己,那么他一辈子都会衣食无忧。

一言

里氏替换原则想告诉我们在继承过程中会遇到什么问题,以及继承有哪些注意事项。


概述

这是流传较广的一个段子:

“一个坐拥万贯家财的富二代,他可以终日花天酒地,飞扬跋扈,跑车炸街,美女为伴,极尽荒唐之能事。只要他不想着证明自己比父亲强,让父辈的产业按既定的规则运转,那么他将一生衣食无忧。”

在这里插入图片描述
看似戏谑的言论实则透露出的是一种稳健的合理。在父辈足够优秀,后人的能力又并非出类拔萃的情况下,不打破既有的优秀机制无疑是最稳妥的选择。段子归段子,玩笑归玩笑。在软件设计中,我们会经常遇到父类子类的继承关系,这个看似荒唐又合理的原则实际上就是里氏替换原则的精髓

OO中的继承性

继承包含这样一层含义:父类中凡是已经实现好的方法,实际上是在设定规范和契约,虽然它不强制要求所有的子类必须遵循这些契约,但是如果子类对这些已经实现的方法任意修改,就会对整个继承体系造成破坏
继承在给程序设计带来便利的同时,也带来了弊端。比如使用继承会给程序带来侵入性,程序的可移植性降低,增加对象间的耦合性,如果一个类被其他的类所继承则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能产生故障。
于是里氏替换原则提供了针对这种问题的规范。

何为里氏替换原则

里氏替换原则在1988年由麻省理工学院的**芭芭拉·里斯克夫(Barbara Liskov)**女士提出。
如果对每个类型为T1的对象O1,都有类型为T2的对象O2,使得以T1定义的所有程序P在所有的对象O1都代换成O2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型。换句话说,所有引用基类的地方必须能透明地使用其子类的对象。
在使用继承时,子类尽量不要重写父类的方法。继承实际让两个类的耦合性增强了,在适当的情况下可以通过聚合、组合、依赖来解决问题。


三寸反骨

我们还是从一个反骨仔的故事开始,为什么里氏替换原则不让重写父类方法?我就要重写父类方法!
反例

public class Story {public static void main(String[] args) {A a = new A();System.out.println("100-50 = "+a.func1(100,50));System.out.println("100-200 = "+a.func1(100,200));System.out.println("-------------------------------");B b = new B();System.out.println("100-50 = "+b.func1(100,50));System.out.println("100-200 = "+b.func1(100,200));System.out.println("(100+200)*10 = "+b.func2(100,200));}
}class A{public int func1(int a, int b){return a-b;}
}class B extends A{public int func1(int a, int b){return a+b;}public int func2(int a, int b){return func1(a, b)*10;}
}

当反骨仔随心所欲的继承重写之后:
在这里插入图片描述
我们发现原本运行正常的相减功能发生了错误。原因就是类B无意中重写了父类的方法,造成原有功能出现错误。在实际编程中,我们常常会通过重写父类的方法完成新的功能,这样写起来虽然简单,但整个继承体系的复用性会比较差。特别是运行多态比较频繁的时候。
子类过分的逆反使得代码极难维护,甚至随着代码量的扩张,真的是牵一发而动全身。


克己正心

听人劝,吃饱饭。反骨仔经过实践之后觉得这个继承之后的重写确实要慎重。那么究竟要如何去优化呢?
通用的做法是:原来的父类和子类都继承一个更通俗的基类,原有的继承关系去掉采用依赖,聚合,组合等关系代替。
在这里插入图片描述
于是经过深思熟虑的代码出现了:

public class Story {public static void main(String[] args) {A a = new A();System.out.println("100-50="+a.func1(100,50));System.out.println("100-200="+a.func1(100,200));System.out.println("---------------------------------");B b = new B();//因为B类不再继承A类,因此调用者,不会在func1求减法System.out.println("100+50="+b.func1(100,50));System.out.println("100+200="+b.func1(100,200));System.out.println("(100+200)*10="+b.func2(100,200));System.out.println("---------------------------------");System.out.println("使用组合依然可以使用到A的方法");System.out.println("100-50="+b.func3(100,50));}
}
class Base{
}
class A extends Base{public int func1(int num1,int num2){return num1-num2;}
}
class B extends Base {private A a = new A();public int func1(int a, int b) {return a + b;}public int func2(int a, int b) {return func1(a, b) * 10;}public int func3(int a, int b) {return this.a.func1(a, b);}
}

我们采用引入基类,子类组合的方式淡化反骨仔的继承关系,进而削弱A、B业务之间的冲突,在一定程度上解耦合。提高了灵活性的同时,也遵循了里氏替换原则。
在这里插入图片描述


里氏替换原则面向类与类之间的继承关系提出了设计规范,在一定程度上规避了业务设计上的杂糅,使得方法在继承关系中更纯粹,也使得设计在扩展方面具有更好的管理性。
事实上,与这个理论很相近的开闭原则才是所有设计的核心。关于开闭原则的拆解我们下次继续!


关注我,共同进步,每周至少一更!——Wayne

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/206594.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

DBS note6:Hashing(哈希存储)

目录 一、一般策略 二、算法简述 三、哈希缺点(Drawbacks of Hashing) 四、举例 五、外部哈希的分析 一、一般策略 由于我们无法一次性将所有数据放入内存中,我们需要构建多个不同的哈希表并将它们连接在一起。然而,这个想法…

第20 章 多线程

20.1线程简介. 20.2创建线程 2.1继承Thread类 Thread 类是java.lang包中的一个类,从这个类中实例化的对象代表线程,程序员启动一个新线程需要建立Thread 实例。Thread类中常用的两个构造方法如下: public Thread():创建一个新的线程对象。 public Threa…

如何提高3D建模技能?

无论是制作影视动画还是视频游戏,提高3D建模技能对于你的工作都至关重要的。那么如何能创建出精美的3D模型呢?本文给大家一些3D建模技能方面的建议。 3D建模通过专门的软件完成,涉及制作三维对象。这项技能在视频游戏开发、建筑、动画和产品…

18、串口通信

串口介绍 串口是一种应用十分广泛的通讯接口,串口成本低、容易使用、通信线路简单,可实现两个设备的互相通信。 单片机的串口可以使单片机与单片机,单片机与电脑、单片机与各式各样的模块互相通信,极大的扩展了单片机的应用范围&…

大模型能否生成搜索引擎的未来?

文|郝 鑫 编|刘雨琦 ChatGPT火爆之前,水面下,也有中国公司也在朝着智能助手的方向努力。夸克便是其中之一。在GPT风靡科技圈后,国内就开始陆续冒出一些大模型厂商。对当时夸克而言,做大模型毋庸置疑&am…

Python list列表添加元素的3种方法及删除元素的3种方法

Python list列表添加元素的3种方法 Python list 列表增加元素可调用列表的 append() 方法,该方法会把传入的参数追加到列表的最后面。 append() 方法既可接收单个值,也可接收元组、列表等,但该方法只是把元组、列表当成单个元素,这…

“逆风飞翔·事实孤儿同行计划”成长陪伴主题区域陪伴培训会

为推进各机构更好地开展事实孤儿成长陪伴工作,促进事实孤儿成长陪伴实施成效,搭建各机构间事实孤儿成长陪伴方式方法交流平台。11月26日,在中国乡村发展基金会、中国民生银行的支持下,由湖南省大爱无疆青少年公益发展中心主办&…

ZZULIOJ 2466: 楼上瞎说,楼下才是,Java

2466: 楼上瞎说,楼下才是 题目描述 《九章算术》的内容十分丰富,全书采用问题集的形式,收有246个与生产、生活实践有联系的应用问题,其中每道题有问(题目)、答(答案)、术&#xff…

MySQL事务详解

MySQL事务详解 数据库事务概述事务是如何实现的事务的ACID特性事务的状态 事务的使用显式事务隐式事务示例自动提交回滚回滚到保存点 事务的隔离级别数据并发问题MySQL 支持的四种隔离级别注意示例 设置隔离级别 事务的常见分类 数据库事务概述 数据库事务是数据库管理系统&am…

【Linux】:信号(一)产生

信号 一.前台进程和后台进程1.前台进程2。后台进程3.总结 二.自定义信号动作接口三.信号的产生1.键盘组合键2.kill信号进程pid3.系统调用1.kill函数2.raise函数3.abort函数 四.异常五.软件条件六.core文件 一.前台进程和后台进程 1.前台进程 一个简单的代码演示 像这种程序在…

华为云之云桌面Workspace的使用体验

华为云之云桌面Workspace的使用体验 一、云桌面Workspace介绍1.云桌面简介2.云桌面特点3. 云桌面应用场景①远程移动办公②协同办公③安全办公④公用终端⑤图形制作渲染 二、本次实践介绍1. 本次实践目的2. 本次实践环境 三、购买云桌面1. 进入华为云的云桌面购买界面2. 选择购…

Linux下删除当前目录下的所有目录

Linux下删除当前目录下的所有目录 Linux下删除当前目录下的所有目录,可以使用命令:rm -rf ./* rm -rf ./*可以得知rm -rf ./命令是删除当前目录下的所有文件和文件夹,但不会删除根目录下的文件。其中,".“代表当前目录&…

ps 透明印章制作

ps 透明印章制作 1、打开不透明印章2、抠出红色印章3、新建图层4、填充红色印章到新图层5、导出透明印章 1、打开不透明印章 打开ps软件,菜单栏选择 文件-打开 选择本地不透明印章 打开 2、抠出红色印章 ps菜单栏 选择 选择-色彩范围 点击色彩范围 色彩范围窗口 取…

Python实现一箭穿心

文章目录 🎄效果🏳️‍🌈Turtle模块🌹代码🌺代码讲解 🎄效果 🏳️‍🌈Turtle模块 Turtle是一个绘图工具,是Python标准库中的一个模块。它提供了一种简单而直观的方式来创…

docker环境安装

环境 主机环境 1. 宿主机环境 ubuntu-22.04.3-live-server-amd64 ,下载地址: https://mirrors.aliyun.com/ubuntu-releases/22.04.3/ubuntu-22.04.3-live-server-amd64.iso 2. apt 包管理器,镜像源修改 : 将 http://cn.archive.ubunt…

【玩转 EdgeOne】| 腾讯云下一代边缘加速CDN EdgeOne 是安全加速界的未来吗?

目录 前言边缘加速与安全加固边缘计算与CDN的融合EdgeOne优秀的安全特性EdgeOne卓越的性能表现灵活的配置和管理生态系统的支持与发展技术创新与未来展望EdgeOne试用结束语 前言 在当下互联网的迅猛发展的时刻,云计算和边缘计算技术的快速发展为网络加速领域带来了…

83基于matlab 的时钟时间识别GUI

基于matlab 的时钟时间识别GUI。图像去除背景-转化为二值化图像-找出对应的直线边缘-找到秒针、分针、时针对应的直线,并算出斜率、角度-判断时间,分针与时针 (度数)。数据可更换自己的,程序已调通,可直接运…

【WSA】无法打开 适用于 Android™ 的 Windows 子系统,因为它处于脱机状态。可能缺少存储设备,或者存储设备已断开连接。

问题描述 之前可以正常使用适用于 Android™ 的 Windows 子系统(WSA),但突然间无法启动了。 当尝试启动WSA中的软件时,都会出现以下错误提示: 无法打开 适用于 Android™ 的 Windows 子系统,因为它处于脱…

分块矩阵知识点整理:

1.分块方法:横竖线不能拐弯,思想为将矩阵分块看作向量计算 2.标准型 不一定是方的 特殊性:经过分块后会出现单位矩阵和0矩阵 3.分块矩阵的运算: 1.加减乘的运算与向量运算相同 4.分块矩阵求转置: 1.将子块看作普通元素求转置 2…

HarmonyOS开发准备(一) TypeScript基本语法

HarmonyOS开发准备(一) TypeScript基本语法 TypsScript官网:https://www.typescriptlang.org/play 可在官网 Playround 在线运行 Typescript 一、变量声明 // 创建 number(数值) 类型变量 let test_number: number 111 console.log(test_number:, tes…