深入理解设计原则之里氏替换原则(LSP)

news2025/4/6 16:17:12

系列文章目录

C++高性能优化编程系列
深入理解设计原则系列
深入理解设计模式系列
高级C++并发线程编程

LSP:里氏替换原则

  • 系列文章目录
  • 1、里氏替换原则的定义和解读
  • 2、里氏替换原则可以用于哪些设计模式中?
  • 3、如何使用里氏替换原则来降低代码耦合度?
  • 4、违反里氏替换原则的反模式
  • 5、案例解读
  • 6、里氏替换原则与多态的区别
  • 7、小结

1、里氏替换原则的定义和解读

里氏替换原则(Liskov Substitution Principle, LSP)于1986年有Barbara Liskov提出,他当时是这样描述这条原则的:如果S是T的子类型,那么T的对象可以被S的对象所替换,并不影响代码的运行。1996年,Robert Martin在他的SOLID原则中重新描述了里氏替换原则:使用父类对象的函数可以在不了解子类的情况下替换为使用子类对象
结合上面的描述,我们将里氏替换原则描述为:子类对象能够替换到程序中的父类对象出现的任何地方,并且保证程序原有的逻辑行为不变和正确性不被破坏

2、里氏替换原则可以用于哪些设计模式中?

将里氏替换原则应用于设计模式中,它可以用在许多设计模式中,例如:

  1. 工厂模式:子类可以替换父类,而不会影响到工厂的实现。
  2. 策略模式:不同的策略可以被替换,而不会影响到程序的正确性。
  3. 装饰器模式:装饰器可以替换被装饰的对象,而不会破坏程序的可扩展性。
  4. 模板方法模式:子类可以被使用来扩展或修改模板方法,而不会影响到程序的正确性。

总之,里氏替换原则是一个通用的设计原则,可以在许多设计模式中使用,以增强程序的可读性、可维护性和可扩展性。

3、如何使用里氏替换原则来降低代码耦合度?

通过遵循里氏替换原则,可以使得代码更加灵活、可扩展、易维护。下面是几种具体的方法来降低代码耦合度:

  1. 使用接口而不是具体的类:对于一个类,尽可能使用其接口而非具体的实现。这样可以减少对具体实现的依赖,并且可以更容易地替换实现。
  2. 将通用的行为移到基类:将一些通用的方法或属性移动到基类中,这样子类就可以继承这些方法并添加自己的实现,而不是每个子类都去重复实现相同的方法。
  3. 使用抽象类或接口来表示通用行为:通过使用抽象类或接口来表示通用行为,可以使得代码更加灵活和可扩展。当需要添加新的特定行为时,只需要实现新的抽象类或接口即可,而不需要修改已有的代码。
  4. 避免破坏子类的先决条件:子类必须满足其父类的先决条件。如果子类修改了父类的行为,那么子类就不能替代其父类,这会导致代码的耦合度增加。

4、违反里氏替换原则的反模式

在设计子类时,要遵守父类的行为约定(或者为协议)
以下是违反里氏替换原则的例子:

  1. 子类违反父类声明要实现的功能
  2. 子类违反父类对输入、输出和异常约定
  3. 子类违反父类注释中罗列的任何特殊说明

5、案例解读

假设我们有一个License类,其结构如图1所示。该类中有一个名为calcFee的方法,该方法将由 Billing应用程序来调用。而License类有两个子类型:PersonalLicense与BusinnessLicense,这两个类会用不同的算法来计算授权费用。
在这里插入图片描述

图1:License类与其衍生类,体现了LSP原则

上述设计符合LSP原则,因为Billing应用程序的行为并不依赖于其使用的任何一个衍生类。也就是说,这两个衍生类的对象都是可以用来替换License类对象的。

正方形/长方形问题是一个著名的违反LSP设计原则的案例,该问题结构如图2所示。
在这里插入图片描述

图2:正方形/长方形问题,违反了LSP原则
在这个案例中,Square类并不是Rectangle类的子类型,因为Rectangle类的高和宽可以分别修改,而Square类的高和宽则必须一同修改。由于User类始终认为自己在操作Rectangle类,因此会带来一些混淆。例如在下面的代码中:
Rectangle r = ...
r.setW(5);
r.setH(2);
assert(r.area() == 10);

很显然,如果上述代码在…处返回的是Square类,则最后assert是不会成立的。

