SOLID原则学习,里氏替换原则(Liskov Substitution Principle, LSP)

SOLID - Liskov替换原则(Liskov Substitution Principle) 在软件设计领域,SOLID原则中的"L"代表Liskov替换原则(Liskov Substitution Principle, 简称LSP)。Liskov替换原则由计算机科学家Barbara Liskov在她关于数据抽象和类型理论的工作中提出,是面向对象编程中的一项核心原则。(英语:Barbara Liskov,1939年11月7日—),本名芭芭拉·简·休伯曼(英语:Barbara Jane Huberman)。美国计算机科学家,2008年得主,2004年得主。现任电子电气与计算机科学系教授。 阅读详情

在这里插入图片描述


1. 定义

里氏替换原则(Liskov Substitution Principle, LSP) 是面向对象设计中的五大原则(SOLID)之一,由 Barbara Liskov 提出。它是继承关系的核心原则,确保子类可以替换父类而不影响程序的正确性。

子类对象必须能够替换父类对象,且替换后程序的行为不会发生变化。

换句话说,如果一个程序使用了一个父类的对象,那么它应该能够使用该父类的任何子类对象,而不会产生错误或意外行为。


2. 里氏替换原则的核心思想

1. 子类必须完全实现父类的行为:

  • 子类必须实现父类的所有方法,且不能改变父类的行为。

  • 子类可以扩展父类的功能,但不能修改父类的原有功能。

2. 子类不能违背父类的约束:

  • 子类的方法参数范围必须与父类一致或更宽松。

  • 子类的方法返回值范围必须与父类一致或更严格。

3. 子类不能抛出父类未声明的异常:

  • 子类的方法不能抛出父类方法未声明的异常。

3. 违反里氏替换原则的后果

如果子类不能完全替换父类,可能会导致以下问题:

程序行为不一致。

难以维护和扩展。

引入隐藏的 bug。


4. 实现里氏替换原则的方法

1. 使用抽象基类或接口:

  • 定义清晰的接口或抽象类,确保子类实现所有必要的行为。

2. 避免子类修改父类的行为:

  • 子类只能扩展父类的功能,不能改变父类的核心逻辑。

3. 遵循契约设计:

  • 父类定义明确的契约(前置条件、后置条件、不变式),子类必须遵守这些契约。

4. 实例分析

场景描述
假设我们有一个表示矩形的类 Rectangle,它有一个计算面积的方法。现在我们需要支持正方形 Square,正方形是一种特殊的矩形。

违反里氏替换原则的实现

class Rectangle {
protected:
    int width, height;

public:
    void setWidth(int w) { width = w; }
    void setHeight(int h) { height = h; }
    int getWidth() const { return width; }
    int getHeight() const { return height; }
    int calculateArea() const { return width * height; }
};

class Square : public Rectangle {
public:
    void setWidth(int w) {
        width = w;
        height = w; // 正方形需要保持宽高相等
    }

    void setHeight(int h) {
        width = h;
        height = h; // 正方形需要保持宽高相等
    }
};

void processRectangle(Rectangle& rect) {
    rect.setWidth(5);
    rect.setHeight(4);
    std::cout << "Expected area: 20, Actual area: " << rect.calculateArea() << std::endl;
}

int main() {
    Rectangle rect;
    processRectangle(rect); // 输出: Expected area: 20, Actual area: 20

    Square square;
    processRectangle(square); // 输出: Expected area: 20, Actual area: 16
    return 0;
}

问题分析:

  • Square 是 Rectangle 的子类,但它修改了 setWidth 和 setHeight 的行为,导致 processRectangle 函数的行为不一致。

  • 这违反了里氏替换原则,因为 Square 不能完全替换 Rectangle。


符合里氏替换原则的实现
为了避免违反里氏替换原则,我们可以重新设计类结构,将 Square 和 Rectangle 分离,或者使用组合代替继承。

class Shape {
public:
    virtual int calculateArea() const = 0;
    virtual ~Shape() = default;
};

class Rectangle : public Shape {
private:
    int width, height;

public:
    Rectangle(int w, int h) : width(w), height(h) {}
    int calculateArea() const override {
        return width * height;
    }
};

