软件设计原则-开闭原则讲解以及代码示例
开闭原则
一,介绍
1.前言
开闭原则(Open-Closed Principle,OCP)是面向对象设计中的一条重要原则,它由Bertrand Meyer在其著作《面向对象软件构造》中提出,并成为SOLID原则之一。
开闭原则的核心思想是:软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。简单来说,就是在不修改已有代码的情况下,通过扩展来实现新的功能或变化。
2.何时使用开闭原则
-
当需要添加新功能时:如果你需要在已有的代码基础上添加新功能,你应该通过扩展现有代码,而不是直接修改已有代码。这样做的好处是不会对原有的功能产生影响,同时也保证了代码的可维护性和稳定性。
-
当需要修改已有功能时:尽管开闭原则主张对已有代码进行关闭,但有时我们仍然需要修改一些已有功能。这种情况下,可以考虑通过使用抽象层、接口或者设计模式来隔离修改的影响范围,从而保持已有功能的稳定性,而不必修改大量的代码。
-
当需要适应变化:在软件开发过程中,需求和业务环境可能会发生变化。为了应对这种变化,我们需要让系统具备良好的扩展性。通过遵循开闭原则,我们可以更容易地添加、调整或替换组件,以适应变化的需求。
二,代码示例
为了更详细地介绍开闭原则,我们可以通过一个例子来说明:
假设有一个图形绘制程序,程序需要能够绘制不同形状的图形,比如矩形、圆形和三角形。最初的设计可能会像这样:
class Shape {private String type;public Shape(String type) {this.type = type;}public void draw() {if (type.equals("rectangle")) {System.out.println("绘制矩形");} else if (type.equals("circle")) {System.out.println("绘制圆形");} else if (type.equals("triangle")) {System.out.println("绘制三角形");}}
}
这个设计看起来似乎没有问题,但问题在于当我们需要添加新的图形类型时,需要修改`Shape`类的源代码,违背了开闭原则。
为了符合开闭原则,我们可以进行重构。首先,我们定义一个抽象类`Shape`:
abstract class Shape {public abstract void draw();
}
然后,对每种具体的图形类型,创建一个子类并实现`draw()`方法:
class Rectangle extends Shape {@Overridepublic void draw() {System.out.println("绘制矩形");}
}class Circle extends Shape {@Overridepublic void draw() {System.out.println("绘制圆形");}
}class Triangle extends Shape {@Overridepublic void draw() {System.out.println("绘制三角形");}
}
现在,我们可以通过扩展子类来添加新的图形类型,而无需修改`Shape`类的源代码。例如,如果需要添加椭圆形,只需创建一个`Ellipse`类,并实现`draw()`方法即可。
这个重构后的设计符合开闭原则,因为我们通过扩展子类来实现新的功能,而不需要修改父类的代码。这样做的好处是,已有的代码保持不变,不会引入新的错误,同时也增加了系统的可扩展性和可维护性。
总结起来,开闭原则鼓励我们在设计软件时,采用抽象、封装和多态等方式,使得系统能够以最小的修改来适应变化。这种设计思想能够提高代码的可复用性、可扩展性和可维护性,是良好的软件设计实践之一。
三,优缺点
开闭原则的优点:
1. 提高了代码的可维护性与复用性:遵循开闭原则可以让代码更加稳定和可维护,同时也使得代码更容易被复用。如果我们需要修改某个模块的行为,只需要扩展该模块而不需要直接修改源代码,这样就不会影响到其他的模块。
2. 提高了代码的可扩展性:开闭原则还可以提高代码的可扩展性。通过扩展已有的代码,我们可以很容易地添加新的功能或改进现有功能,从而适应业务需求的更改。
3. 提高了代码的可测试性:遵循开闭原则可以降低代码的耦合度,使得测试更加容易。因为我们只需要测试新增的代码,而不必验证已有代码的正确性。
开闭原则的缺点:
1. 对代码的设计要求高:遵循开闭原则需要对代码进行良好的抽象和封装,这对程序员的设计能力和经验要求较高。如果设计不好,可能会导致代码过于复杂难以维护。
2. 可能会增加代码量:通过扩展已有的代码来实现新功能,可能会增加代码量,使得系统变得更加复杂。这需要我们在平衡可维护性和代码量之间做出权衡。
3. 可能会带来设计上的限制:在某些情况下,为了遵循开闭原则,我们可能需要引入更多的抽象层或接口。这可能会带来一定的设计上的限制,限制了代码的表达能力和灵活性。
总之,开闭原则是一条非常重要的设计原则,可以提高代码的可维护性、可复用性和可扩展性,但也需要平衡代码的表达能力和灵活性,避免过度设计。
四,Java那里用到了开闭原则
在Java中,开闭原则(Open-Closed Principle)是面向对象设计原则之一,它强调软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。在Java的各个层面都可以应用开闭原则,下面是几个常见的应用场景:
1. 接口和抽象类:通过定义接口和抽象类,可以为系统定义一组稳定的抽象层,对扩展开放。其他具体的实现类可以继承或实现这些抽象层,通过扩展的方式增加新的功能,而无需修改已有的代码。
2. 使用多态性:Java的多态性机制支持开闭原则的实现。通过基类或接口引用指向子类对象,程序可以在不修改原有代码的情况下,动态地添加新的子类,扩展系统的功能。
3. 设计模式:许多设计模式都是基于开闭原则的思想,通过封装变化和抽象稳定部分,实现对扩展开放,对修改关闭。例如,策略模式、观察者模式、装饰器模式等都可以帮助实现开闭原则。
4. 使用反射机制:Java的反射机制可以动态地获取和操作类、方法、属性等信息,提供了一种在运行时操作类结构的能力。通过反射,可以在不修改代码的情况下,动态地实例化对象、调用方法等,实现对系统的扩展。
5. 使用依赖注入(Dependency Injection):依赖注入是一种实现开闭原则的常见方式。通过将依赖关系的创建和管理交由框架来完成,可以在不修改源代码的情况下,通过配置文件或注解改变依赖的实现,从而扩展系统的功能。
总的来说,Java作为一门面向对象的编程语言,提供了许多机制和设计模式来支持开闭原则的实现。合理运用接口和抽象类、多态性、设计模式、反射机制以及依赖注入等技术手段,可以使Java程序更具可扩展性和可维护性,符合开闭原则的设计原则。
五,总结
1.常规总结
本文介绍了开闭原则(Open-Closed Principle,OCP)的基本概念和作用,即软件实体(类、模块、函数等)应该对扩展开放,对修改关闭。通过一个图形绘制程序的例子,说明如何遵循开闭原则使得代码更加稳定、可维护、可扩展和可测试。同时,本文也分析了开闭原则的优缺点,需要平衡代码的可维护性、可复用性和可扩展性,避免过度设计。
2.幽默总结
开闭原则就是让你的代码像女朋友一样,对改进开放,对修改关闭。要保持稳定,可扩展和可维护,但是也要注意不要设计过度,否则就像约会时穿太多衣服一样僵硬。
3.搞笑总结
开闭原则是指软件实体应该对扩展开放,对修改关闭。这意味着我们应该通过扩展来实现新的功能,而不是直接修改已有代码。这样做的好处是可以提高代码的可维护性、复用性和可扩展性。虽然遵循开闭原则需要对代码进行良好的抽象和封装,而且可能会增加代码量和带来设计上的限制,但是这条原则仍然是非常重要的。如果你违反了开闭原则,那么你就是在自找麻烦!
相关文章:
软件设计原则-开闭原则讲解以及代码示例
开闭原则 一,介绍 1.前言 开闭原则(Open-Closed Principle,OCP)是面向对象设计中的一条重要原则,它由Bertrand Meyer在其著作《面向对象软件构造》中提出,并成为SOLID原则之一。 开闭原则的核心思想是&…...
分类预测 | MATLAB实现SSA-CNN-GRU-Attention数据分类预测(SE注意力机制)
分类预测 | MATLAB实现SSA-CNN-GRU-Attention数据分类预测(SE注意力机制) 目录 分类预测 | MATLAB实现SSA-CNN-GRU-Attention数据分类预测(SE注意力机制)分类效果基本描述模型描述程序设计参考资料 分类效果 基本描述 1.MATLAB实现…...
LeetCode--180 连续出现的数字
文章目录 1 题目描述2 结果示例3 解题思路3.1 MySQL 代码 4 知识拓展 1 题目描述 Logs表: ---------------------- | Column Name | Type | ---------------------- | id | int | | num | varchar | ----------------------在 SQL 中,id …...
面试算法34:外星语言是否排序
题目 有一门外星语言,它的字母表刚好包含所有的英文小写字母,只是字母表的顺序不同。给定一组单词和字母表顺序,请判断这些单词是否按照字母表的顺序排序。例如,输入一组单词[“offer”,“is”,“coming”…...
常用docker命令 docker_cmd_sheet
查看所有docker 容器 docker ps 查看 特定docker容器 比如con1 docker ps | grep con1 查看镜像 docker images 拉取镜像 docker pull imageXXX 打标签 docker tag imageXXX:1.4.0 镜像名冒号tag 按照docker-compose.yml 启动容器,在有docker-compose…...
算法进阶——数组中的逆序对
题目 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P mod 1000000007 数据范围:对于 50% 的数据, size≤104 对…...
hackmyvm之gift
hackmyvm是一个平台,包含了大量靶机,类似于vulnhub、hackthebox等平台,你可以在上面下载靶机,进行渗透测试练习,非常适合热爱黑客技术或从事渗透测试的人员。 (这段解释参考这篇文章) 下载安装…...
1024,向着“顶尖程序员“迈进
10月24日,对每个程序员而言,都是一个具有特殊意义的日子。1024这个数字,不再只是计算机存储容量的基础单位,更是我们向着技术巅峰进发的象征。 回顾我的程序员之路,那是一个不断学习、不断成长的过程。起初是对编程充…...
Arcgis 数据操作
在进行数据操作的时候,需要注意坐标系要一致,这是前提。 数据类型 文件地理数据库:gbd 个人地理数据库:mdb (Mircosoft Access) 矢量数据:shp 推荐使用gbd数据,效率会更高。 采…...
YoloV7改进策略:SwiftFormer,全网首发,独家改进的高效加性注意力用于实时移动视觉应用的模型,重构YoloV7
文章目录 摘要论文:《SwiftFormer:基于Transformer的高效加性注意力用于实时移动视觉应用的模型》1、简介2、相关研究3、方法3.1、注意力模块概述3.2、高效的加性注意力3.3、SwiftFormer 架构4、实验4.1、实现细节4.2、基线比较4.3、图像分类4.4、目标检测和实例分割4.5、语义…...
Day07 Stream流递归Map集合Collections可变参数
Stream 也叫Stream流,是Jdk8开始新增的一套API (java.util.stream.*),可以用于操作集合或者数组的数据。 Stream流大量的结合了Lambda的语法风格来编程,提供了一种更加强大,更加简单的方式操作 public class Demo1 {public stati…...
8.JavaScript-注释
题记 javascript注释 单行注释 <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>实例</title> </head> <body><h1 id"myH1"></h1> <p id"myP"></p>…...
知识分享|分段函数线性化及matlab测试
目录 1 使用0-1变量将分段函数转换为线性约束 2 连续函数采用分段线性化示例 3 matlab程序测试 4 matlab测试结果说明 5 分段线性化应用 1 使用0-1变量将分段函数转换为线性约束 2 连续函数采用分段线性化示例 3 matlab程序测试 clc;clear all; gn10;tn1; x_pfsdpvar(1, t…...
ant target的depends属性
ant的target使用depends属性指明对其它target的依赖。可以依赖多个target,被依赖的多个target之间用逗号分隔。 ant会确保被依赖的target首先执行,然后再执行本target。 ant尽量按照depends属性中指明的target出现的顺序来执行(从左到右&…...
【三维重建】DreamGaussian:高斯splatting的单视图3D内容生成(原理+代码)
文章目录 摘要一、前言二、相关工作2.1 3D表示2.2 Text-to-3D2.3 Image-to-3D 三、本文方法3.1生成式 高斯 splitting3.2 高效的 mesh 提取3.3 UV空间的纹理优化 四. 实验4.1实施细节4.2 定性比较4.3 定量比较4.4 消融实验 总结(特点、局限性) 五、安装与…...
如何使用Flutter开发执行操作系统shell命令的工具
简介 Flutter是一种由Google开发的移动应用程序开发框架,它允许开发人员使用单个代码库构建高性能、高质量的移动体验。而Android终端命令行工具则允许用户在Android手机上运行类似于Linux的操作系统命令。本文的目的是介绍如何在Flutter应用中开发一个Android终端命…...
西山居 游戏研发工程师实习生 面经
西山居实习面经 面试时长:26min(两个面试官交替问) 1、自我介绍 2、你平常怎么学习的 3、你实习接受加班么 4、说一下Unity的生命周期,Start和Awake哪里不同 5、Unity中Update与FixedUpdate的区别,怎么设置Fixed…...
YOLOv8训练自己的数据集+改进方法复现
yolov8已经出来好几个月了,并且yolov8从刚开始出来之后的小版本也升级好几次,总体变化不大,个别文件存放位置发生了变化,以下以最新版本的YOLOv8来详细学习和使用YOLOv8完成一次目标检测。 一、环境按照 深度学习环境搭建就不再…...
尚硅谷kafka3.0.0
目录 💃概述 ⛹定义 编辑⛹消息队列 🤸♂️消息队列应用场景 编辑🤸♂️两种模式:点对点、发布订阅 编辑⛹基本概念 💃Kafka安装 ⛹ zookeeper安装 ⛹集群规划 编辑⛹流程 ⛹原神启动 🤸♂️…...
【Andriod】Appium的不同版本(Appium GUI、Appium Desktop、Appium Server )的安装教程
文章目录 前言一.Appium GUI二.Appium Desktop三.Appium Server 命令行版本1.安装node.js2.安装Appium Server 前言 Appium 安装提供两2方式:桌面版和命令行版。其中桌面版又分为 Appium GUI 和 Appium Desktop。 建议:使用Appium Desktop 一.Appium …...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
Java 语言特性(面试系列1)
一、面向对象编程 1. 封装(Encapsulation) 定义:将数据(属性)和操作数据的方法绑定在一起,通过访问控制符(private、protected、public)隐藏内部实现细节。示例: public …...
【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...
【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)
🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...
leetcodeSQL解题:3564. 季节性销售分析
leetcodeSQL解题:3564. 季节性销售分析 题目: 表:sales ---------------------- | Column Name | Type | ---------------------- | sale_id | int | | product_id | int | | sale_date | date | | quantity | int | | price | decimal | -…...
免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...
解决:Android studio 编译后报错\app\src\main\cpp\CMakeLists.txt‘ to exist
现象: android studio报错: [CXX1409] D:\GitLab\xxxxx\app.cxx\Debug\3f3w4y1i\arm64-v8a\android_gradle_build.json : expected buildFiles file ‘D:\GitLab\xxxxx\app\src\main\cpp\CMakeLists.txt’ to exist 解决: 不要动CMakeLists.…...
抽象类和接口(全)
一、抽象类 1.概念:如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象,这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法,包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中,⼀个类如果被 abs…...
人工智能 - 在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型
在Dify、Coze、n8n、FastGPT和RAGFlow之间做出技术选型。这些平台各有侧重,适用场景差异显著。下面我将从核心功能定位、典型应用场景、真实体验痛点、选型决策关键点进行拆解,并提供具体场景下的推荐方案。 一、核心功能定位速览 平台核心定位技术栈亮…...
用递归算法解锁「子集」问题 —— LeetCode 78题解析
文章目录 一、题目介绍二、递归思路详解:从决策树开始理解三、解法一:二叉决策树 DFS四、解法二:组合式回溯写法(推荐)五、解法对比 递归算法是编程中一种非常强大且常见的思想,它能够优雅地解决很多复杂的…...
