深入理解设计原则之里氏替换原则(LSP)【软件架构设计】

news2024/11/23 16:33:58

系列文章目录

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/606403.html

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

相关文章

《微服务架构设计模式》第一章 逃离单体地狱

内容总结自《微服务架构设计模式》 逃离单体地狱 一、单体架构1、好处2、弊端 二、微服务架构1、定义2、好处3、弊端 三、模式的概念1、定义2、构成3、引申微服务 一、单体架构 1、好处 易于对应用程序进行大规模的更改:可以更改代码和数据库模式,然后…

华为OD机试真题 Java 实现【单词倒序】【2023Q1 100分】,附详细解题思路

一、题目描述 输入单行英文句子,里面包含英文字母,空格以及.? 三种标点符号,请将句子内每个单词进行倒序,并输出倒序后的语句。 二、输入描述 输入字符串S,S的长度1≤N≤100。 三、输出描述 输出逆序后的字符串 …

有道云笔记也挺速度,也搞了个AI助手,能抗衡Notion AI?

前言 小编平时做技术笔记的时候,经常使用到的软件就是有道云笔记,最近无意间发现,笔记编写的页面中,竟然集成了AI助手!网易有道可真是低调!毕竟最近AI圈大火,竟然没有蹭一波热度,直…

Spring Security 核心解读(二)自定义认证授权体系

自定义认证授权体系 概述自定义认证定义登录接口配置 Security 放行策略定义通用登录过滤器并将其配置到 Security 过滤器链上定义资源接口在 Security 授权设置中放行启动项目 结尾 概述 以前使用Spring Security 时,基本都是按部就班参考文档开发。 基本是从 Use…

【Python开发】FastAPI 06:处理错误

某些情况下,有必要向客户端(包括前端浏览器、其他应用程序、物联网设备等)返回错误提示,以便客户端能够了解错误的类型,从而做出应对。 目录 1 默认处理 1.1 错误介绍 1.2 使用 HTTPException 2 自定义处理 2.1 自…

论旅行之收获2

论旅行之收获2 概况站点第一站:北京市大兴区大兴机场基本情况吐槽小小趣事 第二站:云南省昆明长水机场云南省昆明市五华区基本概况经济分析 第三站:昆明站大理站云南省大理白族自治州大理市下关基本情况 第四站:云南省大理白族自治…

操作系统-进程和线程-处理机调度

目录 一、调度的概念 1.1调度的层次 1.1.1高级调度(作业调度) 1.1.2中级调度(内存调度) 1.1.3低级调度(进程调度) 1.2状态模型 1.3调度的时机 1.4调度方式 1.5进程的切换与过程 二、调度算法的评价指标 2.1CPU利用率 2.2吞吐量 2.3周转时间 2.4带权周转时间 2.5等…

Ansible基础6——文件模块、jinja2模板

文章目录 一、常用文件模块1.1 blockinfile模块1.2 file模块1.2.1 创建文件并赋予权限1.2.2 创建目录并赋予权限1.2.3 创建软连接1.2.4 删除文件或目录 1.3 fetch模块1.4 lineinfile模块1.5 stat模块1.6 synchronize模块 二、jinja2模板2.1 构建jinja2模板2.2 管理jinja2模板2.…

LVGL学习(1):中文字体的转换和汉字显示

在使用LVGL的过程中,我们难免需要显示汉字,所以这篇文章就来介绍一下如何转换中文汉字并成功显示到LVGL中。 文章目录 1 字体转换1.1 GUI Guider自动转换1.2 在线转换 2 字体的使用 1 字体转换 1.1 GUI Guider自动转换 打开GUI Guide,创建…

SAP-MM-批次删除归档操作

对于物料主数据的批次管理,一旦一个物料设置了批次管理,并已经产生了业务数据,但是发现当初业务主数据搞错了,此物料不需要在SAP系统进行批次管理,是否就无法取消其批次管理了呢,因为如果要取消此批次管理&…

Java: 字符集详解

1.计算机的存储规则 在计算机中,任意数据都是以二进制的形式来存储的。 每一个二进制数表示的0,1被称为比特位,8个比特位组成一个字节。 字节:计算机最小的存储单元。 2.ASCII字符集 用于存储英文字符,查询制定AS…

阿里云服务器开通全部端口流程

阿里云服务器端口怎么全部打开?在安全组中开启端口号,在安全组中把端口范围设置为-1/-1,授权对象填0.0.0.0/0,即可开通全部端口号,阿腾云来详细说下阿里云服务器端口全部打开教程: 阿里云服务器端口全部开…

结题报告范文

结题报告范文篇一 项目名称:__________ 项目编号: 项目负责人:_________ 专业年级:__________ 所在学院:__________ 起止年月: 电 话:__________ E-mail: 填表时期: 福建…

Vue基础入门(下)

<script src"https://unpkg.com/vuenext"></script> mixin混入&#xff08;局部使用&#xff09; 定义mixin对象 <script>// mixin 混入const mymixin{data(){return {number:2,count:1}},created(){console.log(mymixin created);},methods:{…

【Unity3D】Bloom特效

1 Bloom 特效原理 Bloom 特效是指&#xff1a;将画面中较亮的区域向外扩散&#xff0c;造成一种朦脓的效果。实现 Bloom 特效&#xff0c;一般要经过 3 个阶段处理&#xff1a;亮区域检测、高斯模糊、Bloom 合成。 本文完整资源见→Unity3D Bloom 特效。 1&#xff09;亮区域检…

OS-内存管理1- 4种基本管理方式(连续分配,页式,段式,段页)。

一&#xff0c;内存管理四种方式。 二&#xff0c;连续分配管理方式。 连续分配方式&#xff1a;为用户分配连续的内存空间。 1.单一连续分配方式 2.固定分区分配方式 3.动态分区分配方式 4.三种连续分配方式的对比。 三&#xff0c;基于页式存储管理。 1.页式 为进一步提高…

嵌入式系统中I2C总线通信基本方法

将 I2C spec 文章总结为一篇&#xff0c;目录如下 I2C Introduction I2C Architecture I2C Transfer I2C Synchronization And Arbitration I2C Hs-mode1、I2C Introduction 1、I2C 历史 I2C&#xff1a;Inter-Integrated Circuit&#xff0c;集成电路总线。 I2C 是 Philips…

部分网络结构记录

CVPR 2022 | Mobile-Former来了&#xff01;微软提出&#xff1a;MobileNetTransformer轻量化并行网络 文章链接: CVPR 2022 | Mobile-Former来了&#xff01;微软提出&#xff1a;MobileNetTransformer轻量化并行网络 - 知乎 (zhihu.com) Mobile-Former架构图 Mobile-Form…

Mybatis学习笔记一

目录 一、Mybatis特性二、快速入门1.导入依赖2.mybatis-config.xml配置3.创建mapper接口4.创建MyBatis的映射文件5.通过junit测试功能 三、MyBatis获取参数值的两种方式&#xff08;重点&#xff09;1.单个字面量类型的参数2.多个字面量类型的参数3.map集合类型的参数4.实体类类…

【六一特别文章】Python编写一个六一儿童节问答小游戏及趣味比赛

随着六一儿童节的到来&#xff0c;我们可以为孩子们编写一个有趣的小游戏&#xff0c;让他们在游戏中学习有关六一儿童节的知识。本文将介绍如何用Python编写一个六一儿童节问答小游戏及趣味比赛。 首先&#xff0c;我们需要准备一些有关六一儿童节的问题和答案。这里我准备了…