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

设计原则学习之里氏替换原则

以下内容均来自抖音号【it楠老师教java】的设计模式课程。

1、原理概述

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

2、简单的示例1

// 基类:鸟类
public class Bird {
public void fly () {
System . out . println ( "I can fly" );
} }
// 子类:企鹅类
public class Penguin extends Bird {
// 企鹅不能飞,所以覆盖了基类的 fly 方法,但这违反了里氏替换原则
public void fly () {
throw new UnsupportedOperationException ( "Penguins can't fly" );
}
}
为了遵循LSP,我们可以重新设计类结构,将能飞的行为抽象到一个接口中,让需要飞行能力的鸟类实现这个接口:
// 飞行行为接口
public interface Flyable {
void fly ();
}
// 基类:鸟类
public class Bird {
}
// 子类:能飞的鸟类
public class FlyingBird extends Bird implements Flyable {
@Override
public void fly () {
System . out . println ( "I can fly" );
}
}
// 子类:企鹅类,不实现 Flyable 接口
public class Penguin extends Bird {
}

这里就该明确那些方法是通用的,哪些方法是部分能用的。

比如通用的方法可以放到class bird里。

public void say(){ System.out.println("我属于鸟科")}

public void say(){ System.out.println("我又一双翅膀,尽管不能飞")}

不同的用的方法,可以放到接口里比如有的鸟很小 有的鸟很大

interface  BigBird{ double height()}

interface  SmallBird{ double height()}

上述可能不太准确,但是核心思想就是抽取公共的方法到类里,抽取特殊的方法到接口里。

再举个例 比如

class  door{

//核心方法 只要是门 不管你啥样的 你肯定又面积吧,有价格吧

int price();

int area();

}

但是有的门市防火门 有的是防盗门, 有的是....

interface FangHuo{ void canFangHuo()};

interface FangDao{ void canFangDao()};

3、示例2

我们再来看一个基于数据库操作的案例。假设我们正在开发一个支持多种数据库的程序,包括MySQL、PostgreSQL和SQLite。我们可以使用里氏替换原则来设计合适的类结构,确保代码的可维护性和扩展性。 首先,我们定义一个抽象的Database 基类,它包含一些通用的数据库操作方法, 如 connect() 、disconnect() 和 executeQuery() 。这些方法的具体实现将在 子类中完成。

public abstract class Database {
public abstract void connect ();
public abstract void disconnect ();
public abstract void executeQuery ( String query );
}

然后,为每种数据库类型创建一个子类,继承自 Database 基类。这些子类需要实现基类中定义的抽象方法,并可以添加特定于各自数据库的方法。

这里新手要思考下 为什么用abstract class 怎么不用class 怎么不用interface

不用class,因为我这里还没有具体到那类数据源,其实可以定义个jdbcDatabase ,定义属性driver,url,user ,password。就是更一部的抽取

不用interface 因为connect close 和query是所有数据源都有操作!!!

public class MySQLDatabase extends Database {
@Override
public void connect () {
// 实现 MySQL 的连接逻辑
}
@Override
public void disconnect () {
// 实现 MySQL 的断开连接逻辑
}
@Override
public void executeQuery ( String query ) {
// 实现 MySQL 的查询逻辑
}
// 其他针对 MySQL 的特定方法
}
public class PostgreSQLDatabase extends Database {
// 类似地,为 PostgreSQL 实现相应的方法
}
public class SQLiteDatabase extends Database {
// 类似地,为 SQLite 实现相应的方法
}

 

这样设计的好处是,我们可以在不同的数据库类型之间灵活切换,而不需要修改大量代码。只要这些子类遵循里氏替换原则,我们就可以放心地使用基类的引用来操作不同类型的数据库。例如: 

