软件设计原则-里氏替换原则讲解以及代码示例
里氏替换原则
一,介绍
1.前言
里氏替换原则(Liskov Substitution Principle,LSP)是面向对象设计中的一条重要原则,它由Barbara Liskov在1987年提出。
里氏替换原则的核心思想是:父类的对象可以被子类的对象替换,而程序的行为不会发生变化。也就是说,如果一个类型A是另一个类型B的子类型,那么在任何使用B的地方都可以使用A,而不会引起错误或异常。
2.何时使用里氏替换原则
-
当需要编写基类或抽象类时:在编写基类或抽象类时应该尽可能地遵循里氏替换原则,以保证后续的子类能够正确地继承和使用基类的接口或者抽象类的方法。
-
当需要对已有的代码进行重构时:在重构已有的代码时,我们可以通过遵循里氏替换原则,使得代码更加易于理解、扩展和维护。通过将某些动态绑定的行为转化为静态绑定的行为,可以降低代码的复杂度并增强其可控性。
-
当需要进行单元测试或集成测试时:在进行单元测试或集成测试时,我们可以使用子类对象来替换父类对象,以确保测试结果的准确性。如果使用子类对象无法替换相应的父类对象,则表示可能存在设计上的问题,需要进一步优化。
-
当需要扩展系统的功能时:在扩展系统的功能时,我们应该尽可能地遵循里氏替换原则,以确保新的组件能够与现有的组件正常协作。通过使用基类或抽象类来定义接口,可以使得组件之间的耦合度更低。
二,代码示例
为了更详细地介绍里氏替换原则,我们可以通过一个例子来说明:
假设有一个图形计算程序,程序可以计算不同形状图形的面积。最初的设计可能会像这样:
class Shape {// 省略其他属性和方法public double calculateArea() {// 默认实现,返回0return 0;}
}class Rectangle extends Shape {private double width;private double height;// 省略构造方法和其他属性方法@Overridepublic double calculateArea() {return width * height;}
}class Circle extends Shape {private double radius;// 省略构造方法和其他属性方法@Overridepublic double calculateArea() {return Math.PI * radius * radius;}
}
这个设计看起来似乎没有问题,但问题在于当我们需要添加新的图形类型时,比如三角形,计算面积的方式与矩形和圆形不同,会导致父类的默认实现无法满足需求。
为了符合里氏替换原则,我们可以进行重构。首先,我们定义一个抽象类`Shape`:
abstract class Shape {public abstract double calculateArea();
}
然后,对每种具体的图形类型,创建一个子类并实现`calculateArea()`方法:
class Rectangle extends Shape {private double width;private double height;// 省略构造方法和其他属性方法@Overridepublic double calculateArea() {return width * height;}
}class Circle extends Shape {private double radius;// 省略构造方法和其他属性方法@Overridepublic double calculateArea() {return Math.PI * radius * radius;}
}class Triangle extends Shape {private double base;private double height;// 省略构造方法和其他属性方法@Overridepublic double calculateArea() {return 0.5 * base * height;}
}
现在,我们可以通过扩展子类来添加新的图形类型,而且每个子类都提供了自己的面积计算方式。
这个重构后的设计符合里氏替换原则,因为我们可以将子类的对象替换父类的对象,而不影响程序的行为。这样做的好处是,通过面向抽象编程,代码更加灵活、可扩展,同时也提高了系统的可维护性和可测试性。
总结起来,里氏替换原则强调了继承关系的正确使用,要求子类能够完全替代父类,而不破坏程序的正确性。遵循该原则可以提高代码的重用性、灵活性和可靠性,是良好的软件设计实践之一。
三,优缺点
优点:
-
提高代码的可复用性:遵循里氏替换原则可以确保子类对象能够替换父类对象,这意味着我们可以使用统一的接口或抽象类来处理一组对象,从而提高了代码的可复用性。
-
增强程序的可扩展性:通过良好的继承关系,可以在不修改现有代码的情况下,通过添加新的子类来扩展系统的功能。这样可以降低对原有代码的影响范围,提高了程序的可扩展性。
-
促进代码的层次化结构:通过定义好的抽象类或接口,可以将代码按照层次化的结构组织起来,提高代码的可读性和可维护性。
-
提高代码的可测试性:遵循里氏替换原则可以使得代码更易于进行单元测试,因为我们可以使用父类对象来代替子类对象进行测试,从而提高了代码的可测试性。
缺点:
-
过度约束:有时为了满足里氏替换原则,可能需要引入过多的抽象类或接口,导致代码变得复杂,增加了设计和开发的难度。
-
需要在继承关系上建立合适的层次结构:正确地使用里氏替换原则需要在继承关系上建立适当的层次结构,这需要设计者有较强的面向对象设计能力。
-
可能违反单一职责原则:为了满足里氏替换原则,有时需要在父类中定义多个不相关的接口或抽象方法,这可能违反了单一职责原则,导致代码的可读性和维护性下降。
总的来说,里氏替换原则通过良好的继承关系可以提高代码的可复用性、可扩展性和可测试性,但需要在继承关系的层次结构上做出合理的设计,并权衡与其他设计原则的关系。
相关文章:
软件设计原则-里氏替换原则讲解以及代码示例
里氏替换原则 一,介绍 1.前言 里氏替换原则(Liskov Substitution Principle,LSP)是面向对象设计中的一条重要原则,它由Barbara Liskov在1987年提出。 里氏替换原则的核心思想是:父类的对象可以被子类的…...

