【C++设计模式】开放-封闭原则

2023年8月27日,周日下午

我觉得我的这篇博客还是写得很不错的,哈哈哈。


目录

  • 概述
  • 举例说明
  • 用开放-封闭原则重构

概述

开放-封闭原则(Open-Closed Principle,OCP)是面向对象设计中的一个重要原则,也是许多设计模式的基础。它由Bertrand Meyer在他的书《面向对象软件构造》中提出,并被广泛应用于软件开发中。

开放-封闭原则的核心思想是:软件实体(类、模块、函数等)应该对扩展开放,对修改封闭。换句话说,当需要修改一个软件实体时,应该通过扩展它的行为,而不是修改它的源代码。

这个原则的目标是实现软件设计的稳定性和可维护性。通过遵循开放-封闭原则,我们可以减少修改已有代码的需求,从而降低了引入新错误的风险,并提高了代码的可复用性

实现开放-封闭原则的关键是使用抽象和多态。通过定义抽象的接口或基类,可以将代码与特定的实现分离开来。这样,在需要变更行为时,我们只需要创建新的实现类并基于抽象进行扩展,而不需要修改已有的代码。

举例说明

假如有一天,公司要我写一个计算圆的面积的函数getArea

#include <iostream>// 计算圆的面积
double getArea(double radius) {return 3.14159 * radius * radius;
}int main() {// 计算面积double area = getArea(3);// 输出面积std::cout << "area: " << area << std::endl;return 0;
}

这很简单,是不是?

但是后来公司要求这个getArea函数要增加计算正方形的面积的功能,

假设我不懂开放-封闭原则,那么我只能老老实实修改getArea函数内部的代码

#include <iostream>// 计算面积
double getArea(double num,std::string thing) {if(thing=="圆形")return 3.14159 * num * num;if(thing=="正方形")return num*num;
}int main() {// 计算面积double area1 = getArea(3,"圆形");double area2=getArea(4,"正方形");// 输出面积std::cout << "圆形面积: " << area1 << std::endl;std::cout << "正方形面积: " << area2 << std::endl;return 0;
}

虽然我也完成了任务,但可以看到getArea函数变得复杂了:参数由1个变成2个;内部的实现代码也更多了。

但是任务还没结束,后来公司又让我给getArea函数添加计算长方形的功能

#include <iostream>// 计算面积
double getArea(double num1,double num2,std::string thing) {if(thing=="圆形")return 3.14159 * num1 * num1;if(thing=="正方形")return num1*num1;if(thing=="长方形")return num1*num2;
}int main() {// 计算面积double area1 = getArea(3,0,"圆形");double area2=getArea(4,0,"正方形");double area3=getArea(4,3,"长方形");// 输出面积std::cout << "圆形面积: " << area1 << std::endl;std::cout << "正方形面积: " << area2 << std::endl;std::cout << "长方形面积: " << area3 << std::endl;return 0;
}

可以看出来,我的getArea函数不仅变得更加难以理解,而且变得更加复杂了:参数由2个变成3个,而且内部代码实现也变多了。

接下来就不用写,照这么写下去,随着需求的增多,getArea函数只会变得越来越复杂和难以理解。

用开放-封闭原则重构

不难看出,在getArea中不变的是要返回一个面积,不断变化的是不同图形的计算方法,

所以可以封闭getArea的”返回一个面积“,而开放”计算方法“。

我把所有图形抽象成一个Shape抽象类,要求所有Shape抽象类的派生类都必须提供一个返回面积的接口。至于这些派生类怎么实现父类Shape要求的返回面积的接口,就各显神通、因地制宜了,此之谓”开放扩展“

而getArea函数只需雷打不动地调用Shape类的派生类的返回面积的接口就可以了,此之谓”封闭修改“。

