[C++设计模式] 为什么需要设计模式?


文章目录

  • 什么是设计模式?
  • 为什么需要设计模式?
  • GOF 设计模式
  • 再次理解面向对象
  • 软件设计固有的复杂性
  • 软件设计复杂性的根本原因
  • 如何解决复杂性?
      • 分解
      • 抽象
  • 结构化 VS 面向对象(封装)
      • 结构化设计代码示例:
      • 面向对象设计代码示例:
      • 对比总结
  • 软件设计的目标:复用!
  • 总结

在软件开发过程中,开发者经常面临复杂系统的设计与实现。为了应对这一挑战,“设计模式”应运而生。设计模式是一种解决问题的“套路”,它既简化了软件开发的过程,又提高了代码的复用性和可维护性。本文将从设计模式的概念出发,逐步分析软件设计的复杂性以及如何通过面向对象和设计模式来降低复杂性,最终实现高效的代码复用。


什么是设计模式?

“每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复劳动”。

——Christopher Alexander

设计模式(Design Pattern)是对软件开发过程中反复出现的设计问题所提供的通用解决方案。它不是代码,而是经过验证的“最佳实践”,以一种结构化的方式记录了解决问题的思想。

  • 核心理念:通过设计模式,开发者无需重复发明轮子,可以直接借用已有的设计经验。(复用!!!)
  • 分类:设计模式主要分为三大类:
    • 创建型模式:解决对象创建相关的问题,如单例模式(Singleton)、工厂模式(Factory Method)。
    • 结构型模式:关注对象间的组合和结构,如适配器模式(Adapter)、装饰器模式(Decorator)。
    • 行为型模式:处理对象间的职责分配和通信,如观察者模式(Observer)、状态模式(State)。

为什么需要设计模式?

  1. 解决常见问题
    软件开发中,许多问题是重复出现的。设计模式总结了这些问题的通用解决方案,减少了开发中的试错。
  2. 提升代码质量
    使用设计模式可以使代码更加易读、灵活和扩展性强,方便后期维护。
  3. 提高开发效率
    借助设计模式,开发者可以专注于业务逻辑,而不是基础框架设计,从而加快开发进程。

GOF 设计模式

GOF(Gang of Four,四人帮)指的是设计模式的奠基者——Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides。这四位作者在 1994 年出版了经典著作 《Design Patterns: Elements of Reusable Object-Oriented Software》,首次系统化地总结了 23 种面向对象设计模式。

  • 核心观点
    1. 软件设计应遵循面向对象的基本原则(如单一职责、开放封闭等)。
    2. 模式之间可以组合使用,实现更强大的功能。

再次理解面向对象

对于C++程序猿来说,需要将底层思维与抽象思维都进行分析。

**底层思维:**向下,如何把握机器底层从微观理解对象构造

• 语言构造

• 编译转换

• 内存模型

• 运行时机制

**抽象思维:**向上,如何将我们的周围世界抽象为程序代码

• 面向对象

• 组件封装

• 设计模式

• 架构模式

良好的设计模式可以根据语言在底层的逻辑和在语言逻辑层面的设计进行相结合,以此来提高代码质量和开发效率。

所以,在讨论设计模式之前,必须再次理解面向对象的核心思想:

  1. 封装:将数据和操作封装到对象中,隐藏内部实现。
  2. 继承:通过继承重用已有的代码,减少重复。
  3. 多态:通过统一的接口实现不同的行为。

面向对象通过以上三大特性为设计模式奠定了基础,是理解设计模式的关键。


软件设计固有的复杂性

建筑商从来不会去想给一栋已建好的100层高的楼房底下再新修一个小地下室——这样做花费极大而且注定要败。然而令人惊奇的是,软件系统的用户在要求作出类似改变时却不会仔细考虑,而且他们认为这只是需要简单编程的事。

——Object-Oriented Analysis and Design with Applications

复杂性是软件设计中的一大挑战。软件的复杂性源于多个维度:

  1. 规模增长:软件系统的功能越多,模块之间的关系越复杂。
  2. 需求变化:用户需求总是在变化,如何适应这些变化是设计的难题。
  3. 技术多样性:系统可能需要集成多种技术,增加了设计和实现的难度。

软件设计复杂性的根本原因

  1. 模块之间的耦合:模块间的强耦合导致修改一个模块可能会影响其他模块。
  2. 缺乏抽象:没有抽象层,导致系统难以扩展和维护。
  3. 过度复杂的设计:为了应对变化,设计变得过于复杂,反而不利于实现。
  4. 跨平台的支持:多个版本设计。

