基于面向对象基础设计——里氏替换原则

在Java中,支持抽象和多态的关键机制之一是继承。正是使用了继承,我们才可以创建实现父类中抽象方法的子类。那么,是什么规则在支配着这种特殊的继承用法呢?最佳的继承层次的特征又是什么呢?在什么情况下会使我们创建的类层次结构掉进不符合开闭原则的陷阱中呢?这就是里氏替换原则要解决的问题。

概念

里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。

里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

简单的理解为一个软件实体如果使用的是一个父类,那么一定适用于其子类,而且它察觉不出父类对象和子类对象的区别。也就是说,软件里面,把父类都替换成它的子类,程序的行为没有变化。

子类型必须能够替换掉它们的父类型。

  • 类的继承原则:里氏替换原则常用来检查两个类是否为继承关系。在符合里氏替换原则的继承关系中,使用父类代码的地方,用子类代码替换后,能够正确的执行动作处理。换句话说,如果子类替换了父类后,不能够正确执行动作,那么他们的继承关系就是不正确的,应该重新设计它们之间的关系。
  • 动作正确性保证:里氏替换原则对子类进行了约束,所以在为已存在的类进行扩展,来创建一个新的子类时,符合里氏替换原则的扩展不会给已有的系统引入新的错误。

继承的优缺点

优点:

  • 代码共享,减少创建类的工作量,每个子类都拥有父类的方法和属性
  • 提高代码的重用性
  • 子类可以形似父类,但是又异于父类。
  • 提高代码的可扩展性,实现父类的方法就可以了。许多开源框架的扩展接口都是通过继承父类来完成。
  • 提高产品或项目的开放性

缺点:

  • 继承是侵入性的,只要继承,就必须拥有父类的所有方法和属性
  • 降低了代码的灵活性,子类必须拥有父类的属性和方法,让子类有了一些约束
  • 增加了耦合性,当父类的常量,变量和方法被修改了,需要考虑子类的修改,这种修改可能带来非常糟糕的结果,要重构大量的代码

四层含义

里氏替换原则包含以下4层含义:

  • 子类可以实现父类的抽象方法,但是不能覆盖父类的非抽象方法。
  • 子类中可以增加自己特有的方法。
  • 当子类覆盖或实现父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
  • 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格。

【案例】正方形不是长方形。

在数学领域里,正方形毫无疑问是长方形,它是一个长宽相等的长方形。所以,我们开发的一个与几何图形相关的软件系统,就可以顺理成章的让正方形继承自长方形。

里氏代换原则

代码如下:

长方形类(Rectangle):

public class Rectangle {private double length;private double width;public double getLength() {return length;}public void setLength(double length) {this.length = length;}public double getWidth() {return width;}public void setWidth(double width) {this.width = width;}
}

正方形(Square):

由于正方形的长和宽相同,所以在方法setLength和setWidth中,对长度和宽度都需要赋相同值。

public class Square extends Rectangle {public void setWidth(double width) {super.setLength(width);super.setWidth(width);}public void setLength(double length) {super.setLength(length);super.setWidth(length);}
}

类RectangleDemo是我们的软件系统中的一个组件,它有一个resize方法依赖基类Rectangle,resize方法是RectandleDemo类中的一个方法,用来实现宽度逐渐增长的效果。

public class RectangleDemo {public static void resize(Rectangle rectangle) {while (rectangle.getWidth() <= rectangle.getLength()) {rectangle.setWidth(rectangle.getWidth() + 1);}}//打印长方形的长和宽public static void printLengthAndWidth(Rectangle rectangle) {System.out.println(rectangle.getLength());System.out.println(rectangle.getWidth());}public static void main(String[] args) {Rectangle rectangle = new Rectangle();rectangle.setLength(20);rectangle.setWidth(10);resize(rectangle);printLengthAndWidth(rectangle);System.out.println("============");Rectangle rectangle1 = new Square();rectangle1.setLength(10);resize(rectangle1);printLengthAndWidth(rectangle1);}
}

