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

七大设计原则之开闭原则

目录

一、什么是开闭原则?

二、如何做到开闭原则?

1、面向接口或抽象类编程

2、依赖注入

3、单一职责原则

三、是不是为了满足开闭原则就要一味的追求代码的扩展性?


一、什么是开闭原则?

       相信很多人都听说过这个原则,即使没有直接了解过“开闭原则”,也一定听过“对扩展开放,对修改关闭”这句话。这句话简洁地概括了开闭原则的核心思想。也即是,添加一个新的功能,应该是通过在已有代码基础上扩展代码(新增模块、类、方法、属性等),而非修改已有代码(修改模块、类、方法、属性等)的方式来完成

        开闭原则(Open Closed Principle, OCP),它的英文表述是:software entities (modules, classes, functions, etc.) should be open for extension but closed for modification。翻译成中文,意思就是:软件实体(如模块、类、方法等)应当对扩展开放,而对于修改则应保持关闭。这也就是我们上面说的“对扩展开发,对修改关闭”。我们应当使系统更容易通过添加新代码来扩展其功能,而不是通过修改现有的代码来实现变化。这样不仅能够提高系统的灵活性和可维护性,还能有效减少因频繁更改原有代码导致引入错误的风险。  

二、如何做到开闭原则?

1、面向接口或抽象类编程

        面向接口或抽象类编程,这意味着我们应该尽量使用接口或抽象类,而不是具体的实现类。这样做的好处在于,我们可以在不修改现有代码的情况下,通过添加新的实现类来扩展新功能。此外,当我们需要替换接口方法的具体实现逻辑时,只需替换接口对应的引用对象即可,不需要修改引用和使用引用的地方。这样便提高了代码的可扩展性,并且在需要修改代码的时候,也可以做到尽可能少的修改。

       假设我们有一个支付系统,需要支持多种支付方式(如信用卡支付、微信支付等)。我们可以定义一个支付接口 Payment,然后为每种支付方式创建具体的实现类。

      首先,定义一个支付接口:

//支付接口
public interface Payment {void pay(double amount);
}

      其次,为不同的支付方式创建具体的实现类:

// 信用卡支付
public class CreditCardPayment implements Payment {@Overridepublic void pay(double amount) {System.out.println("Paid " + amount + " using Credit Card.");// 实际的信用卡支付逻辑}
}// 微信支付
public class WeChatPayment implements Payment {@Overridepublic void pay(double amount) {System.out.println("Paid " + amount + " using WeChat.");// 实际的WeChat支付逻辑}
}

        最后,我们可以编写一个方法来处理支付,而无需关心具体的支付方式。

// 支付类
public class PaymentProcessor {private Payment payment;public PaymentProcessor(Payment payment) {this.payment = payment;}public void processPayment(double amount) {payment.pay(amount);}
}

      那么,此时,我们在客户端代码的时候,就可以灵活的选择不同的支付方式。


public class Main {public static void main(String[] args) {//根据指向的具体实现选择对应的支付方式,比如此时我们想要改成微信支付,就把//CreditCardPayment换成WeChatPaymentPayment payment = new CreditCardPayment();  PaymentProcessor processor = new PaymentProcessor(payment);processor.processPayment(100.0);}
}

如果说此时,我们又需要支持支付宝支付,则此时,只需创建一个新的实现类ALiPayment并实现 Payment接口,并更改Payment接口引用指向的对象为ALiPayment对象即可。 

      那么,此时,肯定会有人说,这不是还是修改了原有代码吗?这不也是不满足开闭原则吗?

     就像有句话说的好,水至清则无鱼,人至察则无徒。如果我们总是用放大镜来审视代码,那么很难找到完全满足开闭原则(OCP)的代码。让我们重新审视OCP的定义:软件实体(模块、类、函数等)应该对扩展开放,但对修改关闭。如果站在一个模块的角度来看,我们在这个模块内所做的所有修改似乎都违背了OCP。因此,判断是否满足开闭原则也需要看我们是从哪个角度来看待这个问题。

