SOLID原则学习,开闭原则(Open Closed Principle, OCP)

文章目录
- 1. 定义
- 2. 开闭原则的详细解释
- 3. 实现开闭原则的方法
- 4. 总结
1. 定义
开闭原则(Open-Closed Principle,OCP)是面向对象设计中的五大原则(SOLID)之一,由Bertrand Meyer提出。开闭原则的核心思想是:
软件实体(类、模块、函数等)应该对扩展开放,但对修改关闭。
这意味着,当需求发生变化时,应该通过添加新的代码来扩展系统的行为,而不是修改已有的代码。这样可以减少对现有代码的影响,降低引入新错误的风险,并提高代码的可维护性和可复用性。
2. 开闭原则的详细解释
1. 对扩展开放:当系统需要增加新的功能时,应该允许通过添加新的代码来实现,而不是修改现有的代码。
2. 对修改关闭:一旦一个模块或类已经完成并通过测试,就不应该再修改它的源代码,除非是为了修复bug。
3. 实现开闭原则的方法
为了实现开闭原则,通常会使用以下设计模式或技术:
-
抽象与多态:通过定义抽象类或接口,允许子类或实现类扩展行为。
-
策略模式:将算法或行为封装在独立的类中,使得它们可以相互替换。
-
模板方法模式:定义一个算法的骨架,允许子类在不改变算法结构的情况下重新定义算法的某些步骤。
-
装饰器模式:动态地为对象添加新的行为,而不改变其原有结构。
实例分析
假设我们有一个简单的图形绘制系统,系统需要支持绘制不同类型的图形(如圆形、矩形等)。最初,系统只支持绘制圆形,但随着需求的变化,我们需要支持绘制矩形。
不符合开闭原则的实现
class Graphic {
public:void drawCircle() {// 绘制圆形的代码}void drawRectangle() {// 绘制矩形的代码}
};void drawAllShapes(Graphic& graphic) {graphic.drawCircle();graphic.drawRectangle();
}
在这个实现中,每当需要增加一个新的图形类型时,我们都需要修改Graphic类,添加新的绘制方法。这违反了开闭原则,因为我们需要修改已有的代码来扩展功能。
符合开闭原则的实现
为了符合开闭原则,我们可以使用抽象和多态来设计系统。
#include <iostream>
#include <vector>// 抽象基类
class Shape {
public:virtual void draw() const = 0;virtual ~Shape() = default;
};// 圆形类
class Circle : public Shape {
public:void draw() const override {std::cout << "Drawing a circle." << std::endl;}
};// 矩形类
class Rectangle : public Shape {
public:void draw() const override {std::cout << "Drawing a rectangle." << std::endl;}
};// 绘制所有图形
void drawAllShapes(const std::vector<Shape*>& shapes) {for (const auto& shape : shapes) {shape->draw();}
}int main() {std::vector<Shape*> shapes;shapes.push_back(new Circle());shapes.push_back(new Rectangle());drawAllShapes(shapes);// 清理内存for (const auto& shape : shapes) {delete shape;}return 0;
}
在这个实现中,我们定义了一个抽象的Shape类,并让Circle和Rectangle继承自Shape。当需要增加新的图形类型时,我们只需要创建一个新的类并实现draw方法,而不需要修改现有的代码。这样,系统对扩展是开放的,对修改是关闭的。
4. 总结
开闭原则是面向对象设计中的重要原则,它鼓励我们通过扩展而不是修改来增加系统的功能。通过使用抽象和多态,我们可以设计出符合开闭原则的系统,从而提高代码的可维护性和可复用性。
相关文章:
SOLID原则学习,开闭原则(Open Closed Principle, OCP)
文章目录 1. 定义2. 开闭原则的详细解释3. 实现开闭原则的方法4. 总结 1. 定义 开闭原则(Open-Closed Principle,OCP)是面向对象设计中的五大原则(SOLID)之一,由Bertrand Meyer提出。开闭原则的核心思想是…...
Unreal Engine 5 C++ Advanced Action RPG 七章笔记
第七章 Ranged Enemy 2-Ranged Enemy Starting Weapon 制作新敌人的流程准备 新敌人的武器起始的状态数据自己的战斗能力投射能力自己的行为树 创建角色,添加武器,添加数据,就是继承之前的基类敌人的 运行结果 3-Glacer Starting Stats 看看就行,就是复制曲线表格更改数…...
自动连接校园网wifi脚本实践(自动网页认证)
目录 起因执行步骤分析校园网登录逻辑如何判断当前是否处于未登录状态? 书写代码打包设置开机自动启动 起因 我们一般通过远程控制的方式访问实验室电脑,但是最近实验室老是断电,但重启后也不会自动连接校园网账户认证,远程工具&…...
HTTP/HTTPS ⑤-CA证书 || 中间人攻击 || SSL/TLS
这里是Themberfue ✨上节课我们聊到了对称加密和非对称加密,实际上,单纯地非对称加密并不能保证数据不被窃取,我们还需要一个更加重要的东西——证书 中间人攻击 通过非对称加密生成私钥priKey和公钥pubKey用来加密对称加密生成的密钥&…...
traceroute原理探究
文章中有截图,看不清的话,可以把浏览器显示比例放大到200%后观看。 linux下traceroute的原理 本文通过抓包观察一下linux下traceroute的原理 环境:一台嵌入式linux设备,内网ip是192.168.186.195,其上有192.168.202.…...
50_Lua垃圾回收
1.Lua垃圾回收机制概述 Lua采用了一种自动内存管理机制,称为垃圾回收(Garbage Collection, GC)。垃圾回收的主要目的是回收程序中不再被使用的内存,从而避免内存泄漏。Lua的垃圾回收器负责回收动态分配的对象,如函数、用户数据、表、字符串、线程、内部结构等。Lua的垃圾…...
Git-2-:Cherry-Pick 的使用场景及使用流程
前面我们说了 Git合并、解决冲突、强行回退等解决方案 >> 点击查看 这里再说一下 Cherry-Pick功能,Cherry-Pick不是merge,只是把部分功能代码Cherry-Pick到远程的目标分支 git cherry-pick功能简介: git cherry-pick 是用来从一个分…...
【C++】21.map和set的使用
文章目录 1. 序列式容器和关联式容器2. set系列的使用2.1 set和multiset参考文档2.2 set类的介绍2.3 set的构造和迭代器构造函数:双向迭代器迭代器: 2.4 set的增删查2.5 insert和迭代器遍历使用样例:2.6 find和erase使用样例:2.7 …...
burpsiute的基础使用(2)
爆破模块(intruder): csrf请求伪造访问(模拟攻击): 方法一: 通过burp将修改,删除等行为的数据包压缩成一个可访问链接,通过本地浏览器访问(该浏览器用户处于登陆状态&a…...
ElasticSearch 同义词匹配
synonym.txt 电脑, 计算机, 主机 复印纸, 打印纸, A4纸, 纸, A3 平板电脑, Pad DELETE /es_sku_index_20_20250109 PUT /es_sku_index_20_20250109 {"settings": {"index": {"number_of_shards": "5","number_of_replicas&quo…...
linux RT-Preempt spin lock实现
一、spin_lock概述 Spinlock是linux内核中常用的一种互斥锁机制,和mutex不同,当无法持锁进入临界区的时候,当前执行线索不会阻塞,而是不断的自旋等待该锁释放。正因为如此,自旋锁也是可以用在中断上下文的。也正是因为…...
PySpark广播表连接解决数据倾斜的完整案例
使用PySpark解决数据倾斜问题的完整案例,通过广播表连接的方式来优化性能。 准备数据 假设我们有两张表,一张大表 big_table 和一张小表 small_table ,小表将作为广播表。 from pyspark.sql import SparkSession# 初始化SparkSession spar…...
Chromium CDP 开发(十二):为自己的Domain建立custom_config.json
引言 本章详细介绍了如何为自定义的 CDP Domain 创建 custom_config.json 文件,并通过修改 BUILD.gn 文件来确保自定义的配置文件参与编译。我们通过 inspector_protocol_generate 配置段自动生成自定义 Domain 的头文件和实现文件,并成功将其集成到构建…...
【Vue】全局/局部组件使用流程(Vue2为例)
全局组件和局部组件区别 如何使用 全局组件:全局注册后,可以在任意页面中直接使用。局部组件:在页面中需要先导入子组件路径,注册组件才能使用。 适用场景 全局组件:适用于高频使用的组件,如导航栏、业…...
Vue.js组件开发详解
在现代前端开发中,Vue.js 凭借其简洁、高效、灵活的特性,成为了众多开发者的首选框架之一,而组件化开发则是 Vue.js 的核心优势。组件可以将复杂的 UI 界面拆分成一个个独立的、可复用的小块,极大地提高了开发效率和代码的可维护性…...
解决:ubuntu22.04中IsaacGymEnv保存视频报错的问题
1. IsaacGymEnvs项目介绍 IsaacGymEnvs:基于NVIDIA Isaac Gym的高效机器人训练环境 IsaacGymEnvs 是一个基于 NVIDIA Isaac Gym 的开源 Python 环境库,专为机器人训练提供高效的仿真环境。Isaac Gym 是由 NVIDIA 开发的一个高性能物理仿真引擎…...
深度学习camp-第J7周:对于ResNeXt-50算法的思考
🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 📌你需要解决的疑问:这个代码是否有错?对错与否都请给出你的思考 📌打卡要求:请查找相关资料、逐步…...
java: 错误: 无效的源发行版:17解决办法
遇到“java: 错误: 无效的源发行版:17”的问题,通常是因为项目设置中指定的Java版本与当前环境不一致导致的。以下是几种可能的解决方案: 检查并升级Java版本:确保你已经安装了支持Java 17的JDK版本。你可以通过命令行输入java -v…...
Docker 安装开源的IT资产管理系统Snipe-IT
一、安装 1、创建docker-compose.yaml version: 3services:snipeit:container_name: snipeitimage: snipe/snipe-it:v6.1.2restart: alwaysports:- "8000:80"volumes:- ./logs:/var/www/html/storage/logsdepends_on:- mysqlenv_file:- .env.dockernetworks:- snip…...
Go语言封装加解密包(AES/DES/RSA)
Go语言封装加解密包(AES/DES/RSA) 1. Base64编码与解码2. AES加解密3. DES加解密4. RSA加解密5. SHA256哈希6. 单元测试1. AES加解密单元测试2. DES加解密单元测试3. RSA加解密单元测试4. SHA256哈希单元测试测试用例说明 总结 在现代软件开发中…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
什么是库存周转?如何用进销存系统提高库存周转率?
你可能听说过这样一句话: “利润不是赚出来的,是管出来的。” 尤其是在制造业、批发零售、电商这类“货堆成山”的行业,很多企业看着销售不错,账上却没钱、利润也不见了,一翻库存才发现: 一堆卖不动的旧货…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
LLMs 系列实操科普(1)
写在前面: 本期内容我们继续 Andrej Karpathy 的《How I use LLMs》讲座内容,原视频时长 ~130 分钟,以实操演示主流的一些 LLMs 的使用,由于涉及到实操,实际上并不适合以文字整理,但还是决定尽量整理一份笔…...
STM32HAL库USART源代码解析及应用
STM32HAL库USART源代码解析 前言STM32CubeIDE配置串口USART和UART的选择使用模式参数设置GPIO配置DMA配置中断配置硬件流控制使能生成代码解析和使用方法串口初始化__UART_HandleTypeDef结构体浅析HAL库代码实际使用方法使用轮询方式发送使用轮询方式接收使用中断方式发送使用中…...
并发编程 - go版
1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...
HTML前端开发:JavaScript 获取元素方法详解
作为前端开发者,高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法,分为两大系列: 一、getElementBy... 系列 传统方法,直接通过 DOM 接口访问,返回动态集合(元素变化会实时更新)。…...
xmind转换为markdown
文章目录 解锁思维导图新姿势:将XMind转为结构化Markdown 一、认识Xmind结构二、核心转换流程详解1.解压XMind文件(ZIP处理)2.解析JSON数据结构3:递归转换树形结构4:Markdown层级生成逻辑 三、完整代码 解锁思维导图新…...