我们运行一下这段代码就会发现,假如我们把一个普通长方形作为参数传入resize方法,就会看到长方形宽度逐渐增长的效果,当宽度大于长度,代码就会停止,这种行为的结果符合我们的预期;假如我们再把一个正方形作为参数传入resize方法后,就会看到正方形的宽度和长度都在不断增长,代码会一直运行下去,直至系统产生溢出错误。

所以,普通的长方形是适合这段代码的,正方形不适合。

我们得出结论:在resize方法中,Rectangle类型的参数是不能被Square类型的参数所代替,如果进行了替换就得不到预期结果。因此,Square类和Rectangle类之间的继承关系违反了里氏代换原则,它们之间的继承关系不成立,正方形不是长方形。

如何改进呢?此时我们需要重新设计他们之间的关系。抽象出来一个四边形接口(Quadrilateral),让Rectangle类和Square类实现Quadrilateral接口。

里氏代换原则

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mac端口扫描工具

端口扫描工具 Mac内置了一个网络工具 网络使用工具 按住 Command 空格 然后搜索 “网络实用工具” 或 “Network Utility” 即可 域名/ip转换Lookup ping功能 端口扫描 https://zhhll.icu/2022/Mac/端口扫描工具/ 本文由 mdnice 多平台发布

【深度学习_TensorFlow】梯度下降

写在前面 一直不太理解梯度下降算法是什么意思&#xff0c;今天我们就解开它神秘的面纱 写在中间 线性回归方程 如果要求出一条直线&#xff0c;我们只需知道直线上的两个不重合的点&#xff0c;就可以通过解方程组来求出直线 但是&#xff0c;如果我们选取的这两个点不在直…

eclipse was unable to locate its companion shared library

当转移或者Copy工程时&#xff0c; eclipse was unable to locate its companion shared library eclipse.ini 里面的路径配置错误导致 --launcher.library C:/Users/**/.p2/pool/plugins/org.eclipse.equinox. launcher.win32.win32.x86_64_1.2.700.v20221108-1024 -product …

ubuntu下,在vscode中使用platformio出现 Can not find working Python 3.6+ Interpreter的问题

有一段时间没有使用platformio了&#xff0c;今天突然使用的时候&#xff0c;发现用不了&#xff0c;报错&#xff1a; Ubuntu PlatformIO: Can not find working Python 3.6 Interpreter. Please install the latest Python 3 and restart VSCode。 上网一查&#xff0c;发现…

Java版工程行业管理系统源码-专业的工程管理软件-em提供一站式服务

​ Java版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 功能清单如下&#xff1a; 首页 工作台&#xff1a;待办工作、消息通知、预警信息&#xff0c;点击可进入相应的列表 项目进度图表&#xff1a;选择&#xff08;总体或单个&#xff09;项目…

Docker 安装

1、下载 去官网下载Docker: Accelerated, Containerized Application Development 下载完成就ok了&#xff0c;下载之后估计是打不开的&#xff0c;我下了一个 去微软商店找wsl然后下载&#xff0c;下载之后Docker就好了。 这个就能用了

72. 编辑距离

题目链接&#xff1a;力扣 解题思路&#xff1a;

Modbus tcp转ETHERCAT网关modbus tcp/ip协议

捷米JM-ECT-TCP网关能够连接到Modbus tcp总线和ETHERCAT总线中&#xff0c;实现两种不同协议设备之间的通讯。这个网关能够大大提高工业生产的效率和生产效益&#xff0c;让生产变得更加智能化。捷米JM-ECT-TCP 是自主研发的一款 ETHERCAT 从站功能的通讯网关。该产品主要功能是…

Jetson Docker 编译 FFmpeg 支持硬解nvmpi和cuvid

0 设备和docker信息 设备为NVIDIA Jetson Xavier NX&#xff0c;jetpack版本为 5.1.1 [L4T 35.3.1] 使用的docker镜像为nvcr.io/nvidia/l4t-ml:r35.2.1-py3,详见https://catalog.ngc.nvidia.com/orgs/nvidia/containers/l4t-ml 使用下列命令拉取镜像: sudo docker pull nvcr…