class Square : public Shape {
private:
    int side;

public:
    Square(int s) : side(s) {}
    int calculateArea() const override {
        return side * side;
    }
};

void processShape(const Shape& shape) {
    std::cout << "Area: " << shape.calculateArea() << std::endl;
}

int main() {
    Rectangle rect(5, 4);
    processShape(rect); // 输出: Area: 20

    Square square(4);
    processShape(square); // 输出: Area: 16
    return 0;
}

改进点:

Rectangle 和 Square 都继承自 Shape,但它们没有直接的继承关系。

processShape 函数可以处理任何 Shape 类型的对象,符合里氏替换原则。

5. 总结

里氏替换原则的核心是确保子类可以完全替换父类而不影响程序的正确性。通过合理设计类的继承关系,避免子类修改父类的行为,可以提高代码的可维护性和可扩展性。在实际开发中,如果发现子类无法完全替换父类,可能需要重新审视类的设计,考虑使用组合或其他设计模式来解决问题。

C++里氏替换原则实战指南:从矩形-正方形反例到图形系统设计 在面向对象编程中,继承是实现代码复用和多态的核心机制。其原理在于通过建立类之间的“is-a”关系,让子类可以扩展或重写父类的行为。这一机制的技术价值在于提升代码的可维护性、可扩展性和可测试性,广泛应用于构建复杂的软件系统,如GUI框架、游戏引擎和业务中间件。然而,不当的继承设计会破坏多态的基础,导致难以察觉的运行时错误,这正是里氏替换原则(LSP)要解决的核心问题。LSP要求子类对象必须能无缝替换父类对象,且不破坏程序的正确性。本文以C++为背景,通过经典的矩形-正方形继承陷阱这一实战案例,深入剖析违反LS 阅读详情

相关推荐

Python爬虫实战(基础篇)—23—爬取微信小程序<随机目的地旅行>生成随机地址(附GUI版)

爬取微信小程序<随机目的地旅行>生成随机地址

weixin_42636075的博客 1228

设计模式六大原则:里氏替换原则详细说明和案例示范

子类对象可以替换父类对象,程序的行为不会发生变化。子类在扩展父类功能的同时,不能改变父类原有的行为。通俗地说,如果我们在程序中使用的是一个基类对象,那么在不修改程序的前提下,用它的子类对象替换这个基类对象,程序应该仍然可以正常运行。里氏替换原则是面向对象设计中的重要原则之一,它确保子类能够替换父类而不会影响系统的正确性。在设计系统时,遵循里氏替换原则可以帮助我们构建一个更稳定、可扩展的系统,避免因子类行为改变而引发的潜在问题。在实际开发中,建议开发人员在设计继承体系时,时刻考虑子类是否能够替代父类。

J老熊 2988

深入解析uncompyle6反编译pyc文件及常见错误修复指南

本文深入解析了使用uncompyle6工具反编译Python字节码文件(.pyc)的完整流程。针对实践中最高频的KeyError和Python版本范围限制两大错误,提供了详细的修复方案与步骤指南,并分享了处理复杂反编译结果的高级技巧与安全实践,是进行代码恢复、调试与分析的重要参考。

uber9的博客 534

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

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

weixin_30197685的博客 3441

AVM环视:系统搭建整体流程

文章目录前言一、畸变矫正鱼眼相机二、透视变换三、图像拼接四、图像融合总结 前言 由于正在做AVM环视,所以记录一下整个过程,归纳为一个系列文章,并对期间遇到的错误进行归纳整理。本文主要概括了AVM环视的大致流程和逻辑,不涉及代码部分,仅供思路参考,其中涉及的具体技术会在后期整理为小章节进行分析。 一、畸变矫正 鱼眼相机 做环视基本上使用到的是鱼眼照片,因为鱼眼相机的视角范围可以达到220°到230°,所以一个相机就可以采集到一个面的全部信息,例如汽车具有四个面,那么在汽车的前后左右分别安装一个鱼眼相机.

qq_34468890的博客 6536