public class DatabaseClient {
private Database database ;
public DatabaseClient ( Database database ) {
this . database = database ;
}
public void performDatabaseOperations () {
database . connect ();
database . executeQuery ( "SELECT * FROM users" );
database . disconnect ();
}
}
public class Main {
public static void main ( String [] args ) {
// 使用 MySQL 数据库
DatabaseClient client1 = new DatabaseClient ( new MySQLDatabase ());
client1 . performDatabaseOperations ();
// 切换到 PostgreSQL 数据库
DatabaseClient client2 = new DatabaseClient ( new PostgreSQLDatabase ());
client2 . performDatabaseOperations ();
// 切换到 SQLite 数据库
DatabaseClient client3 = new DatabaseClient ( new SQLiteDatabase ());
client3 . performDatabaseOperations ();
}
}

好了,我们稍微总结一下。虽然从定义描述和代码实现上来看,多态和里式替换有点类似,但它们关注的角度是不一样的。多态是面向对象编程的一大特性,也是面向对象编程语言的一种语法。它是一种代码实现的思路。而里式替换是一种设计原则,是用来指导继承关系中子类该如何设计的,子类的设计要保证在替换父类的时候,不改变原有程序的逻辑以及不破坏原有程序的正确性。 

2 、哪些代码明显违背了 LSP

1.子类覆盖或修改了基类的方法

当子类覆盖或修改基类的方法时,可能导致子类无法替换基类的实例而不引起问题。这违反了LSP,会导致代码变得脆弱和不易维护。

public class Bird {
public void fly () {
System . out . println ( "I can fly" );
}
}
public class Penguin extends Bird {
@Override
public void fly () {
throw new UnsupportedOperationException ( "Penguins can't fly" );
}
}
在这个例子中, Penguin 类覆盖了 Bird 类的 fly() 方法,抛出了一个异常。这违反了LSP,因为现在 Penguin 实例无法替换 Bird 实例而不引发问题。

 

2、子类违反了基类的约束条件

当子类违反了基类中定义的约束条件(如输入、输出或异常等),也会违反LSP

public class Stack {
private int top ;
private int [] elements ;
public Stack ( int size ) {
elements = new int [ size ];
top = - 1 ;
}
public void push ( int value ) {
if ( top >= elements . length - 1 ) {
throw new IllegalStateException ( "Stack is full" );
} elements [ ++ top ] = value ;
}
public int pop () {
if ( top < 0 ) {
throw new IllegalStateException ( "Stack is empty" );
}
return elements [ top -- ];
}
}
// 正数的栈
public class NonNegativeStack extends Stack {
public NonNegativeStack ( int size ) {
super ( size );
}
@Override
public void push ( int value ) {
if ( value < 0 ) {
throw new IllegalArgumentException ( "Only non-negative values are allowed" );
}
super . push ( value );
}
}
// 正确的写法
public class NonNegativeStack extends Stack {
public NonNegativeStack ( int size ) {
super ( size );
}
public void pushNonNegative ( int value ) {
if ( value < 0 ) {
throw new IllegalArgumentException ( "Only non-negative values are allowed" );
}
super . push ( value );
}
}

这里感觉给的资料有问题。。。等我看完视频在说 

相关文章:

设计原则学习之里氏替换原则

以下内容均来自抖音号【it楠老师教java】的设计模式课程。 1、原理概述 子类对象&#xff08;objectofsubtype/derivedclass&#xff09;能够替换程序&#xff08;program&#xff09;中父类对象&#xff08;objectofbase/parentclass&#xff09;出现的任何地方&#xff0c…...

排序进行曲-v4.0

文章目录 小程一言快速排序步骤详细解释具体步骤 举例总结 复杂度分析时间复杂度分析&#xff1a;空间复杂度分析&#xff1a;注意 应用场景总结 实际举例结果总结 代码实现结果解释 小程一言 这篇文章是在排序进行曲3.0之后的续讲&#xff0c; 这篇文章主要是对快速排序进行细…...

Flink 系列四 Flink 运行时架构

目录 前言 介绍 1、程序结构 1.1、Source 1.2、Transformation 1.3、Sink 1.4、数据流 2、Flink运行时组件 2.1、Dispatcher 2.2、JobManager 2.3、TaskManager 2.4、ResourceManager 3、任务提交流程 3.1、standalone 模式 3.2、yarn 模式 4、任务调度原理 4…...

