整洁架构SOLID-里氏替换原则(LSP)

news2024/12/23 0:28:12

文章目录

  • 定义
  • LSP继承实践
    • 正例
    • 反例
  • LSP软件架构实践
    • 反例
  • 小结

定义

1988年,Barbara Liskov在描述如何定义子类型时写下了这样一段话:

这里需要的是一种可替换性:如果对于每个类型是S的对象o1都存在一个类型为T的对象o2,能使操作T类型的程序P在用o2替换o1时行为保持不变,我们就可以将S称为T的子类型。

这段话中所体现的设计理念,就是里氏替换原则(LSP)。

上述定义可以总结为:

  1. 衍生类不要修改基类的行为,保证基类与衍生类的可替换性
  2. 基类型中应该只提供尽量少的必需的行为,而且不针对这些行为进行任何实现
  3. 只要有可能,不要从具体类继承,而应该由抽象类继承或由接口实现

LSP继承实践

正例

假设我们有一个License类,其结构如下图所示。该类中有一个名为calcFee()的方法,该方法将由Billing应用程序来调用。而License类有两个“子类型”:PersonalLicense与BusinessLicense,这两个类会用不同的算法来计算授权费用。

在这里插入图片描述

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

反例

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

在这个案例中,Square类并不是Rectangle类的子类型,因为Rectangle类的高和宽可以分别修改,而Square类的高和宽则必须一同修改。由于User类始终认为自己在操作Rectangle类,因此会带来一些混淆。例如在下面的代码中:

Rectangle r = …r.setW(5);
r.setW(5);

r.setH(2);
assert(r.area()== 10);

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

如果想要防范这种违反LSP的行为,唯一的办法就是在User类中增加用于区分Rectangle和Square的检测逻辑(例如增加if语句)。但这样一来,User类的行为又将依赖于它所使用的类,这两个类就不能互相替换了。

LSP软件架构实践

我们的普遍认知正如上文所说,认为LSP只不过是指导如何使用继承关系的一种方法,然而随着时间的推移,LSP逐渐演变成了一种更广泛的、指导接口与其实现方式的设计原则。

这里提到的接口可以有多种形式:

  • 可以是Java风格的接口,具有多个实现类。
  • 可以像Ruby一样,几个类共用一样的方法签名。
  • 甚至可以是几个服务响应同一个REST接口。

LSP适用于上述所有的应用场景,因为这些场景中的用户都依赖于一种接口,并且都期待实现该接口的类之间能具有可替换性。

想要从软件架构的角度来理解LSP的意义,最好的办法还是来看一个反面案例。

反例

假设我们现在正在构建一个提供出租车调度服务的系统。在该系统中,用户可以通过访问我们的网站,从多个出租车公司内寻找最适合自己的出租车。当用户选定车子时,该系统会通过调用restful服务接口来调度这辆车。

接下来,我们再假设该restful调度服务接口的URI被存储在司机数据库中。一旦该系统选中了最合适的出租车司机,它就会从司机数据库的记录中读取相应的URI信息,并通过调用这个URI来调度汽车。

也就是说,如果司机Bob的记录中包含如下调度URI:

purplecab.com/driver/Bob

那么,我们的系统就会将调度信息附加在这个URI上,并发送这样一个PUT请求:

purplecab.com/driver/Bob
    /pickupAddress/24 Maple St.
    /pickupTime/153
    /destination/ORD

这意味着所有参与该调度服务的公司都必须遵守同样的REST接口,它们必须用同样的方式处理pickupAddress、pickupTime和destination字段

接下来,我们再假设Acme出租车公司现在招聘的程序员由于没有仔细阅读上述接口定义,结果将destination字段缩写成了dest。

这会对系统的架构造成什么影响呢?

显然,我们需要为系统增加一类特殊用例,以应对Acme司机的调度请求。而这必须要用另外一套规则来构建。

最简单的做法当然是增加一条if语句:

if(driver.getDispatchUri().startsWith("acme.com"))…