原因在后期会进行功能的添加或者修改,但是在刚开始程序进行设计的时候的代码逻辑无法使得在基础上直接添加新功能,所以只能在原来的上面进行强行添加,也就是if - else屎山设计的由来~


如何解决复杂性?

分解

人们面对复杂性有一个常见的做法:即分而治之,将大问题分解为多个小问题,将复杂问题分解为多个简单问题。

抽象

更高层次来讲,人们处理复杂性有一个通用的技术,即抽象。由于不能掌握全部的复杂对象,我们选择忽视它的非本质细节,而去处理泛化和理想化了的对象模型。

  1. 遵循设计原则
    • 单一职责原则(SRP):每个模块只做一件事。
    • 开放封闭原则(OCP):对扩展开放,对修改封闭。
    • 依赖倒置原则(DIP):高层模块不应该依赖于低层模块,二者都应该依赖于抽象。
  2. 采用面向对象思想:通过封装、继承和多态降低耦合、增强复用性。
  3. 使用设计模式:通过复用成熟的设计经验,将复杂的设计问题分解为可管理的模块。

结构化 VS 面向对象(封装)

我们以一个现实问题为例:计算矩形的面积和周长

结构化设计代码示例:

在结构化设计中,数据和功能是分离的。函数主要以操作为中心,而数据仅作为参数传递。

#include <iostream>
using namespace std;// 数据结构:存储矩形的长度和宽度
struct Rectangle {double length;double width;
};// 函数:计算面积
double calculateArea(const Rectangle& rect) {return rect.length * rect.width;
}// 函数:计算周长
double calculatePerimeter(const Rectangle& rect) {return 2 * (rect.length + rect.width);
}int main() {// 创建一个矩形Rectangle rect = {5.0, 3.0};// 调用函数进行操作cout << "Area: " << calculateArea(rect) << endl;cout << "Perimeter: " << calculatePerimeter(rect) << endl;return 0;
}

特点

  1. 数据 (Rectangle) 和操作(calculateAreacalculatePerimeter)是独立的。
  2. 如果新增功能(如计算对角线长度),需要添加新的函数,操作逻辑分散。
  3. 数据和逻辑间的耦合较弱**,但扩展性差**。

面向对象设计代码示例:

在面向对象设计中,数据和功能封装在一个类中,操作直接基于对象调用。

#include <iostream>
#include <cmath> // 为了计算对角线
using namespace std;// 面向对象设计:定义矩形类
class Rectangle {
private:double length;double width;public:// 构造函数Rectangle(double l, double w) : length(l), width(w) {}// 方法:计算面积double calculateArea() const {return length * width;}// 方法:计算周长double calculatePerimeter() const {return 2 * (length + width);}// 方法:计算对角线长度double calculateDiagonal() const {return sqrt(length * length + width * width);}// 设置长度和宽度void setDimensions(double l, double w) {length = l;width = w;}// 显示矩形信息void display() const {cout << "Rectangle [Length: " << length << ", Width: " << width << "]" << endl;}
};int main() {// 创建一个矩形对象Rectangle rect(5.0, 3.0);// 调用对象方法进行操作rect.display();cout << "Area: " << rect.calculateArea() << endl;cout << "Perimeter: " << rect.calculatePerimeter() << endl;cout << "Diagonal: " << rect.calculateDiagonal() << endl;return 0;
}

特点

  1. 数据 (length, width) 和操作(如 calculateArea, calculatePerimeter)被封装在类中。
  2. 新增功能(如计算对角线长度)可以通过类方法轻松扩展。
  3. 高度封装,使对象的行为与数据直接相关,提高了模块化和复用性。

对比总结

特点结构化设计面向对象设计
数据与操作的关系数据和操作分离,函数作用于独立的数据数据和操作封装在一个对象内,操作与数据密切相关
扩展性新增功能需要新增函数,整体设计不易扩展新增功能只需添加类方法,设计更具扩展性
维护性操作逻辑分散在多个函数中,难以维护操作封装在类中,逻辑清晰,易于维护
代码复用性数据结构和操作复用性较差类和方法具有较高的复用性
示例中的实现复杂度数据操作较为简单,但难以应对复杂系统初期实现略复杂,但适合复杂系统

通过以上对比,可以直观地看出,结构化设计更适合小型、单一功能的程序,而面向对象设计在解决复杂系统时更具优势。

结构化设计面向对象设计
以功能为中心,数据为次要目标以对象为中心,功能服务于对象
数据和操作分离数据和操作封装在对象内部
难以复用和扩展具有较好的复用性和扩展性

