SOLID-开闭原则
单一职责原则:https://blog.csdn.net/dmk877/article/details/143447010
在前面我们学习了单一职责原则,今天来一起学习一下SOLID原则中的开闭原则(Open-Closed Principle, OCP)
通过本篇博客你将学到到以下内容
①什么是开闭原则
②如何实现开闭原则
③两个开闭原则的案例
一、什么是开闭原则
首先我们来看下开闭原则的定义:
Software entities like classes,modules and functions should be open for extension but closed for modifications.
软件实体如类、模块和函数应该对扩展开放,对修改关闭。
怎么理解这句话呢?这句话最重要的就是"对扩展开放,对修改关闭"
- 对扩展开放:当有新的需求或变化时,可以对现有代码进行扩展,以适应新的需求
- 对修改关闭:需求一旦开发完成,就可以独立完成其工作,而不要对已有代码做修改
二、如何实现开闭原则
一般用来提高扩展性的方法有:多态、依赖注入、面向抽象而非面向具体编程,怎么利用多态、依赖注入、面向抽象而非具体编程,来实现“对扩展开放、对修改关闭”呢?接下来我举两个例子,相信通过这两个例子你对开闭原则的理解会更加深入。
2.1 举例一
什么依赖注入,什么是面向抽象而非具体的编程
// 将图形进行抽象
public interface Shape {//..}
// 圆形
public class Cirlce implements Shape {//..}
// 三角形
public class Triangle implements Shape {//..}
// 长方形
public class Rectangle implements Shape {//..}public class Demo {private Shape shape; // 基于接口而非实现的编程// 依赖注入public Demo(Shape shape) {this.shape = shape;}
}
以上这段伪代码就是基于接口而非具体编程,将图形共同的属性和方法抽取到Shape接口中,然后针对不同的图形会分别实现接口中的功能。那么问题来了,为什么要这么做呢?来一个完整的例子,通过这个例子你就会明白为什么要这么做
2.2 举例二 电商支付系统
假如我们在开发电商平台的支付系统,当前支付的方式有Alipay、WechatPay,这个支付系统如何设计呢?首先可以定义个PayManager来统一管理支付。代码如下
public class PayManager {public void pay(int payMode) {if (payMode == 1) {aliPay();} else if (payMode == 2) {wechatPay();}}private void aliPay() {System.out.println("调用 alipay 接口");}private void wechatPay() {System.out.println("调用 wechatpay 接口");}
}
可以看到在PayManager的pay方法里根据传递的参数来判断应该使用哪种支付方式,貌似没啥问题,但是随着业务的发展需要增加银行卡支付方式BankCardPay,应该怎么处理呢?需要修改两个地方
- pay方法里增加一个if分支
- 写一个bankcardPay方法
代码如下
public class PayManager {public void pay(int payMode) {if (payMode == 1) {aliPay();} else if (payMode == 2) {wechatPay();} else if (payMode == 3) {// 修改点一:增加一个if分支bankcardPay();}}private void aliPay() {System.out.println("调用 alipay 接口");}private void wechatPay() {System.out.println("调用 wechatpay 接口");}// 修改点二:增加一个bankcardPay方法private void bankcardPay() {System.out.println("调用 bankcardpay 接口");}
}
有没有发现一个问题,PayManager里的pay方法进行了修改,假如后续还有其它支付方式的增加是不是每次都要增加一个if语句呢?这么做有什么弊端呢?
上述修改方式对现有的代码进行了修改,有潜在的危险,因为我们的pay方法在新增支付方式之前已经测试过并上线,你新增了一个支付方式对其进行了修改,是不是还要重新测试一遍呢?
另外这种写法其实违背了开闭原则即“对扩展开放,对修改关闭”,哪里违背了呢?其实就是对扩展支持的不好,新增一种支付方式需要修改核心代码逻辑风险很大。那么我们应该怎么去设计它呢?案例一中可以了解到什么是面向抽象而非具体编程,同样Shape接口一样是不是可以对支付方式进行抽象呢?当然,可以定义一个接口Payment在其中抽象一个pay方法用来完成支付功能,它的代码如下
public interface Payment {public void pay();
}
所有的支付方式都需要实现此接口,当前只支持AliPay和WechatPay这两种支付方式,它们的代码如下
public class AliPay implements Payment {@Overridepublic void pay() {System.out.println("调用 alipay 接口");}
}public class WechatPay implements Payment {@Overridepublic void pay() {System.out.println("调用 WechatPay 接口");}
}
PayManager的代码如下
public class PayManager {// 依赖注入public void pay(Payment payment) {payment.pay();}
}
然后就是如何去调用
public class TestPay {public static void main(String[] args) {Payment wechatPay = new WechatPay();PayManager.pay(wechatPay);}
}
可以看到第三行需要哪种支付方式直接创建其对象并将其传递个PayManager的pay方法即可。此时需要增加一个银行卡支付功能应该如何处理呢?只需要增加一个BankCardPay类并实现Payment接口即可,代码如下
public class BankCardPay implements Payment {@Overridepublic void pay() {System.out.println("调用 BankCardPay 接口");}
}
这样就已经修改完成,调用方法也跟上面一样,代码如下
public class TestPay {public static void main(String[] args) {Payment wechatPay = new BankCardPay();PayManager.pay(wechatPay);}
}
可以看到增加一种BankCardPay支付方式并未对PayManager类做修改,因此对之前已经上线的功能没有影响。后续再增加其它支付方式比如京东支付、抖音支付等等都很容易扩展。
可能有些同学会说,这样修改增加很多个类可读性还没有之前好,确实如此,有些情况下代码的扩展性会跟可读性相冲突,比如上面我们重构代码之后,可读性没有之前的if分支好,所以很多时候我们要在扩展性和可读性之间做权衡,在某些场景下代码的可读性很重要,我们就牺牲一些扩展性,在某些场景下代码的扩展性很重要,我们就牺牲一些可读性。
比如上述的例子,如果项目一开始就说我们的支付方式只支持Alipay和WechatPay这两种支付方式,那刚开始的写法思路简单易读,它就是合理的。相反如果后续要增加很多种支付方式,那么我们重构之后的写法就是比较合理的,因此是否合理没有一个统一的标准,一定要结合业务需求、场景等来进行重构。
三、总结
1.定义
软件实体如类、模块和函数应该对扩展开放,对修改关闭。
开闭原则并不是说完全杜绝修改,而是以最小的修改代码的代码来完成新的功能开发,低层模块的变更,必然要有高层模块进行耦合,否则就是一个独立无意义的代码片段。
2.为什么要使用开闭原则
(1)对于测试来讲,新增的方法不会对已有的方法造成影响,只需要保证新增的类、模块和函数是正确的就可以了
(2)提高可复用性,在面向对象的设计中,所有的逻辑都是从原子逻辑组合而来的,而不是在一个类中独立实现一个业务逻辑。只有这样代码才可以复用,粒度越小,被复用的可能性就越大。
(3)提高可维护性,从上述例子中可以看出来当对已有功能做修改时增加一个类即可,这是不是就是维护人员很乐意干的事,即增加一个类,而不是修改一个类。
3.如何做到"对扩展开放、对修改关闭"
添加一个新的功能,应该是通过在已有代码基础上扩展代码(新增模块、类、方法、属性等),而非修改已有代码(修改模块、类、方法、属性等)的方式来完成。我们要时刻具备扩展意识、抽象意识、封装意识,在写代码的时候要多花点时间思考一下,未来可能的变更,以便在未来需求变更的时候,在不改变代码整体结构的情况下,将新的代码灵活的插入到项目中。
很多设计原则、设计思想、设计模式都是以提到代码的扩展性为最终目的。特别是23中经典设计模式,大部分都是为了解决代码的扩展性而总结出来的,都是以开闭原则为指导原则。最常用提高代码扩展性的方法有:多态、依赖注入、基于接口而非实现编程,以及大部分的设计模式(装饰、策略、模版、责任链、状态)
好了本篇博客就到这里了,后续还会继续更新关于设计原则和设计模式相关的文章,如果觉得对你有用,帮忙点赞回复666
参考书籍:
《设计模式之禅》
《架构整洁之道》
相关文章:
SOLID-开闭原则
单一职责原则:https://blog.csdn.net/dmk877/article/details/143447010 在前面我们学习了单一职责原则,今天来一起学习一下SOLID原则中的开闭原则(Open-Closed Principle, OCP) 通过本篇博客你将学到到以下内容 ①什么是开闭原则 ②如何实现开闭原则 ③…...
Mac 安装 Flutter 提示 A network error occurred while checking
错误信息 A network error occurred while checking "https://maven.google.com/": Operation timed out原因 在中国大陆(由于访问 Google 服务器的限制导致超时),无法连接到 https://maven.google.com/ 解决方案 需要使用镜像网站 #flutter 使用国内的镜像 export …...
Rocky Linux下安装meld
背景介绍: meld是一款Linux系统下的用于 文件夹和文件的比对软件,非常常用; 故障现象: 输入安装命令后,sudo yum install meld,报错。 12-31 22:12:17 ~]$ sudo yum install meld Last metadata expirat…...
Sentinel 介绍与使用指南:构建高可用、可靠的微服务架构
在微服务架构中,服务间的依赖和调用非常复杂,这也带来了高并发、大流量等挑战。 如何确保系统在高负载情况下仍能稳定运行,如何避免某个服务的故障影响整个系统的稳定性?Sentinel,作为一个轻量级的、专为分布式系统设计…...
异步请求在TypeScript网络爬虫中的应用
异步请求的重要性 异步请求是现代网络应用中不可或缺的一部分,特别是在网络爬虫领域。它允许爬虫在等待网络响应的同时继续执行其他任务,从而提高效率和性能。在JavaScript和TypeScript中,异步请求可以通过多种方式实现,包括回调…...
智能商业分析 Quick BI
Quick BI 是阿里云提供的一款智能商业分析(BI)工具,旨在帮助企业快速获取业务洞察、优化决策过程、提升数据分析效率。通过强大的数据可视化和分析功能,Quick BI 能够帮助用户轻松连接多种数据源、创建多维度的报表和仪表盘&#…...
[算法] [leetcode-75] 颜色分类
75 颜色分类 给定一个包含红色、白色和蓝色、共 n 个元素的数组 nums ,原地 对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。 我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。 必须在不使用库内置的 sort 函…...
抖音短视频矩阵系统源码开发技术解析
开发概览: 抖音短视频矩阵系统的构建基于一系列现代技术栈,主要包括VUE, Spring Boot和Django。本文档旨在为开发者提供关于短视频矩阵系统源代码的开发与部署指南。 技术框架分析: 前端技术选型: 对于前端界面的构建…...
Linux(CentOS)安装 MySQL
CentOS版本:CentOS 7 三种安装方式: 一、通过 yum 安装,最简单,一键安装,全程无忧。 二、通过 rpm 包安装,需具备基础概念及常规操作。 三、通过 gz 包安装,需具备配置相关操作。 --------…...
头歌实训数据结构与算法-二叉树及其应用(第9关:二叉树的顺序存储及基本操作)
任务描述 本关任务:以顺序结构存储二叉树,编写前序、中序、后序及层次顺序遍历二叉树的算法,并计算二叉树深度、所有结点总数。 相关知识 二叉树的定义 二叉树的递归定义: 二叉树或者是一棵空树。 或者是一棵由一个根结点和两…...
打印进度条
文章目录 1.Python语言实现(1)黑白色(2)彩色:蓝色 2.C语言实现(1)黑白颜色(2)彩色版:红绿色 1.Python语言实现 (1)黑白色 import sys import timedef progress_bar(percentage, width50):"""打印进度条:param percentage: 当前进度百分比…...
【LLM】Langflow 的简单使用
(PS:爆肝整理,请不要吝啬你的点赞和收藏。) 什么是 Langflow ?Langflow 是一种用于构建多智能体和RAG应用的可视化框架。它提供了个无需编码的 AI 生态系统,能够无缝集成各种常用工具和技术栈。Langflow 以 Python 为基础&#x…...
探索 DC-SDK:强大的 3D 地图开发框架
在现代 Web 开发中,地理信息系统(GIS)和 3D 地图可视化变得越来越重要。dc-sdk 是一个基于 Cesium 的开源 WebGL 地图开发框架,它提供了丰富的地图可视化功能和简单易用的 API,使开发者能够轻松地在 Web 应用中集成 3D…...
3.5mm耳机接口硬件连接
结构 以最复杂的结构为例 简单的结构无非就是没有MIC(麦克风)接口 上图的5就是Detect的作用 上面这两款产品都为3.5mm的音频插座,图一 为连接4节的音频座,而且有两个开关,1接地,2接MIC,3接左声…...
nvidia_gpu_exporter 显卡监控
导入 grafana/dashboard.json https://github.com/utkuozdemir/nvidia_gpu_exporter/blob/master/grafana/dashboard.json参考 nvidia_gpu_exporter...
聊聊 Mongod 以及 MongoDB 常用命令
Mongod mongod 是 MongoDB 数据库服务器的核心守护进程,它负责启动并管理 MongoDB 数据库实例。简单来说,mongod 是 MongoDB 数据库服务器程序,它负责处理数据存储、数据请求、数据复制等后台服务。运行 mongod 是启动 MongoDB 数据库的第一…...
webrtc 源码阅读 make_ref_counted模板函数用法
目录 1. 模板参数解析 1.1 typename T 1.2 typename... Args 1.3 typename std::enable_if::value, T>::type* nullptr 2. scoped_refptr 3. new RefCountedObject(std::forward(args)...); 4. 综合说明 5.在webrtc中的用法 5.1 peerConnectionFactory对象的构建过…...
僵尸进程,孤儿进程、守护进程以及wait函数,waitpid函数
僵尸进程 如果子进程退出,但是父进程没有调用 wait 或 waitpid 获取子进程的状态信息,那么子进程的进程描述符(task_struct)仍然保存在系统中,那么该子进程叫做僵尸进程 #include<iostream> #include<pthre…...
Kafka消息不丢失与重复消费问题解决方案总结
1. 生产者层面 异步发送与回调处理 异步发送方式:生产者一般使用异步方式发送消息,异步发送有消息和回调接口两个参数。在回调接口的重写方法中,可通过异常参数判断消息发送状态。若消息发送成功,异常参数为null;若发…...
Docker新手:在tencent云上实现Python服务打包到容器
1 使用docker的原因 一致性和可移植性:Docker 容器可以在任何支持 Docker 的环境中运行,无论是开发者的笔记本电脑、测试服务器还是生产环境。这确保了应用在不同环境中的行为一致,减少了“在我的机器上可以运行”的问题。 隔离性ÿ…...
什么是 Spring 的组件(Bean)
什么是 Spring 的组件(Bean)? Spring 会自动创建、初始化、装配和销毁这些对象。Spring 使用 IoC(控制反转) 和 DI(依赖注入) 的理念,将应用程序的对象交给 Spring 容器统一管理&am…...
PawSQL性能巡检平台 (3) - 慢查询采集和优化
在数据库运维管理中,慢查询一直是影响系统性能的重要因素。本文将详细介绍PawSQL数据库性能巡检平台在慢查询管理和优化方面的功能特性,帮助数据库管理员更好地应对性能挑战。 一、PawSQL巡检平台慢查询管理概述 PawSQL平台提供了全面的慢查询管理功能&…...
虚拟机Centos下安装Mysql完整过程(图文详解)
目录 一. 准备工作 1. 设置虚拟机静态IP 2. 卸载Mysql 3. 给CentOS添加rpm源 二. 安装MySQL 1. 安装mysql服务 2. 启动mysql服务 3. 开启MySQL开机自启动 4. 查看mysql服务状态 5. 查看mysql初始密码 6. 登录mysql ,修改密码 7. 允许外部访问MySQL数据库…...
微服务保护-sentinel
为什么要有微服务保护? 微服务保护是为了避免微服务雪崩而出现的,每个微服务能处理的请求是有限的,如果一个微服务出现问题导致一个请求进入微服务的时间太久,就会导致大量去请求停滞在微服务内部,这样就会过分占用系统…...
Redis Java 集成到 Spring Boot
Hi~!这里是奋斗的明志,很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~~ 🌱🌱个人主页:奋斗的明志 🌱🌱所属专栏:Redis 📚本系列文章为个人学习笔…...
RabbitMQ实现生产者消费者
一.启动MQ 注意管理员身份进入cmd才行,我这里是在本地安装的MQ,推荐使用虚拟机安装 二.思路 官方解释RabbitMQ结构: 自我理解RabbitMQ结构: 其实RabbitMQ的服务器就像邮局一样,我们的生产者和消费者对于这个服务器来说都是消费者,因为服务器都可以向两者发送消息 环境准备 …...
stm32f103zet6 ds18b20
main.c // main.c #include "sys.h" #include "ds18b20.h"int main(void){ uart_init(9600);delay_init();while(DS18B20_Init()) //DS18B20初始化 {printf("error");delay_ms(200);}while(1){printf("%4.2f\r\n",Get_Temp());}}ds18…...
期权懂|期权入门知识:开通50ETF期权需要什么条件?
锦鲤三三每日分享期权知识,帮助期权新手及时有效地掌握即市趋势与新资讯! 开通50ETF期权需要什么条件? 一、基本资格要求 (1)年龄限制:投资者必须年满18周岁,具备完全民事行为能力。 &#…...
Linux day 1129
家人们今天继续学习Linux,ok话不多说一起去看看吧 三.Linux常用命令 3.1 Linux命令体验 3.1.1 常用命令演示 在这一部分中,我们主要介绍几个常用的命令,让大家快速感 受以下 Linux 指令的操作方式。主要包含以下几个指令: ls命…...
智能家居体验大变革 博联 AI 方案让智能不再繁琐
1. 全球AI技术发展背景及智能家居市场趋势 人工智能(AI)技术的飞速发展正在推动全球各行业的数字化转型。国际电信联盟与德勤联合发布《人工智能向善影响》报告指出,全球94%的商界领袖认为,人工智能技术对于其企业在未来5年内的发…...
git使用
git初始化 git init 指定要添加的文件 git add [文件名1] [文件名2] [文件名3] // 添加指定文件 git add . // 添加当前目录所有文件 将文件提交到本地仓库 git commit -m "备注信息" 添加远程仓库 git remote add origin [远程仓库地址] git remote -v // …...
嵌入科技的温情
嵌入式世界,是一个微小却无比精妙的宇宙。晶体管之间的脉动,仿佛是心跳的回响;代码中跳跃的逻辑,犹如人生中不可预知的转折。每一个嵌入式系统,都像是一个看不见的灵魂,将冰冷的机器唤醒,为生活…...
python利用selenium实现大麦网抢票
大麦网(damai.cn)是中国领先的现场娱乐票务平台,涵盖演唱会、音乐会、话剧、歌剧、体育赛事等多种门票销售。由于其平台上经常会有热门演出,抢票成为许多用户关注的焦点。然而,由于票务资源的有限性,以及大…...
PS等软件学习笔记
目录 一、ps基础操作快捷键 1、快速打开图片 2、屏幕画布变大变小 3、移动画布 4、CTRL回车,快速完成更改 5、还原 6、创建画布,CTRLN 7、复制图层,CTRLJ 8、一段文字行间距调整 9、反向选择,CTRLSHIFTI 10、抠图 二、…...
vue3学习笔记(9)-pinia、storeToRefs、getters
1.新的集中式状态(数据)管理库,redux vuex pinia 搭建 2.ref拆包 如果在reactive里面定义ref,则打印c时,无需.value 他自动拆包,如果直接在外面定义的ref则需要.value,他没有拆包 3.pinia存储读取数据 存…...
数据库基础知识---以MySQL为例
一、什么是MySQL 数据保存在不同的表中,而不是将所有数据放在一个大仓库内 二、特点 开源--免费下载跨平台--可以在多个操作系统进行运行性能好--可以出来大量数据简单--安装配置简单支持多种编程语言--可以与多种编程语言进行无缝集成 三、分类 DDL--数据定义…...
013-spring的注解整合第三方框架
给spring的ioc容器中添加对象 常用这3个方法...
使用ForceBindIP绑定应用到指定IP
前言 使用ForceBindIP工具,用户可以轻松地将特定应用程序绑定到指定的IP地址,从而确保应用程序的网络连接通过指定的网络适配器进行。通过在命令提示符下运行ForceBindIP并指定IP地址和应用程序的完整路径,用户能够控制应用程序的网络流量&a…...
python-LeetCode-两数之和
1. 两数之和 - 力扣(LeetCode) class Solution:def twoSum(self, nums: List[int], target: int) -> List[int]:# 创建一个哈希表用于存储值和索引num_to_index {}for i, num in enumerate(nums):# 计算目标值需要的补数complement target - num# 如…...
项目开发实践——基于SpringBoot+Vue3实现的在线考试系统(四)
文章目录 一、管理员角色功能实现1、添加教师功能实现1.1 页面设计1.2 前端功能实现1.3 后端功能实现1.4 效果展示2、教师管理功能实现2.1 页面设计2.2 前端功能实现2.3 后端功能实现2.3.1 后端查询接口实现2.3.2 后端编辑接口实现2.3.3 后端删除接口实现2.4 效果展示二、代码下…...
大语言模型的token和向量
现在大语言模型火了,像 ChatGPT 什么的,能回答问题、写文章,。但它们为啥这么聪明呢?这就和向量、Token 有关系。那怎么通过向量、Token来理解我们的问题呢。看完这篇文章就知道了 token Token 就像是语言里的小积木,…...
Hyperledger Fabric有那些核心技术,和其他区块链对比Hyperledger Fabric有那些优势
Hyperledger Fabric是一个模块化、权限化的企业级区块链平台,与比特币、以太坊等公有链相比,Fabric主要为私有链或联盟链设计,适用于企业应用。它包含多项核心技术,使其在企业级区块链应用中具有独特优势。以下是Fabric的核心技术…...
ThinkPHP 8高效构建Web应用-第一个简单的MVC应用示例
【图书介绍】《ThinkPHP 8高效构建Web应用》-CSDN博客 《2025新书 ThinkPHP 8高效构建Web应用 编程与应用开发丛书 夏磊 清华大学出版社教材书籍 9787302678236 ThinkPHP 8高效构建Web应用》【摘要 书评 试读】- 京东图书 使用VS Code开发ThinkPHP项目-CSDN博客 我们先实现一…...
【Goland】怎么执行 go mod download
1、终端的执行 go mod tidy 2、终端执行不行的话,就可以通过右击go.mod文件来执行; 3、也可以按住Ctrl点击这个包安装;...
SpringBoot入门案例
目录 一、SpringBoot入门 1. Spring Boot 简介(脚手架) 2. 微服务 微服务优点: 微服务缺点: 3. 环境准备 3.1 spring boot项目的创建 3.2 导入spring boot相关的依赖 3.5 编写主程序 3.4 编写相关的controller、service…...
自定义 Celery的logging模块
为什么需要自定义 Celery 日志 默认的 Celery 日志配置虽然满足基本需求,但在以下情况下可能需要进行自定义: 支持日志滚动:原生celery不支持日志滚动。更详细的日志信息:需要包含更多上下文信息,以便更好地理解任务…...
docker-开源nocodb,使用已有数据库
使用已有数据库 创建本地数据库 数据库:nocodb 用户:nocodb 密码:xxxxxx修改docker-compose.yml 默认网关的 IP 地址是 172.17.0.1(适用于 bridge 网络模式)version: "2.1" services:nocodb:environment:…...
【UE5】UnrealEngine源码构建1:tag为5.3.2源码clone
fatal: fetch-pack: invalid index-pack output clone unreal 速度很快,但是很容易失败Cloning into UnrealEngine... remote: Enumerating objects: 6511087, done. remote: Counting objects: 100% (196/196), done. remote: Compressing objects: 100% (50/50), done. erro…...
RoboMIND:多体现基准 机器人操纵的智能规范数据
我们介绍了 RoboMIND,这是机器人操纵的多体现智能规范数据的基准,包括 4 个实施例、279 个不同任务和 61 个不同对象类别的 55k 真实世界演示轨迹。 工业机器人企业 埃斯顿自动化 | 埃夫特机器人 | 节卡机器人 | 珞石机器人 | 法奥机器人 | 非夕科技 | C…...
FPGA自学之路:到底有多崎岖?
FPGA,即现场可编程门阵列,被誉为硬件世界的“瑞士军刀”,其灵活性和可编程性让无数开发者为之倾倒。但谈及FPGA的学习难度,不少人望而却步。那么,FPGA自学之路到底有多崎岖呢? 几座大山那么高?…...