前端Vue入门-day06-路由进阶

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 路由的封装抽离 声明式导航 导航链接 两个类名 自定义高亮类名 跳转传参 1. 查询参数传参 2. 动态…

Python接口自动化-requests模块之post请求

一、源码解析 def post(url, dataNone, jsonNone, **kwargs):r"""Sends a POST request.:param url: URL for the new :class:Request object.:param data: (optional) Dictionary, list of tuples, bytes, or file-likeobject to send in the body of the :cla…

Unity通过代码切换材质

效果展示 代码 using System.Collections; using System.Collections.Generic; using UnityEngine;public class MaterialSwitcher : MonoBehaviour {public Material newMaterial; // 新材质private Material oldMaterial; // 旧材质private Renderer renderer; // 渲染器组件…

秋招算法备战第37天 | 738.单调递增的数字、968.监控二叉树、贪心算法总结

738. 单调递增的数字 - 力扣&#xff08;LeetCode&#xff09; 这个问题是关于找到一个小于或等于给定数字n的最大单调递增数字。 我们可以将数字n转换为字符数组&#xff0c;然后从左到右扫描&#xff0c;寻找第一个违反单调递增条件的位置。一旦找到这样的位置&#xff0c;…

线上java程序CPU及内存占用过高问题排查总结

背景 最近发现线上的一个JAVA程序总是过段时间慢慢卡死&#xff0c;最后导致无法提供服务&#xff0c;外部请求接口超时。 经排查发现&#xff0c;该程序CPU及内存占用都很高&#xff0c;导致整个系统负载很高。 到这里&#xff0c;就想到了对程序内存进行分析。排查过程 查询…

直播预告|还在说做不出、改不好地图贴图?一次直播包教包会!

在EasyV中&#xff0c;地图组件通常会作为可视化大屏中的「主视觉」部分&#xff0c;用户通过地图组件的使用&#xff0c;可以极大程度上提高搭建的效率以及视觉效果。正因如此&#xff0c;我们的素材广场中大多模板也将「地图」作为核心部分&#xff0c;以此来方便用户快速套用…

golang函数传参——值传递理解

做了五年的go开发&#xff0c;却并没有什么成长&#xff0c;都停留在了业务层面了。一直以为golang中函数传参&#xff0c;如果传的是引用类型&#xff0c;则是以引用传递&#xff0c;造成这样的误解&#xff0c;实在也不能怪我。我们来看一个例子&#xff0c;众所周知&#xf…

#rust taur运行报错#

场景:在window11系统上运行 tauri桌面莹应用&#xff0c;提示错误。 Visual Studio 2022 生成工具 安装的sdk11 , rust运行模式是stable-x86_64-pc-window-gnu&#xff0c; 运行npm run tauir dev 一致失败&#xff0c;失败信息如下 原因&#xff1a;1&#xff1a;在window11系…

数字图像处理(番外)图像增强

图像增强 图像增强的方法是通过一定手段对原图像附加一些信息或变换数据&#xff0c;有选择地突出图像中感兴趣的特征或者抑制(掩盖)图像中某些不需要的特征&#xff0c;使图像与视觉响应特性相匹配。 图像对比度 图像对比度计算方式如下&#xff1a; C ∑ δ δ ( i , j …

【方法】PDF可以转换成Word文档吗?如何操作?

很多人喜欢在工作中使用PDF&#xff0c;因为PDF格式可以准确地保留文档的原始格式&#xff0c;比如字体、图像、布局和颜色等。 但如果编辑文档的话&#xff0c;PDF还是没有Word文档方便。那可以将PDF转换成Word格式&#xff0c;再来编辑吗&#xff1f;如何操作呢&#xff1f;…

Vue2 第二十节 vue-router (一)

1.相关概念理解 2.基本路由 3.嵌套路由&#xff08;多级路由&#xff09; 一.相关概念理解 1.1 vue-router的理解 路由&#xff1a;就是一组key-value的对应关系, key为路径&#xff0c;value可能是function或者component多个路由&#xff0c;需要经过路由器的管理编程中的…