面向对象设计通过关注对象及其交互,解决了结构化设计中模块间强耦合的问题,为设计模式的实施提供了基础。


软件设计的目标:复用!

复用是软件设计的核心目标之一。设计模式通过以下方式实现复用:

  1. 代码复用:减少重复代码,提高开发效率。
  2. 设计复用:使用模式模板应对类似的设计需求。
  3. 经验复用:将设计经验总结为模式,方便传递和共享。

总结

设计模式是应对软件复杂性的重要工具,是面向对象思想的升华。通过学习和实践设计模式,开发者可以设计出灵活、可扩展且易维护的系统。对于初学者来说,了解设计模式的基本分类和使用场景,是深入学习的第一步。


关于具体不同设计模式是如何设计的,请阅读笔者该专栏其他文章。

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

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

相关文章

机器学习:精确率与召回率的权衡

高精度意味着如果诊断得了那种罕见病的病人&#xff0c;可能病人确实有&#xff0c;这是一个准确的诊断&#xff0c;高召回率意味着如果有一个还有这种罕见疾病的病人&#xff0c;也许算法会正确的识别他们确实患有这种疾病&#xff0c;事实中&#xff0c;在精确与召回之间往往…

03-13、SpringCloud Alibaba第十三章,升级篇,服务降级、熔断和限流Sentinel

SpringCloud Alibaba第十三章&#xff0c;升级篇&#xff0c;服务降级、熔断和限流Sentinel 一、Sentinel概述 1、Sentinel是什么 随着微服务的流行&#xff0c;服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点&#xff0c;从流量控制、熔断降级、系统负载保…

基于vite6+ vue3 + electron@33 实现的 局域网内互传文件的桌面软件

目录 项目介绍项目部分截图介绍下基础项目搭建先搭建一个vite 前端项目 再安装 electron 相关依赖依赖安装失败解决方案修改 vite配置文件和 ts 配置文件修改packjsonts相关配置项目结构介绍 项目介绍 前端 基于 vue3 ts windicss 后端 就是node 层 项目地址&#xff1a; h…

安装MySQL 5.7 亲测有效

前言&#xff1a;本文是笔者在安装MySQL5.7时根据另一位博主大大的安装教程基础上做了一些修改而成 首先在这里表示对博主大大的感谢 下面附博主大大地址 下面的步骤言简意赅 跟着做就不会出错 希望各位读者耐下心来 慢慢解决安装中出现的问题~MySQL 5.7 安装教程&#xff08;全…

CSS函数

目录 一、背景 二、函数的概念 1. var()函数 2、calc()函数 三、总结 一、背景 今天我们就来说一说&#xff0c;常用的两个css自定义属性&#xff0c;也称为css函数。本文中就成为css函数。先来看一下官方对其的定义。 自定义属性&#xff08;有时候也被称作CSS 变量或者级…

6.824/6.5840 Lab 1: MapReduce

宁静的夏天 天空中繁星点点 心里头有些思念 思念着你的脸 ——宁夏 完整代码见&#xff1a; https://github.com/SnowLegend-star/6.824 由于这个lab整体难度实在不小&#xff0c;故考虑再三还是决定留下代码仅供参考 6.824的强度早有耳闻&#xff0c;我终于也是到了挑战这座高…

MongoDB集群分片安装部署手册

文章目录 一、集群规划1.1 集群安装规划1.2 端口规划1.3 目录创建 二、mongodb安装&#xff08;三台均需要操作&#xff09;2.1 下载、解压2.2 配置环境变量 三、mongodb组件配置3.1 配置config server的副本集3.1.1 config配置文件3.1.2 config server启动3.1.3 初始化config …

一种多功能调试工具设计方案开源

一种多功能调试工具设计方案开源 设计初衷设计方案具体实现HUB芯片采用沁恒微CH339W。TF卡功能网口功能SPI功能IIC功能JTAG功能下行USB接口 安路FPGA烧录器功能Xilinx FPGA烧录器功能Jlink OB功能串口功能RS232串口RS485和RS422串口自适应接口 CAN功能烧录器功能 目前进度后续计…

三维测量与建模笔记 - 5.3 光束法平差(Bundle Adjustment)

此篇笔记尚未理解&#xff0c;先做笔记。 如上图&#xff0c;在不同位姿下对同一个物体采集到了一系列图像&#xff0c; 例子中有四张图片。物体上某点M&#xff0c;在四幅图像上都能找到其观测点。 上式中的f函数是对使用做投影得到的估计点位置。求解这个方程有几种方法&…

力扣hot100道【贪心算法后续解题方法心得】(三)