从贝叶斯网络到结构因果模型:因果推理在可解释AI中的实践

在机器学习和数据分析领域,理解变量间的因果关系而不仅仅是统计关联,是构建可信赖、可解释系统的关键。概率图模型,特别是贝叶斯网络,通过有向无环图和条件概率表,能够高效地编码和推理变量间的条件依赖关系,广泛应用于医疗诊断、风险评估等场景。然而,贝叶斯网络主要刻画的是“是什么”的关联,难以回答“如果…会怎样”的因果问题。为了进行因果推断,需要引入结构因果模型和do-演算,它们通过定义结构方程和模拟干预操作,能够区分混杂效应,估计处理变量对结果变量的净因果影响。这一套方法论对于可解释人工智能至关重要,它能生成反事实

weixin_42168902的博客 379

里氏替换原则(Liskov Substitution principle)

在软件设计、编码过程中有几个基本原则即SOLID原则,学习理解能够帮忙我们写出更健壮的代码。SOLID是五个基本原则的首字母。这五个原则如下: Single responsibility Open–closed Liskov substitution Interface segregation Dependency inversion 此篇来学习一下里氏替换原则(Liskov Substitution responsibility) 那么什么是里氏替换原则(LSP)呢? Barb.

一斗水的专栏 1333

AMBA-CHI协议详解(十)

该规范提供了一个Request Retry(请求重试)机制,确保当请求到达一个Completer 时,它要么被接受,要么被给予一个RetryAck响应,以防止阻塞REQ通道。

Crazzy_M的博客 986

里氏替换原则是什么?gh_mirrors/so/solid.python项目带你轻松掌握

里氏替换原则(Liskov Substitution Principle,LSP)是SOLID五大设计原则之一,由Barbara Liskov提出。在gh_mirrors/so/solid.python项目中,这一原则通过简洁的Python代码示例得到了生动诠释,帮助开发者理解如何构建灵活可扩展的面向对象系统。 ## 🌟 里氏替换原则的核心思想 简单来说,里氏替换原则要求**子类必须能够替换

gitblog_00370的博客 876

红队内网攻防渗透:内网渗透之内网对抗:实战项目&VPC1打靶&PHP-RCE&三层代理路由&防火墙上线&密码喷射&域控提权

上面已经抓取到了密码,但是我们通过拓扑分析看,要想上线win10只能通过正向shell,因为win7的防火墙是开的,win10反向主动连接肯定入站规则拦截,而win10自己没有开防火墙,所以win7主动正向连接就可以上线。win7开启了防火墙,入站规则肯定是会拦截我们的请求的,所以我们需要反向上线,让win7主动来连接我们。这里扫描发现是存在ms17010漏洞的,但是想上线192.168.2.142的话,我们没有路由是上线不了的,这里有两种办法:一种msf直接生成后门,另一种cs移植后门到msf。

HACKONE的博客 909

【DRAM存储器一】基本存储单元、阵列结构、读写原理

详述DRAM存储器存储单元、存储阵列结构和读写原理

highman110的博客 7254

里氏替换原则(Liskov Substitution Principle, LSP)是面向对象设计五大基本原则(SOLID)之一

✅ 合理重构:避免继承“is-a”误用,改用组合或接口抽象(如定义`Shape`接口,`Rectangle`和`Square`各自实现)。 “正方形继承长方形”是违反里氏替换原则(LSP)最经典的反例,其根本原因不在于几何关系的直观合理性(数学上正方形确实是特殊的长方形),而在于**面向对象中“继承”表达的是行为契约的可替换性,而非单纯数学子集关系**。 🔍 根本契约冲突在于: 长方形(`Rectangle`)的隐式行为契约规定——**宽(width)和高(height)是两个独立可变的状态量**

[Blog][Domain] programb.blog.youkuaiyun.com 260

TC3XX - MCAL知识点(三十五):Tasking编译器下,Tc3xx的MCAL代码如何使用多核,多核中断、多核任务、多核外设之间的MCAL配置与代码例程

文章用于记录在裸机情况下使用多核Tc387芯片,如何高效运行以及正确的代码、工程属性、链接文件配置等问题,相互结合、相互影响的地方,以示后续使用记录。