14-3_Qt 5.9 C++开发指南_QUdpSocket实现 UDP 通信_UDP 单播和广播

文章目录 1.UDP通信概述2. UDP 单播和广播2.1 UDP 通信实例程序功能2.2 主窗口类定义和构造函数2.3 UDP通信的实现2.4 源码2.4.1 可视化UI设计2.4.2 mainwindow.h2.4.3 mainwindow.cpp 1.UDP通信概述 UDP(User Datagram Protocol&#xff0c;用户数据报协议)是轻量的、不可靠的…...

【知识图谱】图数据库Neo4jDesktop的安装图文详解(小白适用)

neo4j 的安装需要有jdk环境的支持。因此在安装Neo4j之前&#xff0c;需要安装Java JDK。 一.安装JDK 参考文章https://blog.csdn.net/weixin_41824534/article/details/104147067?spm1001.2014.3001.5502 二.Neo4j下载 进入Neo4j官网 选择下载中心 下滑选择Neo4j Deskto…...

kafka中幂等性producer和事务性producer

幂等性producer 在Kafka中,“幂等性生产者”的概念是指一种特性,它确保消息在生产者的发送操作被重试时仅发送一次。幂等性是一种重要的特性,因为在分布式系统中,网络问题或其他故障可能导致生产者发送的消息在传输过程中失败,从而需要重新发送。如果生产者没有幂等性保证…...

静态路由 (华为设备)

默认路由&#xff1a;当路由器 收到目标地址不在路由表中的数据包时&#xff0c;将会 全部 发送 到 默认路由所定义的吓一跳 &#xff0c;作为位置地址 数据包的 最后求助方式&#xff0c;这就是默认路由器的功能&#xff0c;默认路由的使用&#xff0c;可以大大的节省系统资源…...

Django学习笔记-默认的用户认证系统(auth)

一、Django默认的用户认证系统 Django 自带一个用户验证系统。它负责处理用户账号、组、权限和基于cookie的用户会话。 Django 验证系统处理验证和授权。简单来说&#xff0c;验证检验用户是否是他们的用户&#xff0c;授权决定已验证用户能做什么。这里的术语验证用于指代这…...

[SQL挖掘机] - 存储过程

介绍: 当你在sql中需要多次执行相同的一组sql语句时&#xff0c;存储过程是一个非常有用的工具。它是一段预先定义好的sql代码块&#xff0c;可以被命名并保存在数据库中&#xff0c;以便重复使用。 存储过程可以包含多个sql语句、逻辑流程、条件判断和循环等&#xff0c;可以…...

MySQL8.0.32详细安装教程(奶妈级手把手教你安装)

MySQL安装详解 前言 对于无论是刚开始接触编程的小伙伴&#xff0c;还是有了几年工作经验的程序猿&#xff08;程序媛&#xff09;来讲&#xff0c;数据库的安装一直都是一个比 较复杂的过程&#xff0c;安装完成以后可能会记得一段时间&#xff0c;但是等到我们换了一台电脑或…...

glut太阳系源码修改和对cpu占用观察

#include <GL/glut.h> static int day 100; // day 的变化&#xff1a;从 0 到 359 void myDisplay(void) {//glEnable(GL_DEPTH_TEST);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);glMatrixMode(GL_PROJECTION);glLoadIdentity();gluPerspective(75, 1, 1, 40…...

掌握NLTK:Python自然语言处理库中级教程

在之前的初级教程中&#xff0c;我们已经了解了NLTK&#xff08;Natural Language Toolkit&#xff09;的基本用法&#xff0c;如进行文本分词、词性标注和停用词移除等。在本篇中级教程中&#xff0c;我们将进一步探索NLTK的更多功能&#xff0c;包括词干提取、词形还原、n-gr…...

Go语言的崛起:探究越来越多公司选择Go语言的原因和优势

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to Golang Language.✨✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1…...

MongoDB 6.0.8 安装配置

一、前言 MongoDB是一个基于分布式文件存储的数据库。由C语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。在高负载的情况下&#xff0c;添加更多的节点&#xff0c;可以保证服务器性能。 MongoDB 将数据存储为一个文档&#xff0c;数据结构由键值(key>value…...

无涯教程-Lua - nested语句函数

Lua编程语言允许在另一个循环中使用一个循环。以下部分显示了一些示例来说明这一概念。 nested loops - 语法 Lua中嵌套for循环语句的语法如下- for init,max/min value, increment dofor init,max/min value, incrementdostatement(s)endstatement(s) end Lua编程语言中的…...

如何使用vue ui创建一个项目?

首先打开cmd 输入vue ui 等待浏览器打开一个窗口&#xff0c;按照下图操作 在"功能页面"中&#xff0c;各个插件代表以下意思&#xff1a; Babel&#xff1a;Babel是一个JavaScript编译器&#xff0c;用于将ES6代码转换为向后兼容的JavaScript版本&#xff0c;以确保…...

STM32——LED内容补充(寄存器点灯及反转的原理)

文章目录 点灯流程开时钟配置IO关灯操作灯反转宏定义最后给自己说 本篇文章使用的是STM32F103xC系列的芯片&#xff0c;四个led灯在PE2,PE3,PE4,PE5上连接 点灯流程 1.开时钟 2.配置IO口 &#xff08;1&#xff09;清零指定寄存器位 &#xff08;2&#xff09;设置模式为推挽输…...

使用Spring Boot和EasyExcel的导入导出

在当今信息化社会&#xff0c;数据的导入和导出在各种业务场景中变得越来越重要。为了满足复杂的导入导出需求&#xff0c;结合Java编程语言、Spring Boot框架以及EasyExcel库&#xff0c;我们可以轻松地构建出强大而灵活的数据处理系统。本文将引导您通过一个案例学习如何使用…...

【H5移动端】常用的移动端方案合集-键盘呼起、全面屏适配、图片大小显示、300ms点击延迟、首屏优化(不定期补充~)

文章目录 前言键盘呼起问题靠近底部的输入项被键盘遮挡底部按钮被顶上去 全面屏适配图片大小显示问题解决300ms延迟首屏优化 前言 这篇文章总结了我在工作中做H5遇到的一些问题&#xff0c;包括我是怎么解决的。可能不是当下的最优解&#xff0c;但是能保证解决问题。 单位适…...

迭代器模式——遍历聚合对象中的元素

1、简介 1.1、概述 在软件开发时&#xff0c;经常需要使用聚合对象来存储一系列数据。聚合对象拥有两个职责&#xff1a;一是存储数据&#xff1b;二是遍历数据。从依赖性来看&#xff0c;前者是聚合对象的基本职责&#xff1b;而后者既是可变化的&#xff0c;又是可分离的。…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析

今天聊的内容&#xff0c;我认为是AI开发里面非常重要的内容。它在AI开发里无处不在&#xff0c;当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗"&#xff0c;或者让翻译模型 "将这段合同翻译成商务日语" 时&#xff0c;输入的这句话就是 Prompt。…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地

借阿里云中企出海大会的东风&#xff0c;以**「云启出海&#xff0c;智联未来&#xff5c;打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办&#xff0c;现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

CMake基础:构建流程详解

目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

现代密码学 | 椭圆曲线密码学—附py代码

Elliptic Curve Cryptography 椭圆曲线密码学&#xff08;ECC&#xff09;是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础&#xff0c;例如椭圆曲线数字签…...

sipsak:SIP瑞士军刀!全参数详细教程!Kali Linux教程!

简介 sipsak 是一个面向会话初始协议 (SIP) 应用程序开发人员和管理员的小型命令行工具。它可以用于对 SIP 应用程序和设备进行一些简单的测试。 sipsak 是一款 SIP 压力和诊断实用程序。它通过 sip-uri 向服务器发送 SIP 请求&#xff0c;并检查收到的响应。它以以下模式之一…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek

文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama&#xff08;有网络的电脑&#xff09;2.2.3 安装Ollama&#xff08;无网络的电脑&#xff09;2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

STM32HAL库USART源代码解析及应用

STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...