当前位置: 首页 > news >正文

【设计模式之美】SOLID 原则之三:里式替换(LSP)跟多态有何区别?如何理解LSP中子类遵守父类的约定

文章目录

    • 一. 如何理解“里式替换原则”?
    • 二. 哪些代码明显违背了 LSP?
    • 三. 回顾

一. 如何理解“里式替换原则”?

子类对象能够替换程序中父类对象出现的任何地方,并且保证原来程序的逻辑行为不变及正确性不被破坏。

里氏替换原则例子如下:

//>1. 父类 Transporter 使用 HttpClient 来传输网络数据。
//>2. 子类 SecurityTransporter 继承父类 Transporter,增加了额外的功能,
//支持传输 appId 和 appToken 安全认证信息。
public class Transporter {private HttpClient httpClient;public Transporter(HttpClient httpClient) {this.httpClient = httpClient;}public Response sendRequest(Request request) {// ...use httpClient to send request}
}public class SecurityTransporter extends Transporter {private String appId;private String appToken;public SecurityTransporter(HttpClient httpClient, String appId, String appToken) {super(httpClient);this.appId = appId;this.appToken = appToken;}@Overridepublic Response sendRequest(Request request) {if (StringUtils.isNotBlank(appId) && StringUtils.isNotBlank(appToken)) {request.addPayload("app-id", appId);request.addPayload("app-token", appToken);}// ***************// 这里调用了父类的方法:即没有改变父类的逻辑(约定)// ***************return super.sendRequest(request);}
}public class Demo {    public void demoFunction(Transporter transporter) {    Reuqest request = new Request();//...省略设置request中数据值的代码...Response response = transporter.sendRequest(request);//...省略其他逻辑...}
}// 里式替换原则
Demo demo = new Demo();
demo.demofunction(new SecurityTransporter(/*省略参数*/););

从刚刚的例子和定义描述来看,里式替换原则跟多态看起来确实有点类似,但实际上它们完全是两回事。

先改造下程序(改造成多态)

//改造前,如果appId 或者 appToken 没有设置,我们就不做校验;
//改造后,如果 appId 或者 appToken 没有设置,则直接抛出NoAuthorizationRuntimeException 未授权异常。
// 改造前:
public class SecurityTransporter extends Transporter {//...省略其他代码..@Overridepublic Response sendRequest(Request request) {if (StringUtils.isNotBlank(appId) && StringUtils.isNotBlank(appToken)) {request.addPayload("app-id", appId);request.addPayload("app-token", appToken);}return super.sendRequest(request);}
}
// 改造后:
public class SecurityTransporter extends Transporter {//...省略其他代码..@Overridepublic Response sendRequest(Request request) {if (StringUtils.isBlank(appId) || StringUtils.isBlank(appToken)) {throw new NoAuthorizationRuntimeException(...);}request.addPayload("app-id", appId);request.addPayload("app-token", appToken);return super.sendRequest(request);}
}

从设计思路上来讲,SecurityTransporter 的设计是不符合里式替换原则的,因为它改变了父类原有的规则,我们接下来讨论里氏替换中协议的具体含义。

 

二. 哪些代码明显违背了 LSP?

里式替换原则还有另外一个更加能落地、更有指导意义的描述,那就是“Design By Contract”,中文翻译就是“按照协议来设计”。

具体说明一下

  • 子类在设计的时候,要遵守父类的行为约定(或者叫协议)。父类定义了函数的行为约定,那子类可以改变函数的内部实现逻辑,但不能改变函数原有的行为约定
  • 这里的行为约定包括:函数声明要实现的功能;对输入、输出、异常的约定;甚至包括注释中所罗列的任何特殊说明。
  • 实际上,定义中父类和子类之间的关系,也可以替换成接口和实现类之间的关系。

 

如下几个违反里式替换原则的例子,来说明约定的含义:

1.子类违背父类声明要实现的功能

父类中提供的 sortOrdersByAmount() 订单排序函数,是按照金额从小到大来给订单排序的,而子类重写这个 sortOrdersByAmount() 订单排序函数之后,是按照创建日期来给订单排序的。那子类的设计就违背里式替换原则。

 

2.子类违背父类对输入、输出、异常的约定,那子类的设计就违背里式替换原则,如下:

  • 在父类中运行出错的时候返回 null;获取数据为空的时候返回空集合(empty collection)。而子类重载函数之后,实现变了,运行出错返回异常(exception),获取不到数据返回 null。
  • 在父类中,某个函数约定,输入数据可以是任意整数,但子类实现的时候,只允许输入数据是正整数,负数就抛出,也就是说,子类对输入的数据的校验比父类更加严格
  • 在父类中,某个函数约定,只会抛出 ArgumentNullException 异常,那子类的设计实现中只允许抛出 ArgumentNullException 异常,任何其他异常的抛出。

 

3.子类违背父类注释中所罗列的任何特殊说明

父类中定义的 withdraw() 提现函数的注释是这么写的:“用户的提现金额不得超过账户余额……”,而子类重写 withdraw() 函数之后,针对 VIP 账号实现了透支提现的功能,也就是提现金额可以大于账户余额,那这个子类的设计也是不符合里式替换原则的。

 

三. 回顾

里式替换原则是用来指导,继承关系中子类该如何设计的一个原则。


  • 理解里式替换原则,最核心的就是理解“design by contract,按照协议来设计”这几个字。父类定义了函数的“约定”(或者叫协议),那子类可以改变函数的内部实现逻辑,但不能改变函数原有的“约定”。
  • 这里的约定包括:函数声明要实现的功能;对输入、输出、异常的约定;甚至包括注释中所罗列的任何特殊说明。

 

参考:《设计模式之美》–王争

相关文章:

【设计模式之美】SOLID 原则之三:里式替换(LSP)跟多态有何区别?如何理解LSP中子类遵守父类的约定

文章目录 一. 如何理解“里式替换原则”?二. 哪些代码明显违背了 LSP?三. 回顾 一. 如何理解“里式替换原则”? 子类对象能够替换程序中父类对象出现的任何地方,并且保证原来程序的逻辑行为不变及正确性不被破坏。 里氏替换原则…...

代码随想录第六十三天——被围绕的区域,太平洋大西洋水流问题,最大人工岛

leetcode 130. 被围绕的区域 题目链接:被围绕的区域 步骤一:深搜或者广搜将地图周边的’O’全部改成’A’ 步骤二:遍历地图,将’O’全部改成’X’,将’A’改回’O’ class Solution { private:int dir[4][2] {-1, 0…...

Docker 项目如何使用 Dockerfile 构建镜像?

1、Docker 和 Dockerfile 的重要性 1.1、Docker 简介:讲述 Docker 的起源、它是如何革新现代软件开发的,以及它为开发者和运维团队带来的好处。重点强调 Docker 的轻量级特性和它在提高应用部署、扩展和隔离方面的优势。 本文已收录于,我的…...

实践学习PaddleScience飞桨科学工具包

实践学习PaddleScience飞桨科学工具包 动手实践,在实践中学习!本项目可以在AIStudio平台一键运行!地址:https://aistudio.baidu.com/projectdetail/4278591 本项目第一次执行会报错,再执行一次即可。若碰到莫名其妙的…...

Vue 中修改 Element 组件的 下拉菜单(Dropdown) 的样式

Vue 中修改 Element 组件的 下拉菜单(Dropdown) 的样式 今天在项目中碰到一个 UI 改造的需求,需要根据设计图把页面升级成 UI 设计师提供的设计图样式。 到最后页面改造完了,但是 UI 提供的下拉菜单样式全部是黑色半透明的,只能硬着头皮改了。…...

达梦数据库主备集群

1:服务器硬件需求 按实际业务需求,选择合适的服务器,准备 3 台服务器,一台主库服务器,一台备库服务器,一台监视器服务器,服务器参数建议如下: 硬件要求物理内存>16 GB交换区Swa…...

Spark Doris Connector 可以支持通过 Spark 读取 Doris 数据类型不兼容报错解决

1、版本介绍: doris版本: 1.2.8Spark Connector for Apache Doris 版本: spark-doris-connector-3.3_2.12-1.3.0.jar:1.3.0-SNAPSHOTspark版本:spark-3.3.1 2、Spark Doris Connector Spark Doris Connector - Apache Doris 目…...

深入理解 go chan

go 里面,在实际程序运行的过程中,往往会有很多协程在执行,通过启动多个协程的方式,我们可以更高效地利用系统资源。 而不同协程之间往往需要进行通信,不同于以往多线程程序的那种通信方式,在 go 里面是通过…...

java+vue基于Spring Boot的渔船出海及海货统计系统

该渔船出海及海货统计系统采用B/S架构、前后端分离进行设计,并采用java语言以及springboot框架进行开发。该系统主要设计并完成了管理过程中的用户注册登录、个人信息修改、用户信息、渔船信息、渔船航班、海货价格、渔船海货、非法举报、渔船黑名单等功能。该系统操…...

Linux第25步_在虚拟机中备份“ST官方的TF-A源码”

TF-A是ARM公司提供的,ST公司通过修改它,做了一个自己的TF-A代码。因为在后期开发中,若硬件被改变了,我们需要通过修改"ST官方的TF-A源码"就可以自己的TF-A代码了。为了防止源文件被误改了,我们需要将"S…...

统计学-R语言-4.1

文章目录 前言编写R函数图形的控制和布局par函数layout函数 练习 前言 安装完R软件之后就可以对其进行代码的编写了。 编写R函数 如果对数据分析有些特殊需要,已有的R包或函数不能满足,可以在R中编写自己的函数。函数的定义格式如下所示: …...

C++(1) —— 基础语法入门

目录 一、C初识 1.1 第一个C程序 1.2 注释 1.3 变量 1.4 常量 1.5 关键字 1.6 标识符命名规则 二、数据类型 2.1 整型 2.2 sizeof 关键字 2.3 实型(浮点型) 2.4 字符型 2.5 转义字符 2.6 字符串型 2.7 布尔类型 bool 2.8 数据的输入 三…...

构建基于RHEL8系列(CentOS8,AlmaLinux8,RockyLinux8等)的支持63个常见模块的PHP8.1.20的RPM包

本文适用:rhel8系列,或同类系统(CentOS8,AlmaLinux8,RockyLinux8等) 文档形成时期:2023年 因系统版本不同,构建部署应略有差异,但本文未做细分,对稍有经验者应不存在明显障碍。 因软件世界之复杂和个人能力…...

Vue-插槽(Slots)

1. 介绍 在Vue.js中,插槽是一种强大的功能,它允许你创建可重用的模板,并在使用该模板的多个地方插入自定义内容。 插槽为你提供了一种方式,可以在父组件中定义一些“插槽”,然后在子组件中使用这些插槽,插…...

新火种AI|GPT-5前瞻!GPT-5将具备哪些新能力?

作者:小岩 编辑:彩云 Sam Altman在整个AI领域,乃至整个科技领域都被看作是极具影响力的存在,而2023年OpenAI无限反转的宫斗事件更是让Sam Altman刷足了存在感,他甚至被《时代》杂志评为“2023年度CEO”。 也正因此&…...

安防视频监控系统EasyCVR设备分组中在线/离线数量统计的开发与实现

安防视频监控EasyCVR系统具备较强的兼容性,它可以支持国标GB28181、RTSP/Onvif、RTMP,以及厂家的私有协议与SDK,如:海康ehome、海康sdk、大华sdk、宇视sdk、华为sdk、萤石云sdk、乐橙sdk等。EasyCVR平台可覆盖多类型的设备接入&am…...

spring cloud之集成sentinel

写在前面 源码 。 本文一起看下spring cloud的sentinel组件的使用。 1:准备 1.1:理论 对于一个系统来说,最重要的就是高可用,那么如何实现高可用呢?你可能会说,集群部署不就可以了,但事实并…...

让车辆做到“耳听八方”,毫米波雷达芯片与系统设计

摘要: 毫米波雷达,是指工作在毫米波波段(一般为30~300GHz频域,波长1~10mm)探测的雷达。毫米波雷达体积小、质量轻、空间分辨率高,穿透“雾烟灰”的能力强,还具备全天候全天时工作的优势。在智能网联汽车体系中,毫米波雷达是系统感知层不可或缺的重要硬件,能让智能驾…...

Python如何实现数据驱动的接口自动化测试

大家在接口测试的过程中,很多时候会用到对CSV的读取操作,本文主要说明Python3对CSV的写入和读取。下面话不多说了,来一起看看详细的介绍吧。 1、需求 某API,GET方法,token,mobile,email三个参数 token为必填项mobil…...

高级分布式系统-第15讲 分布式机器学习--联邦学习

高级分布式系统汇总:高级分布式系统目录汇总-CSDN博客 联邦学习 两种常见的架构:客户-服务器架构和对等网络架构 联邦学习在传统的分布式机器学习基础上的变化。 传统的分布式机器学习:在数据中心或计算集群中使用并行训练,因为…...

使用GLM-4-9B-Chat-1M优化YOLOv8目标检测:智能标注与结果分析

使用GLM-4-9B-Chat-1M优化YOLOv8目标检测:智能标注与结果分析 1. 引言 在计算机视觉项目中,目标检测模型的训练往往需要大量标注数据,而传统的人工标注方式既耗时又费力。更让人头疼的是,当我们得到检测结果后,还需要…...

告别EEPROM!用STM32的BKP备份寄存器实现低成本数据存储(F103C8T6实战)

低成本数据存储方案:STM32 BKP备份寄存器实战指南 引言 在嵌入式系统开发中,数据存储一直是个绕不开的话题。传统方案往往依赖外置EEPROM或Flash芯片,但这意味着额外的物料成本和PCB空间占用。对于学生创客、硬件初创团队或者资源受限的小型项…...

开发者必看:Nanbeige 4.1-3B Streamlit UI深度解析与本地部署教程

开发者必看:Nanbeige 4.1-3B Streamlit UI深度解析与本地部署教程 1. 引言:当大模型遇上极简美学 如果你厌倦了千篇一律、布局拥挤的大模型Web界面,如果你希望给本地部署的AI助手一个清爽、现代、像手机聊天软件一样的家,那么今…...

如何通过运动干预改善儿童多动症的注意力问题?

通过VRAT注意力测评分析儿童多动症运动干预效果 VRAT注意力测评是一种有效的工具,用于评估儿童的注意力水平,尤其是针对多动症的孩子。在运动干预过程中,通过VRAT测评,可以清晰了解孩子在参与不同体育活动前后的注意力变化。这种评…...

Hive数据一致性问题:分桶表_分区表数据倾斜与一致性保障技巧

Hive数据一致性问题:分桶表/分区表数据倾斜与一致性保障技巧 关键词 Hive、分桶表、分区表、数据倾斜、数据一致性、事务、原子替换 摘要 深夜排查数据倾斜的崩溃、统计报表重复计算的焦虑、ETL重试导致的数据遗漏——这些是每一个Hive用户都可能遇到的“痛点”。分…...

从零构建:一个专为中文场景优化的交通标志数据集实践指南

1. 为什么需要中文专属交通标志数据集? 做计算机视觉的朋友都知道,数据集就是AI模型的"粮食"。但现成的国际通用数据集(如德国GTSRB)在中国道路上经常水土不服——我们的禁令标志是红圈白底,而欧美常用红八角…...

OpenClaw赚钱实录:从“养龙虾“到可持续变现的实践指南——OpenClaw与在线大模型服务的本质区别与能力边界

【限时免费】专栏原价299元,在2026年3月享受免费订阅专栏,赶紧关注博主并关注专栏 有任何疑问均可联系博主微信(微信号:NeumannAI),作者将亲自解答并持续优化文章内容,确保读者能快速上手变现赚…...

Python豆瓣图书数据可视化平台 Flask框架 可视化 爬虫 书籍 大数据 机器学习 计算机毕业设计(建议收藏)✅

博主介绍:✌全网粉丝10W,前互联网大厂软件研发、集结硕博英豪成立软件开发工作室,专注于计算机相关专业项目实战6年之久,累计开发项目作品上万套。凭借丰富的经验与专业实力,已帮助成千上万的学生顺利毕业,…...

YOLOv8实战:从检测框到中心坐标的精准提取与应用

1. 为什么需要提取物体中心坐标? 在目标检测任务中,我们通常使用边界框(bounding box)来标识物体的位置。但很多时候,仅仅知道物体的边界框是不够的。比如在做物体追踪时,我们需要一个更简洁的表示方式——…...

云计算,20岁生日快乐!

每天,我们都在使用一种看不见却无处不在的技术——云计算。它支撑着流媒体观看、邮件同步、照片备份以及企业级数据处理,却很少被人们直接感知。2026年3月,正值亚马逊网络服务(AWS)于2006年3月推出S3存储服务的20周年纪…...