梦想技术家 961

Python 工匠:写好面向对象代码的原则(中)——里氏替换原则(LSP)实战详解

本文是“Python 工匠”系列的第 13 篇文章,聚焦 SOLID 设计原则中的 **L(里氏替换原则,Liskov Substitution Principle)**。文章以“用户模型 `User`/`Admin`”与“帖子查询”两个贴近真实业务的 Python 案例为主线,剖析子类重写方法抛异常、修改方法返回值、收紧方法参数三类典型违反场景,并给出从源头重构继承关系、统一返回类型与参数签名的

gitblog_00453的博客 457

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

【SOLID原则前端中的应用】里氏替换原则(Liskov Substitution Principle,LSP)- vue3示例

上面的示例中,首先创建了一个通用输入组件BaseInput,它可以接受任何类型的输入。然后,创建了一个电子邮件输入组件EmailInput,通过扩展BaseInput来专门处理电子邮件输入,并增加了电子邮件格式验证和错误提示功能。EmailInput继承了BaseInput的所有属性和方法,同时增加了对电子邮件格式的验证。如果输入的电子邮件格式不正确,会显示错误提示信息。这样,我们可以在需要电子邮件输入的地方无缝替换BaseInput为EmailInput,而不需要修改其他代码。

无言非影的博客 612

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

OpCore-Simplify 一键生成 OpenCore EFI:10 分钟从硬件报告到可写盘引导器

做黑苹果,最劝退的一步多半是"配引导"。OpCore-Simplify 把这件事压成了一条流水线:导入一份硬件报告、确认 macOS 版本,选 6 构建,Results/ 里就躺着一套能直接写进 U 盘的 OpenCore EFI。新手 10 分钟量级跑完,不用再对着论坛逐个核对设备 ID。 ## 先核对支持名单:你的硬件能不能用 动手前对号入座,省得白跑一趟。下表摘自官方 [README](

gitblog_00038的博客 222

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

SOLID 原则

其实,外国人喜欢把一些重要且普遍或大力宣传让特定群体知道的东西弄成某几个单词或句子的首字母组成一个新的“词”,而这个词一般人是无法从字面上去理解,也不可能,因为它是纯粹的字母,根本没有什么特殊的意思,必须额外的脑补很多这些知识,你必须去找出它们原来是哪些单词才有可能知道它的意思。这也是汉语的神奇之处,哪怕是新词,也能通过组词把它的意思呈现出来。所以SOLID也是这样来的,SOLID如果不在软件行业里的话,真的很难知道什么是SOLID principle,它其实是软件设计的五大原则: 原则1:Single

weixin_40763897的博客 1010

eltdx本地复权原理完全指南:GBBQ股本变迁如何计算前复权与后复权系数

