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

软件设计原则之开闭原则

开闭原则(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、类或函数的维护变得很困难,因为一个类或函数可能会变成数千行难以理解的代码。

破坏开闭原则的表现

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

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【大数据】深入解析向量数据库Faiss:搭建与使用指南
  • Swift-UITableView列表动态设置高度,根据不同的内容长度,设置heightForRowAt
  • WHAT - 通过 react-use 源码学习 React
  • 电商数据分析的价值
  • 订单类业务创建自增编码
  • Tongweb8074+7049m4 安装TongFlowControl(by lqw)
  • 指针(三)
  • MySQL 数据库自动分区
  • 使用Python恢复Windows、Linux、MacOS回收站中的文件和目录
  • MinIO实战攻略:轻松构建私有云存储解决方案
  • streamlit+wordcloud使用pyinstaller打包遇到的一些坑
  • boost库容器之Circular Buffer功能介绍,及使用示例
  • 神经网络微调技术全解(04)-- Prompt Tuning-可训练提示(Learnable Prompts)
  • 第十章 rust网络编程基础
  • 基于web的停车场管理系统设计与实现-计算机毕设 附源码 16856
  • ECMAScript 6 学习之路 ( 四 ) String 字符串扩展
  • Java 11 发布计划来了,已确定 3个 新特性!!
  • JavaScript函数式编程(一)
  • JS 面试题总结
  • js对象的深浅拷贝
  • js作用域和this的理解
  • miniui datagrid 的客户端分页解决方案 - CS结合
  • React Transition Group -- Transition 组件
  • React16时代,该用什么姿势写 React ?
  • Redis提升并发能力 | 从0开始构建SpringCloud微服务(2)
  • 发布国内首个无服务器容器服务,运维效率从未如此高效
  • 翻译--Thinking in React
  • 分布式熔断降级平台aegis
  • 关于for循环的简单归纳
  • 基于 Babel 的 npm 包最小化设置
  • 解析 Webpack中import、require、按需加载的执行过程
  • 快速构建spring-cloud+sleuth+rabbit+ zipkin+es+kibana+grafana日志跟踪平台
  • 每个JavaScript开发人员应阅读的书【1】 - JavaScript: The Good Parts
  • 排序算法学习笔记
  • 我与Jetbrains的这些年
  • 再谈express与koa的对比
  • 字符串匹配基础上
  • Prometheus VS InfluxDB
  • ​Java并发新构件之Exchanger
  • ​你们这样子,耽误我的工作进度怎么办?
  • # Spring Cloud Alibaba Nacos_配置中心与服务发现(四)
  • $NOIp2018$劝退记
  • ( 10 )MySQL中的外键
  • (¥1011)-(一千零一拾一元整)输出
  • (day6) 319. 灯泡开关
  • (MonoGame从入门到放弃-1) MonoGame环境搭建
  • (安卓)跳转应用市场APP详情页的方式
  • (二)fiber的基本认识
  • (附源码)spring boot基于Java的电影院售票与管理系统毕业设计 011449
  • (三分钟了解debug)SLAM研究方向-Debug总结
  • (五)关系数据库标准语言SQL
  • (原+转)Ubuntu16.04软件中心闪退及wifi消失
  • (原创)攻击方式学习之(4) - 拒绝服务(DOS/DDOS/DRDOS)
  • (转)PlayerPrefs在Windows下存到哪里去了?
  • **登录+JWT+异常处理+拦截器+ThreadLocal-开发思想与代码实现**