软件设计原则之开闭原则

news2024/9/20 1:03:22

开闭原则(Open-Closed Principle, OCP)是软件设计中的一个重要原则,由伯特兰·梅耶(Bertrand Meyer)在1988年提出。该原则强调软件实体(如类、模块、函数等)应该对扩展开放,对修改关闭。这意味着当软件需要变化时,我们应该通过扩展已有软件实体的功能来实现新的需求,而不是通过修改已有代码来完成。以下是开闭原则的详细解析:

目录

  • 开闭原则的定义
  • 开闭原则的好处
  • 开闭原则的实现策略
  • “开闭原则”的比方
  • 不遵循开闭原则的示例
  • 使用开闭原则完善
  • 不遵循开闭原则的缺点
  • 破坏开闭原则的表现

开闭原则的定义

扩展开放:软件实体应该能够通过增加新的模块或功能来扩展其行为,而不需要修改现有的代码。
修改关闭:软件实体一旦被创建并投入使用,其内部实现应该保持稳定,不应轻易修改。

开闭原则的好处

提高软件的可维护性:通过减少修改现有代码的需求,降低了因修改引入错误的风险,使得软件更加稳定可靠。
增强软件的可扩展性:允许在不改变现有代码结构的情况下添加新功能,使得软件能够更灵活地应对未来需求的变化。
促进软件复用:遵循开闭原则设计的软件模块往往具有更高的独立性和可复用性,可以在不同的项目中重用。

开闭原则的实现策略

使用抽象和接口: 通过定义抽象类或接口来规定软件实体的行为,使得具体的实现类可以独立地变化而不会影响其他部分。
策略模式: 将一系列算法封装在独立的策略类中,并使它们可以相互替换。这样,算法的变化不会影响到使用算法的客户。
模板方法模式: 在父类中定义一个算法的框架,将某些步骤的实现延迟到子类中。这样,既可以在不修改父类代码的情况下扩展算法,又可以保持算法的结构清晰。

“开闭原则”的比方

排插是这一原理的一个很好的例子。
在这里插入图片描述
插头总是关闭的修改,一旦安装或扩展就不能进行修改,但是排插总是提供一种扩展方法,因此我们可以插入排插来获取更多的适配。

不遵循开闭原则的示例

银行提供各种类型的储蓄帐户(工资储蓄,定期储蓄等) ,以满足不同类型的客户的需要。银行有不同的规则集,每种储蓄账户类型都有不同的计算利息的规则集。
为了计算账户的利息,开发人员开发了以下类,并使用了计算利息的方法。

Public class SavingAccount  
{  
    //Other method and property and code  
    Public decimal CalculateInterest(AccountType accountType)  
    {  
        If(AccountType=="Regular")  
        {  
            //Calculate interest for regular saving account based on rules and   
            // regulation of bank  
           Interest = balance * 0.4;  
            If(balance < 1000) interest -= balance * 0.2;  
            If(balance < 50000) interest += amount * 0.4;  
        }  
        else if(AccountType=="Salary")  
        {  
            //Calculate interest for saving account based on rules and regulation of   
            //bank  
            Interest = balance * 0.5;  
        }  
    }  
}

在上述代码中,SavingAccount 类的 CalculateInterest 方法根据类似 Salary 和 Rules 的帐户类型进行计算。
这个实现并没有遵循开放关闭原则,因为如果明天银行引入了一个新的 SavingAccount 类型,那么就需要修改这个方法来为新的帐户类型添加一个新的用例。
例如,如果银行引入“子女储蓄帐户类型”,要求在计算该帐户类型的利息时附加一个新条件。这意味着该方法始终是开放的,可以进行修改。
这里还需要注意的一点是,该方法也没有遵循单一责任原则。因为这里的方法要做不止一件事,比如计算不止一种类型的利息。

使用开闭原则完善

继承只是实现开闭原则的一种方法。因为继承只是一个面向对象设计(OOD)的基本支柱,允许扩展现有类的功能。
为了实现开闭原则,可以使用接口、抽象类、抽象方法和虚方法,而不是在扩展功能时继承它们。
因此,储蓄帐户的前一个问题可以如下所示解决。
在这里插入图片描述

Interface ISavingAccount  
{  
   //Other method and property and code  
   decimal CalculateInterest();  
}  

Public Class RegularSavingAccount : ISavingAccount  
{  
  //Other method and property and code related to Regular Saving account  
  Public decimal CalculateInterest()  
  {  
    //Calculate interest for regular saving account based on rules and   
    // regulation of bank  
    Interest = balance * 0.4;  
    If(balance < 1000) interest -= balance * 0.2;  
    If(balance < 50000) interest += amount * 0.4;  
  }  
}  

  

Public Class SalarySavingAccount : ISavingAccount  
{  
  //Other method and property and code related to Salary Saving account`  
  Public decimal CalculateInterest()  
  {  
    //Calculate interest for saving account based on rules and regulation of   
    //bank  
    Interest = balance * 0.5;  
  }  
}