eltdx 是通达信 A 股行情协议 Python 库,它的本地复权功能可以基于 GBBQ 股本变迁(权息资料)在本地计算前复权、后复权系数,再把系数应用到不复权 K 线,得到与主站复权 K 线几乎一致的本地复权结果。本文完整拆解这套原理:数据从哪里来、公式怎么推、多事件如何复合,新手也能看懂。 [![eltdx 通达信行情库功能目录界面,展示 K 线与复权等接口能力](https://raw.

gitblog_00069的博客 689

img2img-turbo 快速上手指南:3 步完成图像到图像转换

img2img-turbo 是一个基于 Stable Diffusion Turbo 的单步 image-to-image(图像到图像)转换模型,支持 sketch2image(草图转图片)、edge2image(线稿转图片)、day2night(白天转夜晚)、rainy2clear(雨天转晴天)等转换任务,在 GPU 上对 512x512 图像的推理只需 0.1 到 0.3 秒。 ## 从几笔

gitblog_01034的博客 786

【设计模式之旅】10|设计原则之里式替换原则(LSP)

前言:前面我们介绍了 SOLID 原则中的单一职责原则和开闭原则,这两个原则都比较重要,想要灵活应用也比较难,需要你在实践中多加练习、多加体会。今天,我们再来聊聊SOLID 中的“L”对应的原则:里式替换原则。整体上来讲,这个设计原则是比较简单、容易理解和掌握的。今天我主要通过几个反例,带你看看,哪些代码是违反里式替换原则的?我们该如何将它们改造成满足里式替换原则?除此之外,这条原则从定义上看起来,跟我们之前讲过的“多态”有点类似。所以,我今天也会讲一下,它跟多态的区别。 如何理解里式替换原则? ..

qq_29819209的博客 271

设计一个矩形类rectangle_想做软件开发得先搞懂软件的设计原则—SOLID原则之里氏替换...

里氏替换原则上一篇文章我们学习了SOLID中的开闭原则(O),如果还不了解开闭原则的小伙伴可以先去看了我的这篇文章再回来噢想做软件开发得先搞懂软件的设计原则—SOLID原则之开闭原则 今天我们的主题是SOLID中的L,也就是里氏替换原则;里氏替换原则(以Barbara Liskov的名字命名)规定,如果一个接口接受一个父类型的对象,那么它应该同样接受一个子类型的对象,而不会破坏任何东西。让我们来看...

weixin_39945789的博客 236

软件构造中Behavioral subtyping and Liskov Substitution Principle

一、Behavioral subtyping二、 Liskov Substitution Principle (LSP) 2.1 协变 2.2 逆变2.3 泛型中的LSP三、总结

与温暖。的博客 277

Behavioral subtyping & Liskov Substitution Priciple(LSP)

1、Barbara Liskov简介 Barbara Liskov (1939- ),美国第一位计算机科学方向的女博士,2008年图灵奖获得者。 她提出了第一个支持数据抽象的面向对象编程语言CLU,对现代主流语言如C++/Java/Python /Ruby/C#都有深远的影响。她所提炼出来的数据抽象思想,成为软件工程的重要精髓之一。 她提出的“Liskov替换原则”,是面向对象最重要的几大原则(SOLID)之一。 2、Behavioral subtyp...

tiantian_norain的博客 291

Python实践提升-面向对象设计原则(下)

同时,对于 LocalHNWebPage 类来说,它也不需要再纠结如何实现 get_generated_at() 方法,而只要认准那个只返回文本的 ContentOnlyHNWebPage 接口,实现其中的 get_text() 方法就行,如图 11-5 所示。类似的情况还有许多,比如一个负责发短信的函数,本身只需要两个参数:电话号码(phone_number)和用户姓名(username),但是函数依赖了整个用户对象(User),里面包含了几十个它根本不关心的其他字段和方法。

andyyah晓波的博客 1286

面向对象设计的五个核心原则——SOLID

原则在快餐店点餐系统中的体现SRP菜单、计算价格、支付、打单分成不同类OCP新增折扣(节日/学生)不修改价格计算代码LSP所有支付方式(现金/信用卡)行为一致,不抛出额外限制ISP堂食不需要短信接口,外卖才需要,接口分开DIP点餐控制器依赖接口,而不是具体的微信支付这样设计出来的系统,后续加新菜品、新支付方式、新折扣活动,都只需新增类,而不是在旧代码里改来改去,也更容易写单元测试。

学习记录 667

mongotemplate中save抛出异常_想写好面向对象的代码,这几篇一定要看(中)

在 上一篇文章 里,我用一个虚拟小项目作为例子,讲解了“SOLID”设计原则中的前两位成员:S(单一职责原则)与 O(开放-关闭原则)。在这篇文章中,我将继续介绍 SOLID 原则的第三位成员:L(里氏替换原则)。里氏替换原则与继承在开始前,我觉得有必要先提一下 继承(Inheritance)。因为和前面两条非常抽象的原则不同,“里氏替换原则”是一条非常具体的,和类继承有关的原则。在 OOP 世界...

weixin_39921087的博客 329
上一篇: SOLID原则学习,开闭原则(Open Closed Principle, OCP)
下一篇: SOLID原则学习,接口隔离原则(Interface Segregation Principle, ISP)
智驾
博客等级 码龄11年 6670粉丝 574原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

智驾

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值