C++ 设计模式

理解面向对象机制

  • 封装,隐藏内部实现
  • 继承,复用现有代码
  • 多态,改写对象行为

如何解决复杂性

分解:
人们面对复杂性有一个常见的做法:既分而治之,将大问题分解为多个小问题,将复杂问题分解为多个简单问题。
抽象:
更高层次来讲,人们处理复杂性有一个通用的技术,即抽象。由于不能掌握全部的复杂对象,我们选择忽视它的非本质细节,而去处理泛化和理想化了的对象模型。

例 当前代码实现了直线,矩形的绘制,但如果需要进行迭代更新,增加圆形根据设计模式优化相关实现:

class Point {
public:int getX() { return x; };int getY() { return y; };
private:int x;int y;
};class Line {
public:Point start;Point end;Line(const Point& start, const Point& end) {this->start = start;this->end = end;}
};class Rect {
public:Point leftUp;int width;int height;Rect(const Point& leftUp, int width, int height) {this->leftUp = leftUp;this->width = width;this->height = height;}}; class MainForm :public Form
{
public:MainForm(){};
protected:// 当鼠标被按下时触发virtual void OnMouseDown(const MouseEventArgs& e){// 当鼠标被按下时记录第一个点 p1p1.x = e.X;p1.y = e.Y;//...Form::OnMouseDown(e);}// 当鼠标被抬起时出发virtual void OnMouseUp(const MouseEventArgs& e){// 当鼠标抬起后取第二个点 p2p2.x = e.X;p2.y = e.Y;// 如果用户选择是画线if (rdoLine.Checked) {// 就建立线的数据结构Line line(p1, p2);// 将线保存lineVector.push_back(line);}// 如果用户选择画矩形else if (rdoRect.Checked) {// 就建立矩形数据结构int width = abs(p2.x - p1.x);int height = abs(p2.y - p1.y);Rect rect(p1, width, height);// 将矩形保存rectVector.push_back(rect);}//...this->Refresh();Form::OnMouseUp(e); }// 当界面被刷新时触发virtual void OnPaint(const PaintEventArgs& e){//针对直线 - 绘制for (int i = 0; i < lineVector.size(); i++) {e.Graphics.DrawLine(Pens.Red,lineVector[i].start.x,lineVector[i].start.y,lineVector[i].end.x,lineVector[i].end.y);}//针对矩形 - 绘制for (int i = 0; i < rectVector.size(); i++) {e.Graphics.DrawRectangle(Pens.Red,rectVector[i].leftUp,rectVector[i].width,rectVector[i].height);}//...Form::OnPaint(e); }
private:Point p1;Point P2;std::vector<Line> lineVector;std::vector<Rect> rectVector;
};

MainForm是高层模块,它依赖于Line和Rect这些低层模块,这样就使得一个需要稳定的模块依赖于一个不稳定的模块,这样就不太好

通过设计模式修改后的类:

首先设置一个 Shape(形状)的父类,设置一个纯虚函数Draw,和一个虚析构函数。
在这里只有虚析构函数子类通过多态释放的时候,子类的虚析构函数才会被正确的调用到

class Shape {
public:virtual void Draw(const Graphics& g) = 0;virtual ~Shape() {}
};

然后使得类 Line 和 Rect 继承 Shape ,并且重载(Overload) 了父类的 Draw

class Line : public Shape {
public:Point start;Point end;Line(const Point& start, const Point& end) {this->start = start;this->end = end;}//实现自己的Draw, 负责画自己virtual void Draw(const Graphics& g) {g.DrawLine(Pens.Red, start.x, start.y, end.x, end.y);}
};class Rect : public Shape {
public:Point leftUp;int width;int height;Rect(const Point& leftUp, int width, int height) {this->leftUp = leftUp;this->width = width;this->height = height;}//实现自己的Draw, 负责画自己virtual void Draw(const Graphics& g) {g.DrawRectangle(Pens.Red, leftUp, width, height);}
};

对 MainForm 的设计,删除 lineVector 和 rectVector 只需要设计 vector 的 Shape 就可以应对所有形状。