#include <iostream>// 抽象基类,用于表示图形形状
class Shape {
public:virtual double area() const = 0;
};// 具体的图形形状:矩形
class Rectangle : public Shape {
public:double width;double height;Rectangle(double w, double h) : width(w), height(h) {}double area() const override {return width * height;}
};// 具体的图形形状:圆形
class Circle : public Shape {
public:double radius;Circle(double r) : radius(r) {}double area() const override {return 3.14159 * radius * radius;}
};// 计算所有图形的总面积
double getArea(const Shape* shape) {return shape->area();
}int main() {// 创建矩形和圆形对象Circle circle(3);Rectangle rect1(4, 4);Rectangle rect2(4, 3);// 计算总面积double area1 = getArea(&circle);double area2=getArea(&rect1);double area3=getArea(&rect2);// 输出面积std::cout << "圆形面积: " << area1 << std::endl;std::cout << "正方形面积: " << area2 << std::endl;std::cout << "长方形面积: " << area3 << std::endl;return 0;
}

可以看到,无论公司要求增加什么图形的计算面积功能,都不需要修改getArea函数,

只需要增加一个继承自Shape类的派生类就可以了,

不信的话,你们可以再添加一个计算梯形的面积试试,就当作一个小作业。

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

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

相关文章

npm install sentry-cli失败的问题

1. 目前报错 2. 终端运行 npm set ENTRYCLI_CDNURLhttps://cdn.npm.taobao.org/dist/sentry-cli npm set sentrycli_cdnurlhttps://cdn.npm.taobao.org/dist/sentry-cli3. 再安装 npx sentry/wizardlatest -i nextjs即可成功