6、里氏替换原则与多态的区别

多态是一种代码实现思路,而里氏替换原则是一种设计原则,用来指导继承关系中子类的设计:在替换父类时,确保不改变程序原有的逻辑行为,以及不破坏程序的正确性。

7、小结

里氏替换原则存在的意义:

  • 提高代码的可维护性。遵循里氏替换原则可以使代码的结构更加清晰,如果程序中的对象可以互相替换,那么维护代码的时候就能够更方便地进行修改和扩展。
  • 提高代码的可扩展性。遵循里氏替换原则,可以使代码更容易扩展。如果代码中的对象可以互相替换,那么新的子类可以很容易地替换已有的类,从而实现代码的扩展,而不需要修改原有的代码。
  • 增加代码的可读性。如果遵循里氏替换原则,代码中的类之间的关系会更加清晰明了,这对于其他开发人员来说也更容易理解和阅读。
  • 提高代码的健壮性。遵循里氏替换原则可以使程序更加健壮。如果程序中的对象可以互相替换,那么程序的稳定性和可靠性也会得到提高。
  • 促进代码重用。遵循里氏替换原则,可以使代码更容易重用。如果程序中的对象可以互相替换,那么可以将已有的对象用于新的场景中,从而避免了重复编写代码的情况。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Julia系列14:调用自定义C/C++库

1. 基础调用:ccall 调用的基本格式为: ccall((:函数名, 库地址), 输出格式, (输入格式列表), 输入数据) 下面是例子: 1.1 基础数据结构 1.2 数组 首先是输入数组,注意需要convert 接着是输出数组,需要使用unsafe…

《操作系统》—— 处理机调度算法