Sui提供dApp Kit 助力快速构建React Apps和dApps
近日,Mysten Labs推出了dApp Kit,这是一个全新的解决方案,可用于在Sui上开发React应用程序和去中心化应用程序(dApps)。mysten/dapp-kit是专门为React定制的全新SDK,旨在简化诸如连接钱包、签署交易和从RPC…...

2023年系统设计面试如何破解?进入 FAANG 面试的实战指南
如果您正在准备编码面试,但想知道如何准备关键的系统设计主题,并寻找正确方法、技巧和问题的分步指导,那么您来对地方了。在本文中,我将分享 2023 年系统设计面试的完整指南。 在软件开发领域,如果您正在申请高级工程…...
(react+ts)vite项目中的路径别名的配置
简单两个步骤 找到vite.config.ts,这里会现实报错,需要安装一下 npm i -D types/node 这个库的ts声明配置 import path from path // https://vitejs.dev/config/ export default defineConfig({plugins: [react()],resolve:{alias:{"":path.resolve(__…...

【MATLAB源码-第51期】基于matlab的粒子群算法(PSO)的栅格地图路径规划。
操作环境: MATLAB 2022a 1、算法描述 粒子群算法(Particle Swarm Optimization,简称PSO)是一种模拟鸟群觅食行为的启发式优化方法。以下是其详细描述: 基本思想: 鸟群在寻找食物时,每只鸟都会…...

React之render
一、原理 首先,render函数在react中有两种形式: 在类组件中,指的是render方法: class Foo extends React.Component {render() {return <h1> Foo </h1>;} }在函数组件中,指的是函数组件本身:…...

基于springboot实现财务管理系统项目【项目源码+论文说明】计算机毕业设计
基于springboot实现财务管理系统演示 摘要 随着信息技术和网络技术的飞速发展,人类已进入全新信息化时代,传统管理技术已无法高效,便捷地管理信息。为了迎合时代需求,优化管理效率,各种各样的管理系统应运而生&#x…...

设计模式:组合模式(C#、JAVA、JavaScript、C++、Python、Go、PHP)
上一篇《模板模式》 下一篇《代理模式》 简介: 组合模式,它是一种用于处理树形结构、表示“部分-整体”层次结构的设计模式。它允许你将对象组合成树形结构,以表示部分…...

超强满血不收费的AI绘图教程来了(在线Stable Diffusion一键即用)
超强满血不收费的AI绘图教程来了(在线Stable Diffusion一键即用) 一、简介1.1 AI绘图1.2 Stable Diffusion1.2.1 原理简述1.2.2 应用流程 二、AI绘图工具2.1 吐司TusiArt2.2 哩布哩布LibLibAI2.3 原生部署 三、一键即用3.1 开箱尝鲜3.2 模型关联3.3 Cont…...

【蓝桥每日一题]-动态规划 (保姆级教程 篇12)#照相排列
这次是动态规划最后一期了,感谢大家一直以来的观看,以后就进入新的篇章了 目录 题目:照相排列 思路: 题目:照相排列 思路: 首先记录状态f[a][b][c][d][e]表示每排如此人数下对应的方案数,然…...

纺织工厂数字孪生3D可视化管理平台,推动纺织产业数字化转型
近年来,我国加快数字化发展战略部署,全面推进制造业数字化转型,促进数字经济与实体经济深度融合。以数字孪生、物联网、云计算、人工智能为代表的数字技术发挥重要作用。聚焦数字孪生智能工厂可视化平台,推动纺织制造业数字化转型…...