将Series序列中的缺失值用后一个值填充Series.bfill()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 对于Series序列中的缺失值n1 用n1后面的值n2来填充替代 Series.bfill() [太阳]选择题 关于以下代码的说法中错误的是? import numpy as np import pandas as pd a pd.Series([1,np.nan,2,np.…

react 获取表单中输入框的值

通过使用useState钩子来创建一个状态变量&#xff0c;你可以同时获取多个Input框的值。 1获取input框的值&#xff1a; import React, { useState } from react;function MyComponent() {const [forms, setForms] useState({name: ,nation: ,});const handleInputChange (e…

BlazorServer中C#与JavaScript的相互调用

BlazorServer中C#与JavaScript的相互调用 前言&#xff1a; ​ 虽然BlazorServer中推荐使用C#在razor页面中的替代JavaScript来完成逻辑的编写&#xff0c;但当需要使用第三方的javascript文件/组件里的内容时&#xff0c;则难免要在C#中调用其方法或对象。反之当你的(用到第…

【Go 基础篇】切片:Go语言中的灵活数据结构

在Go语言中&#xff0c;切片&#xff08;Slice&#xff09;是一种强大且灵活的数据结构&#xff0c;用于管理和操作一系列元素。与数组相比&#xff0c;切片的大小可以动态调整&#xff0c;这使得它成为处理动态数据集合的理想选择。本文将围绕Go语言中切片的引入&#xff0c;介…

WPF网格拖动自动布局效果

WPF网格拖动自动布局效果 使用Canvas和鼠标相关事件实现如下的效果: XAML代码: <Window x:Class="CanvasTest.MainWindow"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:

Linux部署RocketMQ并使用SpringBoot创建生产、消费者

&#x1f61c;作 者&#xff1a;是江迪呀✒️本文关键词&#xff1a;RocketMQ、消息队列☀️每日 一言&#xff1a;在你心灰意冷、心烦意乱时也不要停下你的脚步&#xff01; 一、前言 RocketMQ&#xff08;Apache RocketMQ&#xff09;是一种开源的分布式消息中间…

聊一聊微前端框架的选型和实现 | 业务平台

一、项目背景 目前&#xff0c;我们开发维护的项目主要有 6 个&#xff0c;但是分别对应 PC 和 H5 两个端&#xff1a; 如上图所示&#xff0c;我们 6个项目最开始是一个一个进行开发维护的&#xff0c;但是到后期&#xff0c;这几个项目之间有的部分会有业务逻辑不同&#xff…

AUTOSAR从入门到精通-【应用篇】基于 AUTOSAR 的辅助驾驶系统域控制器软件开发(续)

目录 3.4 软件平台 CameraService 模块开发 3.4.1 中继模块 3.4.2 数据管理与调度模块

BUUCTF [SWPU2019]Web1

​ 这是一道sql二次注入题目&#xff0c;但是注入点并不在登录处 注册一个用户然后登录 广告申请处进行sql注入 你会发现过滤了很多关键字 空格#information等等 这里用到了一些绕过技巧 使用 /**/ 代替空格 union/**/select/**/1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,1…

c++11 标准模板(STL)(std::basic_ostringstream)(一)

定义于头文件 <sstream> template< class CharT, class Traits std::char_traits<CharT> > class basic_ostringstream;(C11 前)template< class CharT, class Traits std::char_traits<CharT>, class Allocator std::allo…

LLaMA中ROPE位置编码实现源码解析

1、Attention中q&#xff0c;经下式&#xff0c;生成新的q。m为句长length&#xff0c;d为embedding_dim/head θ i 1 1000 0 2 i d \theta_i\frac{1}{10000^\frac{2i}{d}} θi​10000d2i​1​ 2、LLaMA中RoPE源码 import torchdef precompute_freqs_cis(dim: int, end: i…

uniapp 开发微信小程序使用echart的dataZoom属性缩放功能不生效!bug记录!

在本项目中使用的是这个echart库 在项目中添加了dataZoom配置项但是不生效&#xff0c;突然想到微信小程序代码大小的限制&#xff0c;之前的echarts.js是定制的&#xff0c;有可能没有加dataZoom组件。故重新定制echarts.js。之前用的echarts版本是5.0.0&#xff0c;这次也是…

Web Components

Web Components标准非常重要的一个特性是&#xff0c;它使开发者能够将HTML页面的功能封装为custom elements&#xff08;自定义标签&#xff09;&#xff0c;可以使用CustomElementRegistry来管理自定义标签 <script>//1、创建自定义标签class NewElement extends HTML…

Java基础十八(正则表达式 + 日期时间)

1. 正则表达式 1.1 普通字符 字符描述示例[abc]匹配 […] 中所有字符[hlo] 匹配字符串 "hello world" 中所有的 h l o 字母[^ABC]匹配除了 […] 中所有字符[hlo] 匹配字符串 "hello world" 中除了 h l o 的所有字母[^a-z]匹配除了 […] 中所有字符[hlo] 匹…

Sinkhorn算法

Sinkhorn算法 介绍示例代码 介绍 Sinkhorn算法是一种用于解决最优传输问题的迭代算法。最优传输问题是指在给定两个概率分布 μ \mu μ和 ν \nu ν的情况下&#xff0c;找到一个最优的转移方案&#xff0c;使得从 μ \mu μ到 ν \nu ν的转移成本最小。Sinkhorn算法通过迭代…

C语言static关键字

目录 概述1. 局部变量1.1 普通局部变量1.2 静态局部变量 2.全局变量2.1 普通全局变量2.2 静态全局变量 3.函数4.静态块总结 概述 本文简单介绍一下static关键字在c语言中的应用。 1. 局部变量 1.1 普通局部变量 普通局部变量是在函数内部或代码块内部定义的变量。这些变量只…

Docker file解析

文章目录 简介构建的三步骤Docker执行Dockerfile的大致流程DockerFile常用保留字指令创建第一个Dockerfile镜像的缓存特性 Docker file 解析 简介 Dockerfile是用来构建Docker镜像的文本文件&#xff0c;是由一条条构建镜像所需的指令和参数构成的脚本&#xff0c;记录了镜像构…

单片机IO模拟串口协议

一、前言 嵌入式硬件平台调试中常用的debug方法是看串口打印定位问题&#xff0c;但有时候会遇到单片机没有串口外设或者串口引脚被占用的情况&#xff0c;这时候也可以在代码里操作空闲的IO输出不同个数的脉冲来达到调试的效果&#xff0c;但是要用逻辑分析仪抓线逐个看波形比…

在渗透测试中,探测到6379端口,你打算怎么利用

在渗透测试中&#xff0c;探测到6379端口&#xff0c;你打算怎么利用&#xff1f; 6379端口是Redis数据库的默认端口号。Redis是一种基于键值对的内存数据库&#xff0c;具有高性能和灵活的数据存储和检索功能。6379端口是Redis数据库的默认通信端口&#xff0c;用于连接到Red…