前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >[C++设计模式] 为什么需要设计模式?

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

作者头像
DevKevin
发布于 2024-12-04 00:22:07
发布于 2024-12-04 00:22:07
10500
代码可运行
举报
文章被收录于专栏:Base_CDNKevinBase_CDNKevin
运行总次数:0
代码可运行
在软件开发过程中,开发者经常面临复杂系统的设计与实现。为了应对这一挑战,“设计模式”应运而生。设计模式是一种解决问题的“套路”,它既简化了软件开发的过程,又提高了代码的复用性和可维护性。本文将从设计模式的概念出发,逐步分析软件设计的复杂性以及如何通过面向对象和设计模式来降低复杂性,最终实现高效的代码复用。

什么是设计模式?

“每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。这样,你就能一次又一次地使用该方案而不必做重复劳动”。 ——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 面向对象(封装)

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

结构化设计代码示例:

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#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. 数据和逻辑间的耦合较弱**,但扩展性差**。

面向对象设计代码示例:

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

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#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. 经验复用:将设计经验总结为模式,方便传递和共享。

总结

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

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

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-12-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
机器学习中的统计学——协方差矩阵
在之前的几篇文章中曾讲述过主成分分析的数学模型、几何意义和推导过程(PS:点击即可阅读),这里面就要涉及到协方差矩阵的计算,本文将针对协方差矩阵做一个详细的介绍,其中包括协方差矩阵的定义、数学背景与意义以及计算公式的推导。
统计学家
2019/04/10
2.1K0
机器学习中的统计学——协方差矩阵
概率论基础 - 4 - 协方差、相关系数、协方差矩阵
本文介绍协方差。 协方差 协方差表示的是两个变量的总体的误差,这与只表示一个变量误差的方差不同。 如果两个变量的变化趋势一致,也就是说如果其中一个大于自身的期望值,另外一个也大于自身的期望值,那么两个变量之间的协方差就是正值。 如果两个变量的变化趋势相反,即其中一个大于自身的期望值,另外一个却小于自身的期望值,那么两个变量之间的协方差就是负值。 —— 百度百科 定义 在概率论和统计学中,协方差用于衡量两个变量的总体误差。而方差是协方差的一种特殊情况,即当两个变量是相同的情况。 期望值分别为E[X
为为为什么
2022/08/05
1.4K0
概率论基础 - 4 - 协方差、相关系数、协方差矩阵
随机变量的数学期望
数学期望在解决许多具体问题时非常有效,这些领域包括但不限于医疗、经济、数据分析、社会活动以及彩票抽奖等。以下是一些具体的例子和应用:
用户11315985
2024/10/16
3230
随机变量的数学期望
深度学习-数学基础
目前主要有两种度量模型深度的方式。第一种方式是基于评估架构所需执行的顺序指令的数目。假设我们将模型表示为给定输入后,计算对应输出的流程图,则可以将这张流程图中的最长路径视为模型的深度。另一种是在深度概率模型中使用的方法,它不是将计算图的深度视为模型深度,而是将描述概念彼此如何关联的图的深度视为模型深度。在这种情况下,计算每个概念表示的计算流程图的深度可能比概念本身的图更深。这是因为系统对较简单概念的理解在给出更复杂概念的信息后可以进一步精细化
范中豪
2019/09/10
8420
深度学习-数学基础
常见的机器学习&数据挖掘数学知识点
常见的机器学习&数据挖掘数学知识点之Basis SSE(Sum of Squared Error, 平方误差和) SSE=∑i=1n(Xi−X¯¯¯)2 SAE(Sum of Absolute Error, 绝对误差和) SAE=∑i=1n|Xi−X¯¯¯| SRE(Sum of Relative Error, 相对误差和) SRE=∑i=1nXi−X¯¯¯X¯¯¯ MSE(Mean Squared Error, 均方误差) MSE=∑ni=1(Xi−X¯¯¯)2n RMSE(Root M
机器学习AI算法工程
2018/03/13
1.8K0
常见的机器学习&数据挖掘数学知识点
离散均匀分布的期望和方差(均值和方差的性质)
E [ g ( x ) ] = { ∑ i g ( x i ) p ( x i ) , 离散场合 ∫ − ∞ ∞ g ( x ) p ( x ) d x , 连续场合 E[g(x)]=\begin{cases}\sum\limits_ig(x_i)p(x_i),&\text{离散场合} \\ \\ \int_{-\infty}^\infty{g(x)p(x)\mathrm{d}x},&\text{连续场合}\end{cases} E[g(x)]=⎩⎪⎪⎨⎪⎪⎧​i∑​g(xi​)p(xi​),∫−∞∞​g(x)p(x)dx,​离散场合连续场合​
全栈程序员站长
2022/07/28
1.7K0
协方差详解
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
Steve Wang
2019/10/22
1.3K0
协方差详解
概率论协方差_均值方差协方差公式
  方差的代数意义很简单,两个数的方差就是两个数差值的平方,作为衡量实际问题的数字特征,方差有代表了问题的波动性。
全栈程序员站长
2022/09/20
1.3K0
正交-不相关-独立
本文介绍随机变量中正交、不相关、独立的区别和联系。 概述 三者均是描述随机变量之间关系的概念,看似都可以表示两个随机变量的疏远关系,但定义和约束均有不同。 考察m维随机变量X,Y之间的关系。 定义 正交 定义R(X, Y) = E[XY]为相关函数:若R(X, Y)=0,称X,Y正交 不相关 定义 E[XY] = E[X]E[Y],则X,Y不相关 X,Y的协方差: Cov(X,Y)=E[XY]- E[X]E[Y] 不相关也可以用协方差为0表示 X,Y的相关系数: r(X, Y)
为为为什么
2022/08/05
2K0
正交-不相关-独立
概率论11 协方差与相关系数
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明。谢谢!
Vamei
2018/09/25
1.4K0
概率论11 协方差与相关系数
详解马氏距离中的协方差矩阵计算(超详细)
2.样本方差 方差(Variance)是度量一组数据的离散(波动)程度。方差是各个样本与样本均值的差的平方和的均值,分母除以n-1是为了满足无偏估计:
全栈程序员站长
2022/09/13
3.3K0
详解马氏距离中的协方差矩阵计算(超详细)
数据科学基础(三) 期望和方差
📚 文档目录 随机事件及其概率 随机变量及其分布 期望和方差 大数定律与中心极限定理 数理统计的基本概念 参数估计 假设检验 多维 回归分析和方差分析 降维 3.1 数学期望 3.1.1 离散型数据的数学期望 P(X=x_k)= p_k, 若 \sum^\infty_{k=1}x_kp_k 绝对收敛,则 E(X)=\sum^\infty_{k=1}x_kp_k.注意:数学期望不一定均存在. 3.1.2 连续型数据的数学期望 X 的密度函数为 f(x),\int_{-\infty}^{\infty}xf(x)
Rikka
2022/01/19
7280
图解机器学习 | 降维算法详解
教程地址:http://www.showmeai.tech/tutorials/34
ShowMeAI
2022/03/11
1.3K0
图解机器学习 | 降维算法详解
协方差矩阵-在离散中求“聚合”
方差是均值之上的产物,然后协方差又比方差更近一步,然后带个矩阵的话,可以说明很多变量的关系。
云深无际
2024/11/25
1280
协方差矩阵-在离散中求“聚合”
方差、协方差、协方差矩阵的概念及意义 的理解
想想大学时候,我们学习数学的目的也就是为了考试,从来没有想过它们能解决什么实际问题。但是现在想想,我们真是错了。数学其实就是来自生活。
锦小年
2019/05/29
3.9K0
全网最全总结,有源码!期望、有效值、方差、相关系数、自相关函、互相关函数,还分不清吗?
期望也就是平均值,是一个数值,反应的是随机变量平均取值的情况,期望也叫做加权平均。在信号中代表直流分量。
工程师看海
2022/06/23
1.3K0
全网最全总结,有源码!期望、有效值、方差、相关系数、自相关函、互相关函数,还分不清吗?
使用NumPy介绍期望值,方差和协方差
AiTechYun 编辑:yuxiangyu 基础统计是应用机器学习中的有力工具,它可以更好地理解数据。而且,它也为更先进的线性代数运算和机器学习方法奠定了基础的工具,例如分别协方差矩阵和主成分分析(PCA)。因此,掌握线性代数中基础的统计非常重要。 在本教程中,你会了解基础的统计操作及其原理,和如何使用NumPy实现线性代数的符号和术语。 完成本教程后,你将知道: 期望值,平均数(average)和平均值(mean)是什么,以及如何计算它们。 方差和标准差是多少以及如何计算它们。 协方差,相关性和协方差矩
AiTechYun
2018/03/27
5.6K0
使用NumPy介绍期望值,方差和协方差
斯坦福 CS228 概率图模型中文讲义 二、概率复习
样本空间Ω:随机实验所有结果的集合。 在这里,每个结果ω ∈ Ω可以看作实验结束时真实世界状态的完整描述。
ApacheCN_飞龙
2022/12/01
4410
斯坦福 CS228 概率图模型中文讲义 二、概率复习
机器学习数学基础之概率统计
1、我们借助概率论来解释分析机器学习为什么是这样的,有什么依据,同时反过来借助概率论来推导出更多机器学习算法。很多人说机器学习是老中医,星座学,最主要的原因是机器学习的很多不可解释性,我们应用概率知识可以解释一部分,但还是很多值得我们去解释理解的东西,同时,什么时候机器学习更多的可解释了,反过来,可以用那些理论也可以继续为机器学习的,对人工智能创造推出更多的理论,等到那一天,也许真的能脱离更多的人工智障了。
统计学家
2019/05/06
7990
机器学习数学基础之概率统计
Python协方差矩阵处理脑电数据
在本教程中,我们将介绍传感器协方差计算的基础知识,并构建一个噪声协方差矩阵,该矩阵可用于计算最小范数逆解.
脑机接口社区
2020/06/30
1.2K0
推荐阅读
相关推荐
机器学习中的统计学——协方差矩阵
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验