然而很明显,任何一个称职的软件架构师都不会允许这样一条语句出现在自己的系统中。因为直接将“acme”这样的字串写入代码会留下各种各样神奇又可怕的错误隐患,甚至会导致安全问题。

Acme也许会变得更加成功,最终收购了Purple出租车公司。然后,它们在保留了各自名字的同时却统一了彼此的计算机系统。在这种情况下,系统中难道还要再增加一条“purple”的特例吗?

软件架构师应该创建一个调度请求创建组件,并让该组件使用一个配置数据库来保存URI组装格式,这样的方式可以保护系统不受外界因素变化的影响。例如其配置信息可以如下:
在这里插入图片描述

但这样一来,软件架构师就需要通过增加一个复杂的组件来应对并不完全能实现互相替换的restful服务接口。

整体思想:根据调用方的需求抽象出一套getUrl的接口,对于不同的公司可以实现这个接口。将差异性(变化点)封装,加一个抽象层,磨平差异。

小结

LSP可以且应该被应用于软件架构层面,因为一旦违背了可替换性,该系统架构就不得不为此增添大量复杂的应对机制。

参考内容来源于:《架构整洁之道》

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

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

相关文章

树莓派PICO使用INA226测量电流和总线电压(1)

TI的INA226是一款不错的16位电流测量芯片,拥有非常高的性价比,而且测量精度能够覆盖我们大多数的应用场景,INA226的接线也比较简单,通过I2C读取数据会稍微麻烦一点。 INA226可以在淘宝上直接买开发板,非常的便宜&#…

本地部署,去除动漫图像背景Anime Remove Background

目录 摘要 引言 深度学习在动漫角色中的应用 1.​U-Net 2.Mask R-CNN 3.ISNet 模型 4.MODNet 模型 5.InSPyReNet 模型 本地部署 运行效果 测验结果​ Tip: 摘要 动漫图像背景去除是一项在图像处理和计算机视觉领域具有重要应用的技术,广泛应用于…

「Pytorch」roLabelImg 图像异常旋转 bug

在进行Yolo-obb 模型训练的时候需要标注旋转框,roLabelImg 是比较推荐的一款旋转框标注工具,既可以标注正常的矩形框,还可以标注旋转框 roLabelImg Github 地址:https://github.com/HumanSignal/labelImg 但是在使用过程中遇到了…

Word中使用LaTeX编辑公式

在日常学术写作过程中,公式输入必不可少。然而使用Word传统的公式编辑器往往无法满足高标准的排版要求,如排版不专业、编辑效率低、兼容性差等问题。LaTeX相比Word,在公式输入及排版方面更为强大,但是对于轻量级的任务&#xff0c…

Vivado 2020.1 HLS IP在BD模式无法生成问题

折腾了一周整整,记录一下,希望对大家有用。 各种找、各种操作,也问了FAE,都没搞定。 最后看到如下博文的方法3,管用。 vivado综合hls类ip核报错问题解决方案_vivado ip synth checkpoint mode-CSDN博客 报错描述 m…

Chatto企业AI助手产品技术拆解(上)

往期文章: AI智能客服项目拆解(1) 产品大纲 在上一篇文章中,我们详细介绍了AI智能客服的基本概念、需求分析、产品形态及分类,以及AI在线客服的用户体验流程和系统架构。为了更深入地理解AI智能客服的内在工作机制和技术实现,本…

【读论文】Instant Neural Graphics Primitives with a Multiresolution Hash Encoding

文章目录 1. What2. Why2.1 Introduction2.2 Related work and background 3. How: Multiresolution hash encoding3.1 Structure3.2 Input coordinate3.3 Hash mapping3.4 Interpolation3.5 Performance vs. quality3.6 Hash collision 4. Experiment on Nerf 1. What To red…

Pygame开发五子棋之人机对战游戏

引言 Pygame是一个基于Python的开源游戏开发库,它包含了丰富的多媒体功能,尤其是针对游戏开发所需的各种组件。如果你对游戏开发感兴趣,但又不想从底层开始编写所有东西,Pygame可以成为一个理想的起点。本文将介绍Pygame的基本概…