【七】SpringBoot为什么可以打成 jar包启动
SpringBoot为什么可以打成 jar包启动 简介:庆幸的是夜跑的习惯一直都在坚持,正如现在坚持写博客一样。最开始刚接触springboot的时候就觉得很神奇,当时也去研究了一番,今晚夜跑又想起来了这茬事,于是想着应该可以记录一…...

031-第三代软件开发-屏幕保护
第三代软件开发-屏幕保护 文章目录 第三代软件开发-屏幕保护项目介绍屏幕保护 关键字: Qt、 Qml、 MediaPlayer、 VideoOutput、 function 项目介绍 欢迎来到我们的 QML & C 项目!这个项目结合了 QML(Qt Meta-Object Language&#…...
Ubuntu 22.04 更新完内核重启卡在 grub 命令行解决办法
倒霉伊始 升级内核过程中出现如下警告,然后重启引导失败: Warning: os-prober will not be executed to detect other bootable partitions 屏幕内容如下: GNU GRUB version 2.06Minimal BASH-like line editing is supported. For the fir…...

Stream流式处理
Stream流式处理: 建立在Lambda表达式基础上的多数据处理技术。 可以对集合进行迭代、去重、筛选、排序、聚合等处理,极大的简化了代码量。 Stream常用方法 Stream流对象的五种创建方式 //基于数组 String[] arr {"a","b","c…...
ROG STRIX GS-AX5400 使用笔记
1. 技术支持 咨询京东(因为是在京东购买的) 2. 增强信号设置 Note:关于设置的具体步骤,请参考教程《华硕路由器地区如何改成澳大利亚》。 操作路径:打开主页<192.168.50.1> ⇒ 输入用户名和密码后选择登录 ⇒ …...

【刷题-PTA】堆栈模拟队列(代码+动态图解)
【刷题-PTA】堆栈模拟队列(代码动态图解) 文章目录 【刷题-PTA】堆栈模拟队列(代码动态图解)题目输入格式:输出格式:输入样例:输出样例: 分析题目区分两栈解题思路伪代码动图演示代码测试 题目 题目描述 : 设已知有两个堆栈S1和S2,请用这两个堆栈模拟出一个队列Q。 …...

FileUpload控件上传文件时出现 不支持给定路径的格式.的解决方法
正常代码,部署到server 2012时,在上传音频mp3文件时,显示错误“不支持给定路径的格式”,上传控件使用FileUpload控件: 因为程序之前是正常的,因此应该不是程序的问题。 上传时,发现在选择文件时…...
这两天的一些碎碎念
一直以来我都不算是一个非常热爱运维岗位的一个人,其实本行工作对于我来说只是一个工作。运维的广度很大,说什么工作了7年了,可最终总感觉还曾是一窍不通。 什么shell啊,什么python啊,什么大数据啊,7年里&a…...

Unity 最新DOTS系列之《Baking与Baker的详解》
Unity DOTS Baking与Baker详解 最近DOTS终于发布了正式的版本, 我们来分享一下DOTS里面Baking 与Baker的关键概念,方便大家上手学习掌握Unity DOTS开发。 对惹,这里有一个游戏开发交流小组,希望大家可以点击进来一起交流一下开发经验呀&…...

C++实现分布式网络通信框架RPC(3)--rpc调用端
目录 一、前言 二、UserServiceRpc_Stub 三、 CallMethod方法的重写 头文件 实现 四、rpc调用端的调用 实现 五、 google::protobuf::RpcController *controller 头文件 实现 六、总结 一、前言 在前边的文章中,我们已经大致实现了rpc服务端的各项功能代…...

基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...

2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...

相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...

视觉slam十四讲实践部分记录——ch2、ch3
ch2 一、使用g++编译.cpp为可执行文件并运行(P30) g++ helloSLAM.cpp ./a.out运行 二、使用cmake编译 mkdir build cd build cmake .. makeCMakeCache.txt 文件仍然指向旧的目录。这表明在源代码目录中可能还存在旧的 CMakeCache.txt 文件,或者在构建过程中仍然引用了旧的路…...
uniapp 字符包含的相关方法
在uniapp中,如果你想检查一个字符串是否包含另一个子字符串,你可以使用JavaScript中的includes()方法或者indexOf()方法。这两种方法都可以达到目的,但它们在处理方式和返回值上有所不同。 使用includes()方法 includes()方法用于判断一个字…...
jmeter聚合报告中参数详解
sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample(样本数) 表示测试中发送的请求数量,即测试执行了多少次请求。 单位,以个或者次数表示。 示例:…...