       并且,我们也应该认识到添加一个新功能时,不可能完全不修改任何模块、类或方法的代码。类的创建、组装和初始化操作是构建可运行程序所必需的,这部分代码的修改是不可避免的。然而,我们的目标是尽量将修改操作集中化、减少修改的范围,并使其更上层化。这也就是说我们应该尽量让最核心、最复杂的那部分逻辑代码满足OCP。

2、依赖注入

       提到依赖注入大家应该也都不陌生。因为,我们在学习spring框架的时候都会接触这个概念。依赖注入(Dependency Injection,简称DI)是一种设计模式,用于实现控制反转(Inversion of Control,简称IoC)。在传统的编程中,对象的创建和管理通常由对象自身负责,这会导致代码的耦合度较高,难以测试和维护。依赖注入通过将对象的依赖关系从对象内部转移到外部容器或框架来管理,从而降低了耦合度,提高了代码的灵活性和可维护性。

        在我们上面举的示例中,PaymentProcessor 类中的支付方式接口 Payment 的具体实现便是通过DI来创建的,而不是在 PaymentProcessor 类内部自行创建。这正是依赖注入的核心所在:它允许我们灵活地控制对象的创建过程。这种设计不仅增强了代码的扩展性和灵活性,还使得我们的代码能够更好地遵循OCP。通过依赖注入,我们可以在不修改现有代码的情况下,轻松地引入新的支付方式实现,从而提升系统的适应性和可维护性。

3、单一职责原则

       其实这个不难理解。当我们编写代码时,应尽量使每个类或方法都遵循单一职责原则。这样做的好处是,当需求发生变化时,我们只需修改尽可能少的代码,从而更容易满足OCP。其实,这也说明了很多设计原则、思想、模式都是相通的。其实,为了更好地遵循开闭原则,我们需要深入学习更多的设计模式思想。这些设计模式的核心理念正是在于实现对扩展开放,对修改关闭的原则。

三、是不是为了满足开闭原则就要一味的追求代码的扩展性?

