从C++示例理解开闭原则
开闭原则要求我们在编写代码时,尽量不去修改原先的代码,当出现新的业务需求时,应该通过增加新代码的形式扩展业务而不是对原代码进行修改。
假如我们现在有一批产品,每个产品都具有颜色和大小,产品其定义如下:
enum class Color { Red, Green, Blue };
enum class Size { Small, Medium, Large };struct Product
{string name;Color color;Size size;
};
这里 Product 定义为 struct 是因为 struct 默认的访问权限是公有方便书写,并且 struct 除了访问权限其他语法与 class 相同。
我们现在需要给一组产品提供过滤功能。于是定义下面的过滤器:
struct ProductFilter
{using Items = vector<Product*>;
}
当我们需要针对 Color 的过滤时,我们增加方法 by_color:
struct ProductFilter
{using Items = vector<Product*>;// 新增方法 by_colorItems by_color(Items items, Color color);
}
当我们需要针对 Size 的过滤时,我们增加方法 by_size:
struct ProductFilter
{using Items = vector<Product*>;Items by_color(Items items, Color color);// 新增方法 by_sizeItems by_size(Items items, Size size);
}
当我们需要针对 Color 和 Size 同时满足的筛选时,再添加…
可以看出当我们有新的需求时,必须要对 ProductFilter 类进行修改,并没有遵循开闭原则,所以我们希望重新设计使这个程序满足开闭原则,重构主要用到 template 模版编程。
首先,我们需要将过滤器分为两部分:过滤器本身和指定的过滤规范。
首先我们先定义一个规范接口,不同的过滤需求将通过继承此接口来满足:
template <typename T>
struct Specification
{virtual bool is_satisfied(T* item) = 0;
}
这里的类型 T 可以由我们自由地指定,我们可以指定为类型 Product 也可以指定为其他类型,这就意味着,这个规范将不再局限于 Product,我们可以在任何其他类型中使用它。
接下来是过滤器接口的定义:
template <typename T>
struct Filter
{virtual vector<T*> filter(vector<T*> items, Specification<T>& spec) const = 0;
}
同样地,这里使用模版编程来让过滤器不局限于对 Product 进行过滤。在虚函数 filter 中,我们接受 T 类型的容器,并通过 Specification 指定过滤规范。
然后我们需要继承 Filter 实现针对于 Product 的过滤器:
```cpp
struct BetterFilter: Filter<Product>
{vector<Product*> filter(vector<Product*> items, Specification<Product>& spec) const override {vector<Product*> result;for(auto& p: items) {if(spec.is_satisfied(p)) {result.push_back(p);}}return result;}
};
在 filter 方法中我们会调用 Specification& 中实现过滤规范对 vector<Product*> 容器中的对象进行筛选。
当我们有了以上的过滤器和规范接口之后,我们便可以在不修改代码的情况下,扩展业务了。
比如:当我们需要对于颜色的过滤器时,我们只需要继承 Specification 并覆盖 is_satisfied 方法来实现颜色的过滤法则,即可达到我们的目的:
// 颜色筛选规范
struct ColorSpecification : Specification<Product>
{Color color;explicit ColorSpecification(const Color& color) : color(color) {}bool is_satisfied(Product* item) override {return item->color == color;}
};
当我们需要针对 Size 的过滤时:
// 大小筛选规范
struct SizeSpecification : Specification<Product>
{Size size;explicit SizeSpecification(const Size& size) : size(size) {}bool is_satisfied(Product* item) override {return item->size == size;}
};
可以看到,我们不再需要修改过滤器来达到我们的目的,很显然我们遵从了开闭原则。
需要查看完整的示例代码可以访问 Github 仓库 GnCDesignPatterns。
参考:C++20设计模式
相关文章:
从C++示例理解开闭原则
开闭原则要求我们在编写代码时,尽量不去修改原先的代码,当出现新的业务需求时,应该通过增加新代码的形式扩展业务而不是对原代码进行修改。 假如我们现在有一批产品,每个产品都具有颜色和大小,产品其定义如下…...
Java线程池execute和submit的区别
前言 ThreadPoolExecutor提供了两种方法来执行异步任务,分别是execute和submit,也是日常开发中经常使用的方法,那么它俩有什么区别呢? 语义不同 首先是语义上的不同。execute声明在Executor接口,它接受一个Runnable…...
什么是json
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式。它基于JavaScript编程语言的一个子集,但是由于其文本格式清晰、易于解析,并且能够以键/值对的形式表示复杂的数据结构,因此它被广泛用于不同的编程语言和…...
基于聚类和回归分析方法探究蓝莓产量影响因素与预测模型研究附录
🌟欢迎来到 我的博客 —— 探索技术的无限可能! 🌟博客的简介(文章目录) 目录 背景数据说明数据来源思考 附录数据预处理导入包以及数据读取数据预览数据处理 相关性分析聚类分析数据处理确定聚类数建立k均值聚类模型 …...
java类型转换
pom <dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.76</version></dependency>BeanUtils 在这里插入代码片list<Map>转换成List<bean> public static <T> L…...
Unity打包Webgl端进行 全屏幕自适应
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一:修改 index.html二:将非移动端设备,canvas元素的宽度和高度会设置为100%。三:修改style.css总结 下载地址&#x…...
36. 【Java教程】输入输出流
本小节将会介绍基本输入输出的 Java 标准类,通过本小节的学习,你将了解到什么是输入和输入,什么是流;输入输出流的应用场景,File类的使用,什么是文件,Java 提供的输入输出流相关 API 等内容。 1…...
Visual C++2010学习版详细安装教程(超详细图文)
Visual C 介绍 Visual C(简称VC)是微软公司推出的一种集成开发环境(IDE),主要用于开发C和C语言的应用程序。它提供了强大的编辑器、编译器、调试器、库和框架支持,以及丰富的工具和选项,使得开…...
matlab图像处理入门
matlab在学校科研,仿真及基于模型开发的工作中有重要作用,在图像处理方面由于省去了复杂的上位机开发流程,因此可以让用户快速开发验证算法,下面简要介绍其在图像处理方面的应用。 matlab开发图像处理算法的流程主要是,…...
关于线程池面试题,使用“豆包”训练答案
我提问: 问题描述 下面是一个有关线程池调度的面试真题,来自于疯狂创客圈社群: 一个线程池的核心线程数为10个,最大线程数为20个,阻塞队列的容量为30。现在提交45个 任务,每个任务的耗时为500毫秒。 请问&…...
【WRF理论第二期】模型目录介绍
WRF理论第二期:模型目录介绍 1 WRF主目录2 WPS主目录3 编译后的可执行文件4 运行目录参考 了解 WRF 模型的目录结构有助于有效地管理和操作模型,从而确保模拟和分析工作的顺利进行。以下分解介绍WRF主目录、WPS主目录等。 Github-wrf-model/WRF 1 WRF…...
从了解到掌握 Spark 计算框架(一)Spark 简介与基础概念
文章目录 什么是 Spark?核心特点 Spark 对比 MapReduceSpark 编程模型RDDDataFrameDataset Spark 运行模式Spark 生态 什么是 Spark? Spark 是一个基于内存的分布式计算框架,最初由加州大学伯克利分校的 AMPLab 开发,后来捐赠给了…...
linux bind函数
bind函数的目的是让把客户端对应的端口(port)地址和ip地址绑定到客户端 [参考](Linux之bind 函数(详细篇)_linux bind函数-CSDN博客)...
Flink系列一:flink光速入门 (^_^)
引入 spark和flink的区别:在上一个spark专栏中我们了解了spark对数据的处理方式,在 Spark 生态体系中,对于批处理和流处理采用了不同的技术框架,批处理由 Spark-core,SparkSQL 实现,流处理由 Spark Streaming 实现&am…...
PySpark特征工程(III)--特征选择
有这么一句话在业界广泛流传:数据和特征决定了机器学习的上限,而模型和算法只是逼近这个上限而已。由此可见,特征工程在机器学习中占有相当重要的地位。在实际应用当中,可以说特征工程是机器学习成功的关键。 特征工程是数据分析…...
Mongodb的数据库简介、docker部署、操作语句以及java应用
Mongodb的数据库简介、docker部署、操作语句以及java应用 本文主要介绍了mongodb的基础概念和特点,以及基于docker的mongodb部署方法,最后介绍了mongodb的常用数据库操作语句(增删改查等)以及java下的常用语句。 一、基础概念 …...
七大战略性新兴产业崭露头角:新能源电燃灶或将成为未来厨房新宠
近日,在国家发布的七大战略性新兴产业名单中,新能源产业赫然在列,作为其中的重要组成部分,华火新能源电燃灶凭借其独特的优势,正逐渐走进人们的视野,有望成为未来厨房的新宠。 华火新能源电燃灶作为清洁能源…...
C#进阶-用于Excel处理的程序集
在.NET开发中,处理Excel文件是一项常见的任务,而有一些优秀的Excel处理包可以帮助开发人员轻松地进行Excel文件的读写、操作和生成。本文介绍了NPOI、EPPlus和Spire.XLS这三个常用的.NET Excel处理包,分别详细介绍了它们的特点、示例代码以及…...
持续总结中!2024年面试必问 20 道 Kafka面试题(五)
上一篇地址:持续总结中!2024年面试必问 20 道 Kafka面试题(四)-CSDN博客 九、请解释Kafka中的Zookeeper的作用。 在Kafka中,ZooKeeper扮演着至关重要的角色,主要负责集群管理、协调和状态同步等功能。以下…...
Draw.io 使用详细教程
Draw.io 是一款功能强大的在线绘图工具,适用于创建流程图、网络图、组织结构图、UML 图等。以下是详细的使用教程,包括基本操作、快捷键、常用技巧和进阶技巧。 1. 创建新图 选择存储位置 首次使用时,系统会询问你要将图保存到哪里。你可以…...
为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?
在建筑行业,项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升,传统的管理模式已经难以满足现代工程的需求。过去,许多企业依赖手工记录、口头沟通和分散的信息管理,导致效率低下、成本失控、风险频发。例如&#…...
全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
JS设计模式(4):观察者模式
JS设计模式(4):观察者模式 一、引入 在开发中,我们经常会遇到这样的场景:一个对象的状态变化需要自动通知其他对象,比如: 电商平台中,商品库存变化时需要通知所有订阅该商品的用户;新闻网站中࿰…...
人机融合智能 | “人智交互”跨学科新领域
本文系统地提出基于“以人为中心AI(HCAI)”理念的人-人工智能交互(人智交互)这一跨学科新领域及框架,定义人智交互领域的理念、基本理论和关键问题、方法、开发流程和参与团队等,阐述提出人智交互新领域的意义。然后,提出人智交互研究的三种新范式取向以及它们的意义。最后,总结…...
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的----NTFS源代码分析--重要
根目录0xa0属性对应的Ntfs!_SCB中的FileObject是什么时候被建立的 第一部分: 0: kd> g Breakpoint 9 hit Ntfs!ReadIndexBuffer: f7173886 55 push ebp 0: kd> kc # 00 Ntfs!ReadIndexBuffer 01 Ntfs!FindFirstIndexEntry 02 Ntfs!NtfsUpda…...
快速排序算法改进:随机快排-荷兰国旗划分详解
随机快速排序-荷兰国旗划分算法详解 一、基础知识回顾1.1 快速排序简介1.2 荷兰国旗问题 二、随机快排 - 荷兰国旗划分原理2.1 随机化枢轴选择2.2 荷兰国旗划分过程2.3 结合随机快排与荷兰国旗划分 三、代码实现3.1 Python实现3.2 Java实现3.3 C实现 四、性能分析4.1 时间复杂度…...
02.运算符
目录 什么是运算符 算术运算符 1.基本四则运算符 2.增量运算符 3.自增/自减运算符 关系运算符 逻辑运算符 &&:逻辑与 ||:逻辑或 !:逻辑非 短路求值 位运算符 按位与&: 按位或 | 按位取反~ …...
Python学习(8) ----- Python的类与对象
Python 中的类(Class)与对象(Object)是面向对象编程(OOP)的核心。我们可以通过“类是模板,对象是实例”来理解它们的关系。 🧱 一句话理解: 类就像“图纸”,对…...
Mac flutter环境搭建
一、下载flutter sdk 制作 Android 应用 | Flutter 中文文档 - Flutter 中文开发者网站 - Flutter 1、查看mac电脑处理器选择sdk 2、解压 unzip ~/Downloads/flutter_macos_arm64_3.32.2-stable.zip \ -d ~/development/ 3、添加环境变量 命令行打开配置环境变量文件 ope…...
CSS 工具对比:UnoCSS vs Tailwind CSS,谁是你的菜?
在现代前端开发中,Utility-First (功能优先) CSS 框架已经成为主流。其中,Tailwind CSS 无疑是市场的领导者和标杆。然而,一个名为 UnoCSS 的新星正以其惊人的性能和极致的灵活性迅速崛起。 这篇文章将深入探讨这两款工具的核心理念、技术差…...