前言: 在之前的文章中,我们已经了解了进程和线程相关的基本概念,今天我们将要了解的是关于处理机调度相关的知识。 目录 (一)调度的概念 1、调度的基本概念 2、调度的层次 3、三级调度的关系 (二&…

遗传算法(Genetic Algorithm)

本文为阅读《遗传算法原理及应用》的笔记和心得 ISBN:7-118-02062-1 遗传算法简介 遗传算法是模拟生物在自然环境中的遗传和进化过程中而形成的一种自适应全局优化概率搜索算法 总的来说,求最优解解或近似最优解的方法主要有三种:枚举法、启…

【PCB专题】Allegro设置禁止铺铜区域但仍可以走线和打过孔

在PCB设计中我们有时候需要做一些净空区,但是净空区内有一些走线和过孔。如果使用Route Keepout画一个框的话,那是不允许走线和打过孔的,会报DRC。 那么如何才能既禁止区域铺铜,又可以走线和打过孔不报DRC呢? Setup->Areas->Shape Keepout Options选择要禁止…

第二十一篇、基于Arduino uno,控制有源蜂鸣器和无源蜂鸣器发出声音——结果导向

0、结果 说明:有源蜂鸣器按照一定的频率报警,无源蜂鸣器则是一直报警,都采用非阻塞方式编写,如果是你想要的,可以接着往下看。 1、外观 说明:有源蜂鸣器和无源蜂鸣器看上去一样,但是背面不一…

详解Handler

详解Handler 文章目录 详解Handler1.Handler的工作流程1.1主线程具有如上性质的原因1.2流程图 2.Handler流程中的重要的几个方法2.1Message中的属性2.2.1what2.2.2replyTo2.2.3obtain 2.2Handler.post()与Handler.sendMessage()2.2.1post的源码2.2.1.1sendMessageDelayed()源码…

centos6离线安装docker

参考 RedHat 6.8 离线安装Docker (rpm包安装) - 神奇二进制 - 博客园 (cnblogs.com) 可参考,但本次安装未参考 CentOS6 完全离线安装Docker - 简书 (jianshu.com) 走了一遍,大雾 (1条消息) 离线安装Docker_洒家肉山大魔王的博客…

萌啦科技参加ICBE跨境电商博览会完美落幕,期待再相会!

“ 萌啦科技联合DNY123、喜运达物流共同亮相2023 ICBE跨境电商博览会,更全面地服务东南亚电商卖家,把握新兴市场电商发展商机!” 跨境电商“万人”博览会 5月15日-5月17日,ICBE国际跨境电商交易博览会在广州琶洲保利世贸博览馆隆重…

《商用密码应用与安全性评估》第四章密码应用安全性评估实施要点4.3密码测评要求与测评方法

总体要求测评方法 1.密码算法核查 测评人员应当首先了解信息系统使用的算法名称、用途、位置、执行算法的设备及其实现方式(软件、硬件或固件等)。针对信息系统使用的每个密码算法,测评人员应当核查密码算法是否以国家标准或行业标准形式发布…

数据结构与算法-二分查找

1.1 什么是算法? 定义 在数学和计算机科学领域,算法是一系列有限的严谨指令,通常用于解决一类特定问题或执行计算 In mathematics and computer science, an algorithm (/ˈlɡərɪəm/) is a finite sequence of rigorous instructions, …

IO读写的基础原理

read系统调用write系统调用read系统调用,并不是直接从物理设备把数据读取到内存中,write系统调用,也不是直接把数据写入到物理设备。调用操作系统的read,是把数据从内核缓冲区复制到进程缓冲区;而write系统调用&#x…

健康医疗类APP开发 满足民众在线医疗需求

生活水平和社会大环境的变化让人们对于医疗服务的要求也随之提高,传统的到医院就诊已经无法更好的满足现代人多元化的医疗服务需求了。于是很多医院诊所等都考虑通过互联网技术来实现诊疗和科普健康知识的目的,为用户提供更加便捷化多元化的健康诊疗服务…

Python魔法属性和方法

1.魔法属性 __doc__ 获取类或方法的描述信息 class Foo:""" 类对象__doc__的属性值"""def func(self):""" 类方法的__doc__属性值 """passfoo Foo()print("类对象的__doc__:", Foo.__do…

电力需求侧管理是什么及意义

安科瑞虞佳豪 电力需求侧管理是指综合采取合理可行的技术、经济和管理措施,在用电环节实施需求响应、节约用电、电能替代、绿色用电、智能用电、有序用电,推动电力系统安全降碳、提效降耗。 我国分别于2010年和2017年发布了两版电力需求侧管理办法。国…

元”启长三角 共享新未来!长三角数字干线元宇宙创新发展论坛暨第一届长三角元宇宙日在长三角绿洲智谷·赵巷成功举办

5月30日下午,由工信部网络安全产业发展中心(工信部信息中心)、长三角投资(上海)有限公司、青浦区经济委员会、青浦区科学技术委员会、青浦区科学技术协会指导,北京大数据协会元宇宙专委会主办,长…

ISO21434 项目网络安全管理(三)

目录 一、概述 二、目标 三、输入 3.1 先决条件 3.2 进一步支持信息 四、要求和建议 4.1 网络安全责任 4.2 网络安全规划 4.3 裁剪 4.4 重用 4.5 非上下文组件 4.6 现成组件 4.7 网络安全案例(Cybersecurity case) 4.8 网络安全评估&#…

网店系统如何建设?如何搭建网店?

互联网的不断发展,越来越多的商家开始意识到建设自己的网店是非常必要和重要的。通过搭建网店系统,商家无需承担大量的租赁、装修等成本,同时可以将商品推广到更广阔的市场,提高销售额。那么,网店系统如何建设呢&#…

[QCA6174]QCA6174 DFS认证4.6.2.3 Channel Shutdown出现跳转之后在原始信道上有弱信号问题分析及解决方案

WIFI DFS测试要求 Master设备需要测试的项目 4.6.2.1 Channel Availability Check ---信道可用性检查 定义其作为雷达脉冲检测机制,当雷达脉冲出现时所占用的信道需要能被设备检测到已经被占用。当相关信道未被占用时,这些信道被称为Avaliable Channel可用信道 4.6.2.2 In…

【TA 100】Flow Map实现水体流动效果

最近刚好学到Shader Graph水体流动,看下其他实现方式记录下 1 什么是flow map 1 什么是Flow map? flowmap的实质:一张记录了2D向量信息的纹理Flow map上的颜色(通常为RG通道) 记录该处向量场的方向,让模型上某一点表现出定量流动的特征。通过在shader中…

Python接口自动化—接口测试用例和接口测试报告模板

简介 当今社会在测试领域,接口测试已经越来越多的被提及,被重视,而且现在好多招聘信息要对接口测试提出要求。区别于传统意义上的系统级别测试,很多测试人员在接触到接口测试的时候,也许对测试执行还可以比较顺利的上…