在上述代码中,创建了两个新的类,即从 IsavingAccount 继承的 RgularSavingAccount 和 SalarySavingAccount。
因此,如果银行添加了一个新帐户,那么就不需要修改现有类的逻辑,只需通过继承接口来扩展功能即可。
最后,前面的例子实现了开闭原则,因为不需要修改现有的已实现逻辑,而且它允许扩展添加新的逻辑。
前面的示例还实现了单一责任原则,因为每个类或函数只执行一个任务。
注意: 这里创建了一个接口作为示例。可以有一个由新的储蓄帐户类型实现的 SavingAccount 的抽象类。

不遵循开闭原则的缺点

1、因为类或函数总是允许添加新的逻辑,所以每当添加新的逻辑时,总是需要测试完整的功能。这需要为添加的功能添加一个新的测试用例,并且可能还需要修改由于添加的功能而失败的现有测试用例。
2、它还打破了单一责任原则,因为一个类或函数最终可能会执行多个任务。
3、类或函数的维护变得很困难,因为一个类或函数可能会变成数千行难以理解的代码。

破坏开闭原则的表现

类或函数总是可以修改的,换句话说总是允许添加更多的逻辑。就像前面的例子一样。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【机器学习】 7. 梯度下降法,随机梯度下降法SGD,Mini-batch SGD

梯度下降法,随机梯度下降法SGD,Mini-batch SGD 梯度下降法凸函数(convex)和非凸函数梯度更新方向选择步长的选择 随机梯度下降SGD(Stochastic Gradient Descent)梯度下降法&#xff1a;SGD: Mini-batch SGD 梯度下降法 从一个随机点开始决定下降方向&#xff08;重要&#xff…

关于kafka的分区和消费者之间的关系

消费者和消费者组 当生产者向 Topic 写入消息的速度超过了消费者&#xff08;consumer&#xff09;的处理速度&#xff0c;导致大量的消息在 Kafka 中淤积&#xff0c;此时需要对消费者进行横向伸缩&#xff0c;用多个消费者从同一个主题读取消息&#xff0c;对消息进行分流。 …

【JVM】OOM与调优(二)

OOM与调优 6.JVM工具如jps 该命令是纯Java编写的 -q:只显示Java进程的ID -m:输出Java进程的ID main函数所在类的名字 传递给main函数的参数 -l:输出Java进程的IDmain函数所在类的全限定名(包名类名) -v:输出Java进程的IDmain函数所在类的名称传递给JVM的参数 应用&am…

在野漏洞的应急响应流程

许多时候&#xff0c;对于负责安全工作又不太擅长安全漏洞技术的人员而言&#xff0c;如何应对突发漏洞是工作中主要的难点&#xff0c;这里的突发漏洞指的是两类&#xff1a;一类是通过新闻、咨询推送&#xff0c;被社会舆论所有关注的CVE漏洞&#xff0c;比如前段时间所谓的核…

【YOLOv10改进[Conv]】感受野注意力卷积RFAConv(2024.3)| 使用RFAConv 改进v10目标检测效果 + 含全部代码和详细修改方式

本文将进行在YOLOv10中使用RFAConv,助力YOLOv10目标检测效果,文中含全部代码、详细修改方式。助您轻松理解改进的方法。

手撕M.2 的B-KEY M-KEY、M+B KEY定义

SSD 尺寸与规格 我们在买ssd的时候&#xff0c;商家都会说什么ssd是2280还是2242规格的,这里的规格实际上就是代表的ssd的尺寸大小 M.2模组的尺寸目前有11种&#xff0c;用Type xxyy的方式表示&#xff0c;xx表示宽度&#xff0c;yy表示长度&#xff0c;单位为毫米。例如上面提…

(mcu) 嵌入式基础简单入门(程序架构分析)

文章目录 &#x1f4bd;前言&#x1f4bd;软件框架&#x1f4c0;工具环境&#x1f4c0;模板工程&#x1f4c0;编译后&#x1f4c0;Code&#x1f4c0;典例举例 &#x1f4bd;Keil 使用notes⭐END&#x1f31f;关注我 &#x1f4bd;前言 本文为一份简单入门笔记&#xff0c;以 st…

算法-单词规律(290)

leetcode题目链接 这道题用哈希表来解决 一个哈希表存放从单词到字符的映射&#xff0c;一个存放从字符到单词的映射&#xff0c;依照空格分隔字符&#xff0c;并将每个字符存放到vector字符数组&#xff0c; 遍历 pattern&#xff0c;对于每个字符 c 和对应的单词 w&#xf…

带你快速了解WEB应用服务器TOMCAT