C++:类和对象 I(访问限定符、this指针)

目录 类的定义 类的大小 访问限定符 实例化 this指针 类的定义 class就是类,class是C中的一个关键字 当然类也可以是C语言中的struct,C兼容struct,甚至还有一些升级 定义类的方式 class Date {}; 和C语言的struct一样,c…

【信息收集】域名信息收集

域名介绍 域名(Domain Name),简称域名、网域,是由一串用点分隔的名字组成的Internet上某一台计算机或计算机组的名称,用于在数据传输时标识计算机的电子方位(有时也指地理位置)。 DNS&#xf…

【Python】jupyter notebook平台的使用·

目录 一、安装Anaconda 二、 将BreadCancer.zip上传到jupyter notebook平台中 三、了解BreadCancerClassifier.ipynb文件在jupyter notebook的单元格中的python代码,并运行。 3.1 导入mainFun文件 3.2 读入数据 3.3 开始训练 3.4 读入测试数据 3.5 开始测试…

[笔记] SEW的振动分析工具DUV40A

1.便携式振动分析仪 DUV40A 文档编号:26871998/EN SEW是一家国际化的大型的机械设备供应商。产品线涵盖电机,减速机,变频器等全系列动力设备。DUV40A是他自己设计的一款振动分析工具。 我们先看一下它的软硬件参数: 内置两路传…

i7-13700K负载过高时出现无故自动重启(蓝屏问题)

现象:电脑无故自动重启,关闭故障自动重启后,发现系统蓝屏,然后需要手动重启。经测试,当CPU负载高时,就会有一定概率出现蓝屏。 配置:CPU为i7-13700K,系统为Win11 解决方法 现象刚…

Python那些优质可视化工具!

作者:Lty美丽人生 https://blog.csdn.net/weixin_44208569 本次分享10个适用于多个学科的Python数据可视化库,其中有名气很大的也有鲜为人知的! 1、matplotlib 两个直方图 matplotlib 是Python可视化程序库的泰斗。经过十几年它任然是Pytho…

mitmproxy介绍及使用

mitmproxy介绍 mitmproxy又名中间人攻击代理,是一个抓包工具,类似于WireShark、Filddler,并且它支持抓取HTTP和HTTPS协议的数据包,只不过它是一个控制台的形式操作。另外,它还有两个非常有用的组件,一个mi…

漏扫处理:SSH弱算法问题解决

目录 漏洞说明解决方法1. 查看可用的算法2. 禁用弱算法3.检查ssh配置4.重启ssh服务5.ssh测试连接是否正常6.漏扫测试参考链接漏洞说明 通过漏扫得出,服务器SSH支持密钥交换算法,而此算法被认为是弱算法,存在高风险问题。 启用了以下弱算法: diffie-hellman-group-exchage…

前端JS特效第33波:jQuery旋转木马焦点图轮播插件PicCarousel

jQuery旋转木马焦点图轮播插件PicCarousel&#xff0c;先来看看效果&#xff1a; 部分核心的代码如下&#xff1a; <!doctype html> <html> <head> <meta charset"utf-8"> <meta http-equiv"X-UA-Compatible" content"IE…

Go 初始化一个字典

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

Python量化交易学习——Part12:回归模型的典型应用

回归模型在很多的时候被应用于对股票的基本面数据进行分析&#xff0c;例如经典的CAPM模型、Fama-French三因子模型以及最新的PB_ROE模型等。这些都是已经应用于现实中的金融市场并获得较好收益的经典模型。本章将通过介绍PB_ROE模型&#xff0c;进一步讲解回归分析在实战过程中…

深入探讨【C++容器适配器】:现代编程中的【Stack与Queue】的实现

目录 一、Stack&#xff08;栈&#xff09; 1.1 Stack的介绍 1.2 Stack的使用 1.3 Stack的模拟实现 二、Queue&#xff08;队列&#xff09; 2.1 Queue的介绍 2.2 Queue的使用 2.3 Queue的模拟实现 三、容器适配器 3.1 什么是适配器 3.2 为什么选择deque作为stack和…