在这里需要注意的是:

vector<Shape*> shapeVector; 

在这里使用指针,如果涉及到多态写这里就要使用指针Shape*如果不用多态会导致对象切割。
完整代码实现:


class Shape {
public:virtual void Draw(const Graphics& g) = 0;virtual ~Shape() {}
};class Point {
public:int x;int y;
};class Line : public Shape {
public:Point start;Point end;Line(const Point& start, const Point& end) {this->start = start;this->end = end;}//实现自己的Draw, 负责画自己virtual void Draw(const Graphics& g) {g.DrawLine(Pens.Red, start.x, start.y, end.x, end.y);}
};class Rect : public Shape {
public:Point leftUp;int width;int height;Rect(const Point& leftUp, int width, int height) {this->leftUp = leftUp;this->width = width;this->height = height;}//实现自己的Draw, 负责画自己virtual void Draw(const Graphics& g) {g.DrawRectangle(Pens.Red, leftUp, width, height);}
};class MainForm : public Form {
private:Point p1;Point p2;//针对所有形状vector<Shape*> shapeVector;public:MainForm() {//...}protected:virtual void OnMouseDown(const MouseEventArgs& e);virtual void OnMouseUp(const MouseEventArgs& e);virtual void OnPaint(const PaintEventArgs& e);
}void MainForm::OnMouseDown(const MouseEventArgs& e) {p1.x = e.X;p1.y = e.Y;//...Form::OnMouseDown(e);
}void MainForm::OnMouseUp(const MouseEventArgs& e) {p2.x = e.X;p2.y = e.Y;if (rdoLine.Checked) {// 这里需要在 MainForm 中的析构函数中去释放这部分内存shapeVector.push_back(new Line(p1, p2));}else if (rdoRect.Checked) {int width = abs(p2.x - p1.x);int height = abs(p2.y - p1.y);shapeVector.push_back(new Rect(p1, width, height));}//...this->Refresh();Form::OnMouseUp(e);
}void MainForm::OnPaint(const PaintEventArgs& e) {//针对所有形状for (int i = 0; i < shapeVector.size(); i++) {shapeVector[i]->Draw(e.Graphics); //多态调用,各负其责}//...Form::OnPaint(e);
}

原本的代码实现增加圆形:

新增圆形类:


class Circle {
public:Point start;Point end;Circle(const Point& start, const Point& end) {this->start = start;this->end = end;}
};

然后在 MainForm 中添加圆形类容器 std::vector circleVector;
在 OnMouseUp 中保存圆形类数据在容器中:

else if (rdoCircle.Checked){ Circle rect(p1, p2);circleVector.push_back(rect);}

最终在绘制时 OnPaint 添加针对圆形的绘制:

		// 针对圆形 - 绘制for (int i = 0; i < circleVector.size(); i++) {e.Graphics.DrawLine(Pens.Red,circleVector[i].start.x,circleVector[i].start.y,circleVector[i].end.x,circleVector[i].end.y);}

通过设计模式新增加的类:

修改,以添加圆这个类,继承 Shape

class Circle : public Shape {
public:Point start;Point end;Circle(const Point& start, const Point& end) {this->start = start;this->end = end;}
};

然后在 OnMouseUp 中添加判断增加即可

	else if (rdoCircle.Checked){Circle rect(p1, p2);shapeVector.push_back(new Circle(p1, p2));}

最终在 OnPaint 会根据多态指针去调用。
MainForm依赖于Shape,Line和Rect也都依赖于Shape。这个Shape就是抽象。抽象不应该依赖于实现细节,也就是抽象不应该去依赖于子类,因为抽象是稳定的,不应该去依赖一个变化的东西
这是面向对象 依赖倒置原则(DIP) 的体现,既:

  • 高层模块(稳定)不应该依赖于低层模块(变化),二者都应该依赖于抽象(稳定) 。
  • 抽象(稳定)不应该依赖于实现细节(变化) ,实现细节应该依赖于抽象(稳定)。

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

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

相关文章

Linux学习笔记——磁盘管理命令

lsblk(list block devices):其功能是查看系统的磁盘使用情况 df(disk free):列出文件系统的整体磁盘使用量 du(disk used):检查磁盘空间使用量 fdisk:用于磁盘分区&#xff08;创建分区&#xff09; mkfs:创建并构建一个文件系统&#xff08;格式化&#xff09; mount:挂在…

QT使用eigen

QT使用eigen 1. 下载eigen https://eigen.tuxfamily.org/index.php?titleMain_Page#Download 下载后解压 2. QT引入eigen eigen源码好像只有头文件&#xff0c;因此只需要引入头文件就好了 qt新建项目后。修改pro文件. INCLUDEPATH E:\222078\qt\eigen-3.4.0\eigen-3.…

国内优秀的FPGA设计公司主要分布在哪些城市?

近年来&#xff0c;国内FPGA行业发展迅速&#xff0c;随着5G通信、人工智能、大数据等新兴技术的崛起&#xff0c;FPGA设计企业的需求也迎来了爆发式增长。很多技术人才在求职时都会考虑城市的行业分布和发展潜力。因此&#xff0c;国内优秀的FPGA设计公司主要分布在哪些城市&a…

Linux——rzsz工具

rzsz这个工具用于 windows 机器和远端的 Linux 机器通过 XShell 传输文件. 安装完毕之后可以通过拖拽的方式将文件上传过去. 安装rzsz工具 rz&#xff1a;从Windows机器上传到远程Linux机器&#xff08;或者直接把文件托进Xshell中&#xff09; sz&#xff1a;将文件从Linux远…

【linux网络(3)】应用层HTTP协议详解

目录 前言1. 序列化和反序列化2. 认识URL3. 对网络中资源的理解4. HTTP的报文格式5. HTTP方法详解6. HTTP的状态码和header7. HTTP会话管理8. 总结以及拓展 前言 在理解了网络套接字编程后, 后续的文章会从应用层到链路层, 详解的讲解每一层的协议都做了些什么工作, 并且会拆分…

利用JSON数据类型优化关系型数据库设计

利用JSON数据类型优化关系型数据库设计 前言 在关系型数据库中&#xff0c;传统的结构化存储方式要求预先定义好所有的列及其数据类型。 然而&#xff0c;随着业务的发展&#xff0c;这种设计可能会显得不够灵活&#xff0c;尤其是在需要扩展单个列的描述功能时。 JSON数据…

cursor ide配置远程ssh qt c++开发环境过程记录

cursor是啥就不介绍了&#xff0c;好像是目前最好用的ai ide&#xff0c;下面主要是配置远程ssh连接linux机器进行qt5 c程序运行的配置过程记录。 一、c_cpp_properties.json 在项目根目录的.vscode目录里面新建c_cpp_properties.json文件&#xff0c;根据你的实际情况配置该文…

npm:升级自身时报错:EBADENGINE

具体报错信息如下&#xff1a; 1.原因分析 npm和当前的node版本不兼容。 // 当前实际版本: Actual: {"npm":"10.2.4","node":"v20.11.0"}可以通过官网文档查看与自己 node 版本 兼容的是哪一版本的npm&#xff0c;相对应进行更新即可…

Excel中LOOKUP函数的使用

文章目录 VLOOKUP&#xff08;垂直查找&#xff09;&#xff1a;HLOOKUP&#xff08;水平查找&#xff09;&#xff1a;LOOKUP&#xff08;基础查找&#xff09;&#xff1a;XLOOKUP&#xff08;高级查找&#xff0c;较新版本Excel提供&#xff09;&#xff1a; 在Excel中&…

Verilog中if语句和case语句综合出的电路区别

区别是 if else 的逻辑判断有优先级&#xff0c;最内层的 if 的优先级最高&#xff0c;case 的逻辑判断是并列的。 每个 if else 综合出来的电路是一个 2 选 1 选通器。当信号有明显优先级时使用该语句&#xff0c;但是 if 嵌套太多的话会导致路径延时过大&#xff0c;降低运行…

【C语言常见概念详解】

目录 -----------------------------------------begin------------------------------------- 什么是C语言&#xff1a; 1. 基本数据类型 2. 变量与常量 3. 运算符与表达式 4. 控制结构 5. 函数 6. 指针 7. 数组与字符串 8. 结构体与联合体 9. 文件操作 结语 ----…

CE11.【C++ Cont】练习题组12(结构体专题)

目录 1.P5742【深基7.例11】评等级 题目 代码 提交结果 2.B2125 最高分数的学生姓名 题目 代码 方法1 提交结果 方法2:在方法1基础上改进 提交结果 ​编辑 方法3:先排序后选,较麻烦 提交结果 ​编辑 3.[NOIP2007 普及组] 奖学金 题目 错误代码 提交结果 调试…

开源项目Umami网站统计MySQL8.0版本Docker+Linux安装部署教程

Umami是什么&#xff1f; Umami是一个开源项目&#xff0c;简单、快速、专注用户隐私的网站统计项目。 下面来介绍如何本地安装部署Umami项目&#xff0c;进行你的网站统计接入。特别对于首次使用docker的萌新有非常好的指导、参考和帮助作用。 Umami的github和docker镜像地…

Nginx开发01:基础配置

一、下载和启动 1.下载、使用命令行启动&#xff1a;Web开发&#xff1a;web服务器-Nginx的基础介绍&#xff08;含AI文稿&#xff09;_nginx作为web服务器,可以承担哪些基本任务-CSDN博客 注意&#xff1a;我配置的端口是81 2.测试连接是否正常 访问Welcome to nginx! 如果…

20.Word:小谢-病毒知识的科普文章❗【38】

目录 题目​ NO1.2.3文档格式 NO4.5 NO6.7目录/图表目录/书目 NO8.9.10 NO11索引 NO12.13.14 每一步操作完&#xff0c;确定之后记得保存最后所有操作完记得再次删除空行 题目 NO1.2.3文档格式 样式的应用 选中应用段落段落→开始→选择→→检查→应用一个一个应用ctr…

【Python】第五弹---深入理解函数:从基础到进阶的全面解析

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】【MySQL】【Python】 目录 1、函数 1.1、函数是什么 1.2、语法格式 1.3、函数参数 1.4、函数返回值 1.5、变量作用域 1.6、函数…

从AD的原理图自动提取引脚网络的小工具

这里跟大家分享一个我自己写的小软件&#xff0c;实现从AD的原理图里自动找出网络名称和引脚的对应。存成文本方便后续做表格或是使用简单行列编辑生成引脚约束文件&#xff08;如.XDC .UCF .TCL等&#xff09;。 我们在FPGA设计中需要引脚锁定文件&#xff0c;就是指示TOP层…

MySQL--》深度解析InnoDB引擎的存储与事务机制

目录 InnoDB架构 事务原理 MVCC InnoDB架构 从MySQL5.5版本开始默认使用InnoDB存储引擎&#xff0c;它擅长进行事务处理&#xff0c;具有崩溃恢复的特性&#xff0c;在日常开发中使用非常广泛&#xff0c;其逻辑存储结构图如下所示&#xff0c; 下面是InnoDB架构图&#xf…

30289_SC65XX功能机MMI开发笔记(ums9117)

建立窗口步骤&#xff1a; 引入图片资源 放入图片 然后跑make pprj new job8 可能会有bug,宏定义 还会有开关灯报错&#xff0c;看命令行注释掉 接着把ture改成false 然后命令行new一遍&#xff0c;编译一遍没报错后 把编译器的win文件删掉&#xff0c; 再跑一遍虚拟机命令行…

深入学习Java的线程的生命周期

线程的状态/生命周期 五种状态 这是从 操作系统 层面来描述的 【初始状态】仅是在语言层面创建了线程对象&#xff0c;还未与操作系统线程关联【可运行状态】&#xff08;就绪状态&#xff09;指该线程已经被创建&#xff08;与操作系统线程关联&#xff09;&#xff0c;可以由…