        显然不是!还是那句话,过犹不及。在我们为了OCP,一味的追求扩展性的时候,一定会使代码失去可读性,而可读性往往就会影响代码的可维护性。所以说,我们需要在可读性和可扩展性之间做一个平衡。在某些场景下,如果代码的扩展性至关重要,我们就可以适度牺牲一些可读性;而在另一些场景下,如果代码的可读性更为关键,我们则可以适当地牺牲一些扩展性。所以说,任何技术都应该在具体场景中进行讨论,脱离了实际应用场景来谈论技术是没有任何意义的!

相关文章:

七大设计原则之开闭原则

目录 一、什么是开闭原则? 二、如何做到开闭原则? 1、面向接口或抽象类编程 2、依赖注入 3、单一职责原则 三、是不是为了满足开闭原则就要一味的追求代码的扩展性? 一、什么是开闭原则? 相信很多人都听说过这个原则&#x…...

【stm32+K210项目】基于K210与STM32协同工作的智能垃圾分类系统设计与实现(完整工程资料源码)

视频效果演示: 基于K210与STM32协同工作的智能垃圾分类系统设计与实现 目录: 目录 视频效果演示: 目录: 项目简介: 一、设计目的: 1.1 项目背景 1.2 设计意义: 二、硬件部分: 2.1 st…...

Ps:创建数据驱动的图像

在设计实践中,常常需要处理大量内容变化但设计格式统一的任务,例如批量生成名片、工作证、学生证、胸牌、奖状或证书甚至图册。这些工作如果逐一手动制作,不仅耗时费力,还容易出错。 为解决这一问题,Photoshop 提供了强…...

git的全通路线介绍

一、关系 1.git是代码版本管理工具,即可将项目切换到任意版本。 2.github与gitee是基于git技术构建的远程仓库网站。github是国外建立的,资源更丰富;gitee是国内建立的,免费功能更多。 3.gitlab与github类似,只不过…...

R1-3学习打卡

🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 RNN心脏病识别 导入数据数据预处理标准化模型训练模型评估个人总结 import tensorflow as tfgpus tf.config.list_physical_devices("GPU")…...

Vue.js组件开发-实现无感刷新Token

在Vue.js应用中,实现无感刷新Token涉及到在用户的会话Token即将过期或已经过期时自动获取新的Token,而不影响用户的操作体验。需要通过拦截器(interceptors)来处理API请求,并在检测到Token过期或无效时自动进行刷新。 …...

可编辑31页PPT | 大数据湖仓一体解决方案

荐言分享:在当今数字化时代,大数据已成为企业决策和业务优化的关键驱动力。然而,传统的数据处理架构,如数据仓库和数据湖,各自存在局限性,难以满足企业对数据高效存储、灵活处理及实时分析的综合需求。因此…...

如何实现一个充满科技感的官网(二)

背景 在上一篇文章 《如何实现一个充满科技感的官网(一)》 中,我们初步了解了该官网的整体设计,并与大家探讨了它的视觉呈现和用户体验。 我们前期的内部设计偏向简洁,所以开始思考如何提升网站的整体设计感。这些尝…...

深度学习算法:从基础到实践

简介 深度学习作为人工智能领域的一个重要分支,近年来在多个领域取得了显著的成就。本文将从基础概念出发,探讨深度学习算法的核心原理,并介绍一些实际应用案例。 深度学习算法的核心概念 深度学习算法基于人工神经网络,通过构…...

等价和划分

例子:学生分组 假设我们有一个班级,班级里有10名学生,我们想要根据他们的年龄来分组。我们可以定义一个关系 ( R ) 在学生集合 ( A ) 上,其中 ( A {s_1, s_2, …, s_{10}} ),并且 ( s_i ) 和 ( s_j ) 之间有关系 ( R…...

电商项目-数据同步解决方案(三)商品上架同步更新ES索引库

一、 需求分析和业务逻辑 主要应用技术有:Feign远程调用, 消息队列-RabbitMQ ,分布式搜索引擎-ElasticSearch,Eureka,Canal 商品上架将商品的sku列表导入或者更新索引库。 数据监控微服务需要定义canal监听器&#x…...

MySQL数据库笔记——多版本并发控制MVCC

大家好,这里是Good Note,关注 公主号:Goodnote,本文详细介绍MySQL的并发控制:多版本并发控制MVCC。 文章目录 背景介绍数据库并发控制——锁机制悲观锁和乐观锁悲观锁乐观锁 数据库并发控制——MVCC 的引入MVCC 和锁机…...

【LLM综述】29种大模型Prompt Engineering技术

note 从零样本(Zero-shot)提示到最新进展的各种提示技术,包括推理和逻辑链(Chain-of-Thought, CoT)提示、自动链式思考(Auto-CoT)提示、自我一致性(Self-Consistency)提…...

MySQL高级关联查询与复杂关系的处理

在关系型数据库的操作中,复杂关联查询和多层次关系建模是非常重要的技能。无论是在数据分析、业务数据处理,还是在数据可视化等各个方面,处理多表数据关联都是不可或缺的部分。通过高效的关联查询,可以在不同表之间建立关系,查询并整合多张表的数据,避免数据冗余并提升查…...

URL Moniker API

1. urlmon 介绍 urlmon 是指 URL Moniker API,它是 Microsoft Windows 操作系统中的一部分,通常用于处理 URL 和相关的任务。urlmon.dll 是其动态链接库,提供了一系列函数和接口,主要用于以下目的: URL 分析和处理&a…...

单元测试3.0+ @RunWith(JMockit.class)+mock+Expectations

Jmockit使用笔记_基本功能使用Tested_Injectable_Mocked_Expectations_jmockit.class-CSDN博客 测试框架Jmockit集合junit使用 RunWith(JMockit.class) 写在测试案例类上的注解 Tested 在测试案例中,写在我们要测试的类上面, 一般用实现类 Injectable 在测试案例中声明…...

halcon中图像滤波分为空间域和频域两种方法

均值滤波是一种线性平滑滤波。基本思想是用某像素邻域几个像素的平均值代替此像素原来的灰度值。 高斯滤波是用某像素邻域几个像素的加权平均值代替此像素的原有灰度值。 总结:图像滤波分为空间域和频域两种方法。 空间域滤波主要是对像素的直接处理,它将…...

maxminddb地理信息库–C语言

原文地址:maxminddb地理信息库–C语言 – 无敌牛 欢迎参观我的个人博客:无敌牛 – 技术/著作/典籍/分享等 maxminddb 是一个 IP 的地理信息库,可以根据 IP 地址给出对应的地理位置信息。 下载离线库 maxminddb提供在线查询,也…...

Keil中的gcc

文章目录 一、IDE背后的命令1.1 IDE是什么1.2 IDE的背后是命令1.3 有两套主要的编译器 二、准备工作2.1 arm-linux-gcc和gcc是类似的2.2 Code::Blocks2.2.1 设置windows环境变量2.2.2 命令行示例 三、gcc编译过程详解3.1 程序编译4步骤3.2 gcc的使用方法3.2.1 gcc使用示例3.2.2…...

【Java数据结构】栈和队列相关算法

第一题:改变元素的序列 例1:若进栈序列为1,2,3,4,进栈过程中可以出栈,则下列不可能一个出栈序列(); A:1,4,3&#xff0c…...

LoRA微调系列笔记

系列文章目录 第一章:LoRA微调系列笔记 第二章:Llama系列关键知识总结 第三章:LLaVA模型讲解与总结 文章目录 系列文章目录LoRA:Low-Rank Adaptation of Large Language Models目的:依据:优势:…...

Linux(Ubuntu)下ESP-IDF下载与安装完整流程(3)

接前一篇文章:Linux(Ubuntu)下ESP-IDF下载与安装完整流程(2) 本文主要看参考官网说明,如下: 快速入门 - ESP32-S3 - — ESP-IDF 编程指南 latest 文档 Linux 和 macOS 平台工具链的标准设置 - ESP32-S3 - — ESP-IDF 编程指南 latest 文档 一、安装准备 1. Linux用...

【C++】22___STL常用算法

目录 一、常用遍历算法 二、常用查找算法 2.1 find 2.2 其它查找算法 三、常用排序算法 3.1 sort 3.2 其它排序算法 四、拷贝 & 替换 4.1 copy 4.2 其它算法 五、常用的算数生成算法 5.1 accumulate 5.2 fill 六、常用集合算法 6.1 set_intersection 6…...

linux 批量替换文件指定字符串

启发:数据库连接串的用户名需要从sa修改为sasa find . -type f -name mssql.json -exec sed -i s/IDsa;/IDsasa;/g {}...

List接口(源码阅读)

文章目录 1.List接口常用方法1.代码2.结果 2.ArrayList底层机制1.结论2.ArrayList底层源码1.代码2.debug添加第一个元素1.进入2.elementData数组存储ArrayList的数据3.初始化为空数组4.首先确保使用size1来计算最小容量5.如果elementData为空,最小容量就是106.modCo…...

股市学习 seekingalpha tradingview

EMA EMA(Exponential Moving Average)是一种技术分析中常用的指标,用于平滑股价或其他资产价格的波动,以帮助分析价格走势的趋势和方向。EMA与简单移动平均(SMA)类似,但对最新价格的权重更大&a…...

用再生龙备份和还原操作系统(三)

续上篇《用再生龙备份和还原操作系统(二)》 三,用再生龙将镜像文件还原到硬盘 将再生龙工具盘、待还原系统的硬盘(与源盘一样大或更大)、镜像文件所在磁盘(如果是U盘,也可以后插)安…...

FaceFusion 从0开始本地部署,RTX4060

FaceFusion 从0开始本地部署指南 一、环境准备 1. 基础工具安装 1.1 Git 安装 使用管理员权限打开 PowerShell执行安装命令: winget install -e --id Git.Git验证安装: git --version1.2 FFmpeg 安装 使用管理员权限打开 PowerShell执行安装命令&…...

Swift Combine 学习(六):自定义 Publisher 和 Subscriber

Swift Combine 学习(一):Combine 初印象Swift Combine 学习(二):发布者 PublisherSwift Combine 学习(三):Subscription和 SubscriberSwift Combine 学习(四&…...

服务器网卡绑定mode和交换机的对应关系

互联网各领域资料分享专区(不定期更新): Sheet 模式类别 网卡绑定mode共有七种(0~6): bond0、bond1、bond2、bond3、bond4、bond5、bond6 mode详解 mode0 ,即:(balance-rr) Round-robin policy(平衡轮循环策略,需要配置交换机静态聚合) mode…...

【动手学轨迹预测】2.4 考虑地图拓扑关系的表征方法

上一节我们介绍了VectorNet提出了矢量化场景表征方法, 大幅提高了预测网络编码性能. 但是VectorNet对地图数据的编码是基于无向无权图的, 并没有考虑到地图的拓扑关系. 显然在预测中, 地图的拓扑关系应该被考虑到. 于是在VectorNet的基础上, LaneGCN提出一种将地图车道作为节点…...

ChatGLM3模型搭建(踩坑记录版)

参考 魔搭社区 https://zhuanlan.zhihu.com/p/720148240 智谱AI通用大模型:本地部署ChatGLM3-6B开源大模型 - 编程库 说明 搭建方式多篇文章结合着看; 模型下载强烈推荐魔塔社区下载ZhipuAI/chatglm3-6b; 官方github指定清华的模型没有…...

基于 Python Django 的花卉商城系统的研究与实现

博主介绍:✌程序员徐师兄、7年大厂程序员经历。全网粉丝12w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇…...

Spring Boot 3 文件下载、多文件下载以及大文件分片下载、文件流处理、批量操作 和 分片技术

在 Spring Boot 3 中,实现文件下载、多文件下载以及大文件分片下载需要结合以下功能:文件流处理、批量操作 和 分片技术。以下是详细实现方案: 1. 单文件下载 基础的单文件下载实现,可以参考以下代码: GetMapping(&…...

什么是事件循环(Event Loop)?请谈谈它在 JavaScript 中的作用?

事件循环(Event Loop)是什么? 事件循环(Event Loop)是JavaScript运行时环境(如浏览器或Node.js)中的一个核心机制,用于处理异步操作和事件。 它负责协调代码的执行、事件的处理、以…...

Lua : Coroutine(协程)

Lua 协程(coroutines)是一种强大的控制结构,允许函数在执行过程中暂停并在稍后恢复。与线程不同,协程是非抢占式的,这意味着它们不会被操作系统调度,而是由程序显式地切换。协程在 Lua 中非常有用&#xff…...

【2024华为OD-E卷-200分-跳格子2】(题目+思路+JavaC++Python解析)

题目描述 在一个二维平面上,有一个 n x m 的网格,每个格子有一个非负整数。你从左上角 (0, 0) 开始,每次只能向右或向下移动,目标是到达右下角 (n-1, m-1)。 在移动过程中,你需要记录经过的格子中,最大数…...

【仓颉语言基础】语言概念、环境配置与语法解析

华为仓颉语言是一门专为分布式系统设计的现代编程语言,以简洁的语法和强大的分布式能力为核心,提供高效的资源管理和任务调度方案。本篇文章将带您从概念入手,逐步掌握环境配置与语法基础,为分布式开发奠定坚实基础。 文章目录 一…...

LeetCode - 初级算法 数组(删除排序数组中的重复项)

免责声明:本文来源于个人知识与公开资料,仅用于学术交流。 删除排序数组中的重复项 这篇文章讨论如何从一个非严格递增的数组 nums 中删除重复的元素,使每个元素只出现一次,并返回新数组的长度。因为数组是排序的,只要是相同的肯定是挨着的,所以我们需要遍历所有数组,然…...

SpringMVC进阶(自定义拦截器以及异常处理)

文章目录 1.自定义拦截器 1.基本介绍 1.说明2.自定义拦截器的三个方法3.流程图 2.快速入门 1.Myinterceptor01.java2.FurnHandler.java3.springDispatcherServlet-servlet.xml配置拦截器4.单元测试 3.拦截特定路径 1.拦截指定路径2.通配符配置路径 4.细节说明5.多个拦截器 1.执…...

2 秒杀系统架构

第一步 思考面临的问题和业务场景 秒杀系统面临的问题: 短时间内并发非常高,如果按照秒杀的并发做相应的承载会造成大量资源的浪费。第二解决超卖的问题。 第二步 思考目前的处境和解决方案 因为秒杀系统属于短时间内的高并发问题,我们不可能使用那么…...

C++如何遍历数组vector

在C中&#xff0c;vector是一个可变数组。那么怎么遍历它呢&#xff1f;我们以for循环为例&#xff08;while循环&#xff0c;大家自己脑补&#xff09;。 方法一&#xff1a; 基于范围的for循环&#xff0c;这是C11新引入的。 std::vector<int> v {1, 2, 3, 4, 5, 6…...

ubuntu非root用户操作root权限问题-virbox挂在共享文件夹

首先讲一下&#xff0c;virtuallbox 挂在文件夹&#xff0c;操作的时候总是需要root权限&#xff0c;比较费劲。 这一操作其实也正对着我们在Ubuntu上的操作。 前段时间我想在ubuntu正常用户下去操作i2c&#xff0c;也出现了类似的问题。 后来把正常的操作加到组里面也解决了类…...

大模型推理:vllm多机多卡分布式本地部署

文章目录 1、vLLM分布式部署 docker镜像构建通信环境配置 2、其他大模型部署工具3、问题记录参考文献 单台机器GPU资源不足以执行推理任务时&#xff0c;一个方法是模型蒸馏量化&#xff0c;结果就是会牺牲些效果。另一种方式是采用多台机器多个GPU进行推理&#xff0c;资源不…...

WFP Listbox绑定数据后,数据变化的刷新

Listbox绑定数据通过ItemsSource来的&#xff0c;如果绑定的是普通的List<数据>&#xff0c;不会自己刷新。 使用ObservableCollection集合 解决问题的方法: 将数组替换为 ObservableCollection ObservableCollection 是专为绑定设计的集合类型&#xff0c;可以通知 W…...

AI + 爬虫:智能化数据采集的未来

随着人工智能&#xff08;AI&#xff09;技术的不断进步&#xff0c;传统的网络爬虫正经历一场前所未有的变革。从规则驱动到智能化演变&#xff0c;AI 的引入不仅提高了爬虫的效率和适应性&#xff0c;更为大规模数据采集提供了全新思路。本文将深入探讨 AI 与爬虫的结合&…...

人工智能知识分享第五天-正则化.损失函数案例

正则化 欠拟合与过拟合 过拟合&#xff1a;一个假设 在训练数据上能够获得比其他假设更好的拟合&#xff0c; 但是在测试数据集上却不能很好地拟合数据 (体现在准确率下降)&#xff0c;此时认为这个假设出现了过拟合的现象。(模型过于复杂) 欠拟合&#xff1a;一个假设 在训…...

WebRTC的线程事件处理

1. 不同平台下处理事件的API&#xff1a; Linux系统下&#xff0c;处理事件的API是epoll或者select&#xff1b;Windows系统下&#xff0c;处理事件的API是WSAEventSelect&#xff0c;完全端口&#xff1b;Mac系统下&#xff0c;kqueue 2. WebRTC下的事件处理类&#xff1a; …...

C++软件设计模式之迭代器模式

迭代器模式是一种行为设计模式&#xff0c;它允许你顺序访问一个聚合对象的元素&#xff0c;而不暴露其底层表示。在C软件设计中&#xff0c;迭代器模式的主要目的是将数据的遍历行为与数据结构本身分离&#xff0c;使得数据结构的修改不会影响到遍历代码。 目的和意图 解耦遍…...

git reset --hard(重置到当前提交,所有未提交的更改都会被永久丢弃)

git reset --hard 是一个强大的命令&#xff0c;它会将你的工作目录、暂存区和当前分支的 HEAD 指针重置到指定的提交状态&#xff0c;所有未提交的更改都会被永久丢弃。因此&#xff0c;使用这个命令时需要非常小心。 基本用法 重置到当前提交&#xff08;丢弃所有未提交的更…...