力扣hot100道【贪心算法后续解题方法心得】 十四、贪心算法关键解题思路1、买卖股票的最佳时机2、跳跃游戏3、跳跃游戏 | |4、划分字母区间 十五、动态规划什么是动态规划&#xff1f;关键解题思路和步骤1、打家劫舍2、01背包问题3、完全平方式4、零钱兑换5、单词拆分6、最长递…

ElasticSearch学习篇19_《检索技术核心20讲》搜推广系统设计思想

目录 主要是包含搜推广系统的基本模块简单介绍&#xff0c;另有一些流程、设计思想的分析。 搜索引擎 基本模块检索流程 查询分析查询纠错 广告引擎 基于标签倒排索引召回基于向量ANN检索召回打分机制&#xff1a;非精确打分精准深度学习模型打分索引精简&#xff1a;必要的…

Ambrus 游戏工作室将应对气候变暖与游戏变现完美结合

当 Ambrus Studio 创始人兼 CEO Johnson Yeh 计划打造他称之为“第一款伟大的 Web3 游戏”时&#xff0c;他设立了两个关键目标&#xff1a;游戏需要在传统大型工作室忽视的市场中盈利&#xff0c;以及它需要具备超越娱乐的意义。 在 Sui 的帮助下&#xff0c;Johnson 和他的团…

KAN-Transfomer——基于新型神经网络KAN的时间序列预测

1.数据集介绍 ETT(电变压器温度)&#xff1a;由两个小时级数据集&#xff08;ETTh&#xff09;和两个 15 分钟级数据集&#xff08;ETTm&#xff09;组成。它们中的每一个都包含 2016 年 7 月至 2018 年 7 月的七种石油和电力变压器的负载特征。 traffic(交通) &#xff1a;描…

UEFI Spec 学习笔记---3 - Boot Manager(3)

3.2 Boot Manager Policy Protocol EFI_BOOT_MANAGER_POLICY_PROTOCOL----EFI应用程序使用该协议请求UEFI引导管理器使用平台策略连接设备。 typedef struct _EFI_BOOT_MANAGER_POLICY_PROTOCOL EFI_BOOT_MANAGER_POLICY_PROTOCOL; struct _EFI_BOOT_MANAGER_POLICY_PROTOCOL…

wordpress网站首页底部栏显示网站备案信息

一、页脚文件footer.php 例如&#xff0c;wordpress主题使用的是simple-life主题&#xff0c;服务器IP为192.168.68.89,在wordpress主题文件中有个页脚文件footer.php&#xff0c;这是一个包含网站页脚代码的文件。 footer.php 路径如下&#xff1a; /www/wwwroot/192.168.68…

QT实战-qt各种菜单样式实现

本文主要介绍了qt普通菜单样式、带选中样式、带子菜单样式、超过一屏幕菜单样式、自定义带有滚动条的菜单样式&#xff0c; 先上图如下&#xff1a; 1.普通菜单样式 代码&#xff1a; m_pmenu new QMenu(this);m_pmenu->setObjectName("quoteListMenu"); qss文…

数据结构实训——查找

声明&#xff1a; 以下是我们学校在学习数据结构时进行的实训&#xff0c;如涉及侵权马上删除文章 声明&#xff1a;本文主要用作技术分享&#xff0c;所有内容仅供参考。任何使用或依赖于本文信息所造成的法律后果均与本人无关。请读者自行判断风险&#xff0c;并遵循相关法…

指针(上)

目录 内存和地址 指针变量和地址 取地址&#xff08;&&#xff09; 解引用&#xff08;*&#xff09; 大小 类型 意义 const修饰 修饰变量 修饰指针 指针运算 指针- 整数 指针-指针 指针的关系运算 野指针 概念 成因 避免 assert断言 指针的使用 strl…

13TB的StarRocks大数据库迁移过程

公司有一套StarRocks的大数据库在大股东的腾讯云环境中&#xff0c;通过腾讯云的对等连接打通&#xff0c;通过dolphinscheduler调度datax离线抽取数据和SQL计算汇总&#xff0c;还有在大股东的特有的Flink集群环境&#xff0c;该环境开发了flink开发程序包部署&#xff0c;实时…

ARP表、MAC表、路由表的区别和各自作用

文章目录 ARP表、MAC表、路由表的区别和各自作用同一网络内:ARP表request - 请求reply - 响应 MAC地址在同一网络内,交换机如何工作? 不同网络路由表不同网络通信流程PC1到路由器路由器到PC2流程图 简短总结 ARP表、MAC表、路由表的区别和各自作用 拓扑图如下: 同一网络内:…