目录 一、WEB技术 1.1 HTTP协议和B/S 结构 1.2 前端三大核心技术 1.2.1 HTML 1.2.2 CSS&#xff08;Cascading Style Sheets&#xff09;层叠样式表 1.2.3 JavaScript 二 WEB框架 2.1 web资源和访问 2.2 后台应用架构 2.2.1 单体架构 2.2.2 微服务 2.2.3 单体架构和…

机器学习 第6章 支持向量机

这里写目录标题 6.1 间隔与支持向量6.2 对偶问题6.3 核函数6.4 软间隔与正则化6.5 支持向量回归 6.1 间隔与支持向量 给定训练样本集 D { ( x 1 , y 1 ) , ( x 2 , y 2 ) , . . . , ( x m , y m ) } , y i ∈ { − 1 , 1 } D\left \{ (x_{1},y_{1}),(x_{2},y_{2}) ,...,(x_{…

禹晶、肖创柏、廖庆敏《数字图像处理》Otsu方法描述勘误

最大化类间距离准则与最小错误率准则不等价。 虚线处为最大类间距离&#xff0c;前景与背景直方图的交界处为最小错分概率&#xff0c;当部分前景错分为背景或者部分背景错分为前景时&#xff0c;一般会导致类间距离变小。所以两者接近&#xff0c;但不相等。 禹晶、肖创柏、…

《Spring Boot 集成 Swagger:打造高效接口文档与开发体验》

Swagger 一.导语&#xff1a; ​ 相信无论是前端还是后端开发&#xff0c;都或多或少地被接口文档折磨过。前端经常抱怨后端给的接口文档与实际情况不一致。后端又觉得编写及维护接口文档会耗费不少精力&#xff0c;经常来不及更新。其实无论是前端调用后端&#xff0c;还是后…

MindSearch 部署

任务 按照教程&#xff0c;将 MindSearch 部署到 HuggingFace 并美化 Gradio 的界面&#xff0c;并提供截图和 Hugging Face 的Space的链接。 创建开发机 & 环境配置 mkdir -p /root/mindsearch cd /root/mindsearch git clone https://github.com/InternLM/MindSearch.…

dbsyncer同步mysql数据

1 概述 DBSyncer&#xff08;代码地址&#xff1a;https://github.com/86dbs/dbsyncer&#xff09;是一款开源的数据同步中间件&#xff0c;提供MySQL、Oracle、SqlServer、PostgreSQL、Elasticsearch(ES)、Kafka、File、SQL等同步场景。支持上传插件自定义同步转换业务&#…

人脸质量评价:深入解析和实现

人脸质量评价&#xff1a;深入解析和实现 引言 随着人工智能和计算机视觉技术的飞速发展&#xff0c;人脸识别已成为许多领域的关键技术之一。然而&#xff0c;人脸识别的准确性高度依赖于输入的人脸图像质量。因此&#xff0c;人脸质量评价作为人脸识别前的预处理步骤&#…

如何利用电商 API 数据分析助力精准选品!

电商 API 数据分析在选品过程中起着至关重要的作用&#xff0c;它们之间有着密切的关系&#xff1a; 一、提供市场趋势洞察 热门商品识别&#xff1a; 通过分析电商 API 中的销售数据&#xff0c;包括商品的销售量、销售额、销售频率等指标&#xff0c;可以快速准确地识别出当…

清华计算几何-线段求交与BO算法

单轴线段求交 给定单边轴下, N定线段&#xff0c;检查出相交的线段. 解法一: 暴力求解 遍历所有线段对&#xff0c;进行相交判断, 算法复杂度为O(n2) 解法二: LR扫描 把每条线段的头尾认定为L和R。对所有点进行排序&#xff0c;如果每两个点满足LL或者RR&#xff0c;则对应…

Leetcode JAVA刷刷站(97)交错字符串

一、题目概述 二、思路方向 为了验证字符串 s3 是否由 s1 和 s2 交错组成&#xff0c;我们可以使用动态规划&#xff08;Dynamic Programming, DP&#xff09;的方法来解决这个问题。 首先&#xff0c;我们需要定义状态 dp[i][j]&#xff0c;它表示 s1 的前 i 个字符和 s2 的前…

DocuSign集成方案 | 结合 DocuSign 与 Oracle,加快业务完成速度!

DocuSign for Oracle 集成将 DocuSign 的电子签名功能与 Oracle 的项目生命周期管理 (PLM) 解决方案相结合 DocuSign 和 Oracle 是两家全球领先的技术公司&#xff0c;它们都致力于帮助企业简化和自动化流程。DocuSign 是电子签名领域的领导者&#xff0c;其解决方案可帮助企业…

【操作系统】实验:进度调度(2)

目录 一、实验目的 二、实验要求 三、实验步骤 四、核心代码 五、记录与处理 六、思考 七、完整报告和成果文件提取链接 一、实验目的 1、掌握高优先权调度算法 2、理解时间片、优先权、抢占等基本概念。 二、实验要求 1. 优先权属于静态优先权&#xff1b; 2. 进入 …