C++设计模式-创建型设计模式

设计模式

设计模式是什么

设计模式是指在软件开发中,经过验证的,用于解决在特定环境下,重复出现的,特定问题的解决方案;其实就是解决问题的固定套路。但是要慎用设计模式,有一定的工程代码量之后用它比较好。

设计模式怎么来的

满足设计原则后,然后慢慢迭代出来的

设计模式解决了什么问题?

前提:具体需求既有稳定点,又有变化点。全是稳定点,或者全是变化点(游戏开发,脚本语言)都不要用。我们期望修改少量的代码,就可以适应需求的变化。
一个比喻:整洁的房间,好动猫,怎么保证房间的整洁?
就是把猫关在一个笼子里,这个猫就是我们需求,把猫关在笼子中,就是设计模式。

设计模式的基础

面向对象的思想

封装:主要是隐藏实现细节,实现模块化
继承:无需修改原有类的情况下通过继承实现对功能的扩展
多态:静态多态:函数重载。
多态:动态多态:继承中虚函数重写。
这个就是多态:
在这里插入图片描述

设计原则

依赖倒置

  • 高层模块不应该依赖低层模块,两者都应该依赖抽象;
  • 抽象不应该依赖具体实现,具体实现应该依赖于抽象;
    在这里插入图片描述
  • 自动驾驶系统公司是高层,汽车生产厂商为低层,它们不应该互相依赖,一方变动另一方也会跟着变动;而应该抽象一个自动驾驶行业标准,高层和低层都依赖它;这样以来就解耦了两方的变动;自动驾驶系统、汽车生产厂商都是具体实现,它们应该都依赖自动驾驶行业标准(抽象);接口的使用者不要依赖具体的实现。要依赖具体的接口。

开放封闭
主要针对封装和多态
一个类应该对扩展(组合和继承)开放,对修改关闭;

面向接口
主要针对封装

  • 不将变量类型声明为某个特定的具体类,而是声明为某个接口;
  • 客户程序无需获知对象的具体类型,只需要知道对象所具有的接口;
  • 减少系统中各部分的依赖关系,从而实现“高内聚、松耦合”的类型设计方案;

封装变化点
主要针对封装和多态
将稳定点和变化点分离,扩展修改变化点;让稳定点和变化点的实现层次分离;

单一职责
一个类应该仅有一个引起它变化的原因;

里氏替换
主要针对多态
子类型必须能够替换掉它的父类型;主要出现在子类覆盖父类实现,原来使用父类型的程序可能出现错误;覆盖了父类方法却没有实现父类方法的职责;

接口隔离

  • 不应该强迫客户依赖于它们不用的方法;
  • 一般用于处理一个类拥有比较多的接口,而这些接口涉及到很多职责;
  • 客户端不应该依赖它不需要的接口。一个类对另一个类的依赖应该建立在最小的接口上。

组合优于继承
继承耦合度高,组合耦合度低;

最小知道原则
对这个类知道的越少越好,只要知道接口就行

如何学习设计模式

明确目的

新手:在现有设计模式的基础上,扩展代码
老手:做功能抽象时,如何选择设计模式

学习步骤

该设计模式解决了什么问题?

  • 稳定点
  • 变化点

该设计模式的代码结构是什么?

符合哪些设计原则?

如何在上面扩展代码?

该设计模式有哪些典型应用场景?

  • 联系工作场景
  • 开源框架

设计模式-模块方法

第一个设计模式:模块方法

定义

定义一个操作中的算法的骨架 ,而将一些步骤延迟到子类中。Template Method使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。 ——《 设计模式》 GoF
背景:
某个品牌动物园,有一套固定的表演流程,但是其中有若干个表演子流程可创新替换,以尝试迭代更新表演流程;这个和上面定义差不多。

解决问题

解决问题:稳定点就是算法骨架,变化点就是重定义该算法的某些特定步骤,子流程需要变化。

代码结构

  • 基类中有骨架流程接口,上面代码中就是那个show()函数中,有show1(),show2()
  • 所有子流程对子类开放并且是虚函数(protected)
  • 多态使用方式,用基类指针new一个扩展类

有虚函数,并且被protected方法修饰,90%是我们的模板方法。

我们来实现这个某个品牌动物园,有一套固定的表演流程,但是其中有若干个表演子流程可创新替换,以尝试迭代更新表演流程;这个和上面定义差不多。

来看一个代码对比,第一个代码是没有设计模式的,第二个代码是用了设计模式的:

#include <iostream>
using namespace std;// 设计原则
// 1. 设计原则演变过来的  2.符合设计原则的代码,只需要修改少量代码就可以演变成设计模式
// 接口隔离原则  1. 类封装 权限限定词(private,public....)来实现 2. 类与类依赖 接口(依赖注入)
// 最小知道原则
// 破坏了哪些设计原则:
// 单一职责(变化方向) 封装
// 开闭原则 对扩展开放 对修改关闭
// 扩展方式:继承,多态组合
class ZooShow {
public:ZooShow(int type = 1) : _type(type) {}public:void Show() {if (Show0())PlayGame();Show1();Show2();Show3();}private://不能让其选择show0(),show1(),show2().void PlayGame() {cout << "after Show0, then play game" << endl;}bool Show0() {if (_type == 1) {// return true;} else if (_type == 2 ) {//  ...} else if (_type == 3) {}cout << _type << " show0" << endl;return true;}void Show1() {if (_type == 1) {cout << _type << " Show1" << endl;} else if (_type == 2) {cout << _type << " Show1" << endl;} else if (_type == 3) {}}void Show2() {if (_type == 20) {}cout << "base Show2" << endl;}void Show3() {if (_type == 1) {cout << _type << " Show1" << endl;} else if (_type == 2) {cout << _type << " Show1" << endl;}}
private:int _type;
};int main () {ZooShow *zs = new ZooShow(1);zs->Show();return 0;
}

用了设计模式

#include <iostream>
using namespace std;// 开闭
class ZooShow {
public:void Show() {// 如果子表演流程没有超时的话,进行一个中场游戏环节;如果超时,直接进入下一个子表演流程if (Show0())PlayGame();Show1();Show2();Show3();}private:void PlayGame() {cout << "after Show0, then play game" << endl;}bool expired;// 对其他用户关闭,但是子类开放的
protected:virtual bool Show0() {cout << "show0" << endl;if (! expired) {return true;}return false;}virtual void Show2() {cout << "show2" << endl;}virtual void Show1() {}virtual void Show3() {}
};// 框架
// 模板方法模式
class ZooShowEx10 : public ZooShow {
protected:virtual void Show0() {if (! expired) {return true;}return false;}
}class ZooShowEx1 : public ZooShow {
protected:virtual bool Show0() {cout << "ZooShowEx1 show0" << endl;if (! expired) { // 里氏替换return true;}return false;}virtual void Show2(){cout << "show3" << endl;}
};class ZooShowEx2 : public ZooShow {
protected:virtual void Show1(){cout << "show1" << endl;}virtual void Show2(){cout << "show3" << endl;}
};class ZooShowEx3 : public ZooShow {
protected:virtual void Show1(){cout << "show1" << endl;}virtual void Show3(){cout << "show3" << endl;}virtual void Show4() {//}
};
/*
*/
int main () {ZooShow *zs = new ZooShowEx10; // 晚绑定还是早绑定// ZooShow *zs1 = new ZooShowEx1;// ZooShow *zs2 = new ZooShowEx2;zs->Show();return 0;
}

符合那些原则

单一职责
开闭
依赖倒置

  • 子类扩展时,需要依赖基类的虚函数实现
  • 使用者只依赖接口

封装变化点(protected)
接口隔离
最小知道原则
对于用户我们只需要知道show(),然后zs->show()就可以了。

如何扩展

也就是这一块

class ZooShowEx1 : public ZooShow {
protected:virtual bool Show0() {cout << "ZooShowEx1 show0" << endl;if (! expired) { // 里氏替换return true;}return false;}virtual void Show2(){cout << "show3" << endl;}
};

实现子类继承基类,复写子流程
通过多态调用方式使用

总结

要点

  • 最常用的设计模式,子类可以复写父类子流程,使父类的骨架流程丰富;
  • 反向控制流程的典型应用;
  • 父类 protected 保护子类需要复写的子流程;这样子类的子流程
  • 只能父类来调用;

本质
通过固定算法骨架来约束子类的行为;
结构图
在这里插入图片描述

设计模式-观察者模式

定义

对象间的一种一对多(变化)的依赖关系,以便当一个对象(Subject)的状态发生改变时,所有依赖于它的对象都得到通知并自动更新。 ——《 设计模式》 GoF

解决问题

稳定性:“—”对“多”的依赖关系,“一”变化“多”跟着变化

变化点:“多”增加,“多”减少

代码结构

背景:
气象站发布气象资料给数据中心,数据中心经过处理,将气象信息更新到两个不同的显示终端(A 和B);一是数据中心的数据,多是所有的中断的数据都要变化

没有使用设计模式的时候:问题就是每次增加终端都要修改代码


class DisplayA {
public:void Show(float temperature);
};class DisplayB {
public:void Show(float temperature);
};class DisplayC {
public:void Show(float temperature);
}class WeatherData {
};class DataCenter {
public:void TempNotify() {DisplayA *da = new DisplayA;DisplayB *db = new DisplayB;DisplayC *dc = new DisplayC;// DisplayD *dd = new DisplayD;float temper = this->CalcTemperature();//计算数据da->Show(temper);//跟新数据db->Show(temper);dc->Show(temper);dc->Show(temper);//问题就是每次增加终端都要修改代码}
private:float CalcTemperature() {WeatherData * data = GetWeatherData();// ...float temper/* = */;return temper;}WeatherData * GetWeatherData(); // 不同的方式
};int main() {DataCenter *center = new DataCenter;//数据中心center->TempNotify();return 0;
}

使用设计模式:具体中断的改变不能影响我们的接口。应对稳定点,抽象。应对变化点,扩展(继承和组合)
对于一对多,一就是DataCenter,而多就是在DataCenter中定义一个list。

// 应对稳定点,抽象
// 应对变化点,扩展(继承和组合)
class DataCenter {//一
public:void Attach(IDisplay * ob) {//增加终端,用户可以加入//}void Detach(IDisplay * ob) {//减少终端//}void Notify() {//数据变化,通知各个终端,一变化,多就变化float temper = CalcTemperature();for (auto iter : obs) {iter.Show(temper);}}// 接口隔离
private:WeatherData * GetWeatherData();float CalcTemperature() {WeatherData * data = GetWeatherData();// ...float temper/* = */;return temper;}std::list<IDisplay*> obs;//终端,这个就是多
};

完整代码:

#include <list>
#include <algorithm>
using namespace std;
//
class IDisplay {//显示每一个终端
public:virtual void Show(float temperature) = 0;virtual ~IDisplay() {}
};class DisplayA : public IDisplay {
public:virtual void Show(float temperature) {cout << "DisplayA Show" << endl;}
private:void jianyi();
};class DisplayB : public IDisplay{
public:virtual void Show(float temperature) {cout << "DisplayB Show" << endl;}
};class DisplayC : public IDisplay{
public:virtual void Show(float temperature) {cout << "DisplayC Show" << endl;}
};class DisplayD : public IDisplay{
public:virtual void Show(float temperature) {cout << "DisplayC Show" << endl;}
};class WeatherData {
};// 应对稳定点,抽象
// 应对变化点,扩展(继承和组合)
class DataCenter {
public:void Attach(IDisplay * ob) {//增加终端,用户可以加入//}void Detach(IDisplay * ob) {//减少终端//}void Notify() {//数据变化,通知各个终端float temper = CalcTemperature();for (auto iter : obs) {iter.Show(temper);}}// 接口隔离
private:WeatherData * GetWeatherData();float CalcTemperature() {WeatherData * data = GetWeatherData();// ...float temper/* = */;return temper;}std::list<IDisplay*> obs;//终端
};int main() {// 单例模式DataCenter *center = new DataCenter;// ... 某个模块(实际上都是在其他文件中)IDisplay *da = new DisplayA();center->Attach(da);// ...(实际上都是在其他文件中)IDisplay *db = new DisplayB();center->Attach(db);(实际上都是在其他文件中)IDisplay *dc = new DisplayC();center->Attach(dc);center->Notify();//-----center->Detach(db);center->Notify();//....center->Attach(dd);center->Notify();return 0;
}

补充:单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。

符合哪些设计原则

面向接口编程
接口隔离

  • 类与类依赖在一个接口(就是那个list)
  • 容器存储接口

封装变化点

  • attach(增加终端)
  • detach(减少终端)

如何扩展

继承实现接口
调用attach
调用detach
就是这样:

class DisplayD : public IDisplay{
public:virtual void Show(float temperature) {cout << "DisplayC Show" << endl;}
};center->Attach(dd);center->Notify();

总结

要点

  • 观察者模式使得我们可以独立地改变目标与观察者,从而使二者之间的关系松耦合;
  • 观察者自己决定是否订阅通知,目标对象并不关注谁订阅了;
  • 观察者不要依赖通知顺序,目标对象也不知道通知顺序;
  • 常用在基于事件的ui框架中,也是 MVC 的组成部分;
  • 常用在分布式系统中、actor框架中;
    本质
    触发联动
    结构图
    在这里插入图片描述

设计模式-策略模式

定义

定义一系列算法,把它们一个个封装起来,并且使它们可互相替换。该模式使得算法可独立于使用它的客户程序而变化。——《设计模式》 GoF

解决问题

稳定点

  • 客户程序与算法的调用关系

变化点

  • 新加算法
  • 算法内容改变

客户只需要调用就行,不需要知道那个算法

代码结构

某商场节假日有固定促销活动,为了加大促销力度,现提升国庆节促销活动规格;稳定点就是这个促销活动,改变点就是这些具体的活动折扣啥的。
也就是策略模式用来消除if-else
没有设计模式的代码:

enum VacationEnum {VAC_Spring,VAC_QiXi,VAC_Wuyi,VAC_GuoQing,VAC_ShengDan,
};class Promotion {VacationEnum vac;
public:double CalcPromotion(){if (vac == VAC_Spring {// 春节}else if (vac == VAC_QiXi) {// 七夕}else if (vac == VAC_Wuyi) {// 五一}else if (vac == VAC_GuoQing) {// 国庆}else if (vac == VAC_ShengDan) {}}};

加人了设计模式:
其中
稳定点:抽象去解决它(调用关系)
变化点:扩展(继承和组合)去解决它

class Context {};// 稳定点:抽象去解决它
// 变化点:扩展(继承和组合)去解决它
class ProStategy {
public:virtual double CalcPro(const Context &ctx) = 0;virtual ~ProStategy(); 
};
// cpp
class VAC_Spring : public ProStategy {
public:virtual double CalcPro(const Context &ctx){}
};
// cpp
class VAC_QiXi : public ProStategy {
public:virtual double CalcPro(const Context &ctx){}
};
class VAC_QiXi1  : public VAC_QiXi {
public:virtual double CalcPro(const Context &ctx){}
};
// cpp
class VAC_Wuyi : public ProStategy {
public:virtual double CalcPro(const Context &ctx){}
};
// cpp
class VAC_GuoQing : public ProStategy {
public:virtual double CalcPro(const Context &ctx){}
};class VAC_Shengdan : public ProStategy {
public:virtual double CalcPro(const Context &ctx){}
};class Promotion {
public:Promotion(ProStategy *sss) : s(sss){}~Promotion(){}double CalcPromotion(const Context &ctx){return s->CalcPro(ctx);}
private:ProStategy *s;//这就是一种依赖注入的方式
};int main () {Context ctx;ProStategy *s = new VAC_QiXi1();Promotion *p = new Promotion(s);p->CalcPromotion(ctx);return 0;
}

设计原则

接口隔离

  • 依赖注入(*s就是依赖注入)
    class Promotion {
    public:Promotion(ProStategy *sss) : s(sss){}~Promotion(){}double CalcPromotion(const Context &ctx){return s->CalcPro(ctx);}
    private:ProStategy *s;//依赖注入的方式
    };
    
  • 解决通过一个接口解决两个类的依赖

面向接口编程
开闭原则

如何扩展代码

写一个接口,然后依赖注入的方式调用它

class VAC_QiXi1: public ProStategy {
public:virtual double CalcPro(const Context &ctx){}
};Context ctx;ProStategy *s = new VAC_QiXi1();Promotion *p = new Promotion(s);p->CalcPromotion(ctx);

总结

要点

  • 策略模式提供了一系列可重用的算法,从而可以使得类型在运行时方便地根据需要在各个算法之间进行切换;
  • 策略模式消除了条件判断语句;也就是在解耦合;

本质
分离算法,选择实现;
结构图
在这里插入图片描述

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

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

相关文章

【论文阅读】Tutorial on Diffusion Models for Imaging and Vision

1.The Basics: Variational Auto-Encoder 1.1 VAE Setting 自动编码器有一个输入变量x和一个潜在变量z Example. 获得图像的潜在表现并不是一件陌生的事情。回到jpeg压缩&#xff0c;使用离散余弦变换&#xff08;dct&#xff09;基φn对图像的底层图像/块进行编码。如果你给…

先电2.4的openstack搭建

先电2.4版本的openstack&#xff0c;前期虚拟机部署参考上一篇2.2版本&#xff0c;基本步骤是一样的&#xff0c;准备两个镜像文件CentOS-7.5-x86_64-DVD-1804.iso&#xff0c;XianDian-IaaS-V2.4.iso [rootcontroller ~]# cat /etc/sysconfig/network-scripts/ifcfg-eno16777…

华为OD机试 - 小扇和小船的数字游戏 - 二进制(Java 2024 C卷 200分)

华为OD机试 2024C卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷C卷&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;每一题都有详细的答题思路、详细的代码注释、样例测试…

论文笔记(四十五)Attention Is All You Need

Attention Is All You Need 文章概括摘要1. 介绍2. 背景3. 模型架构3.1 编码器和解码器堆栈3.2 Attention3.2.1 按比例点积Attention3.2.2 Multi-Head Attention3.2.3 注意力在模型中的应用 3.3 定位前馈网络3.4 嵌入与 Softmax3.5 位置编码 4 为什么 Self-Attention5. Trainin…

ssm104园区停车管理系统+jsp

园区停车管理系统的设计与实现 摘 要 网络技术和计算机技术发展至今&#xff0c;已经拥有了深厚的理论基础&#xff0c;并在现实中进行了充分运用&#xff0c;尤其是基于计算机运行的软件更是受到各界的关注。加上现在人们已经步入信息时代&#xff0c;所以对于信息的宣传和管…

P9422 [蓝桥杯 2023 国 B] 合并数列

P9422 [蓝桥杯 2023 国 B] 合并数列 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 用队列即可 当两个队列队首&#xff1a;a b &#xff0c;弹出 当a < b&#xff0c;把a加给其后一个元素&#xff0c;弹出a 当b < a&#xff0c;把b加给其后一个元素&#xff0c;弹出…

[初阶数据结构】时间复杂度与空间复杂度

前言 &#x1f4da;作者简介&#xff1a;爱编程的小马&#xff0c;正在学习C/C&#xff0c;Linux及MySQL。 &#x1f4da;本文收录于初阶数据结构系列&#xff0c;本专栏主要是针对时间、空间复杂度&#xff0c;顺序表和链表、栈和队列、二叉树以及各类排序算法&#xff0c;持续…

无人机+无人车:自组网协同技术及应用前景详解

无人车&#xff0c;也被称为自动驾驶汽车、电脑驾驶汽车或轮式移动机器人&#xff0c;是一种通过电脑系统实现无人驾驶的智能汽车。这种汽车依靠人工智能、视觉计算、雷达、监控装置和全球定位系统协同合作&#xff0c;使得电脑可以在没有任何人类主动操作的情况下&#xff0c;…

C++基础——输入输出(文件)

一、标准输入输出流 C 的输入输出是程序与用户或外部设备&#xff08;如文件、网络等&#xff09;之间交换信息的过程。 C 提供了丰富的标准库来支持这种交互&#xff0c;主要通过流的概念来实现。 流&#xff1a;抽象概念&#xff0c;表示一连串的数据&#xff08;字节或字…

typescript:vscode的settings配置文件配置ts语法提示

typescript&#xff1a;vscode的settings配置文件配置ts语法提示 1 找到vscode左下角的齿轮按钮 2 点击Settings&#xff08;或者快捷键ctrl,&#xff09;&#xff1a; 点击右上角的Open Settings(JSON)按钮打开配置文件&#xff1a; 或者ctrlshiftp&#xff0c;搜索settings&…

展开说说:Android实现多线程几种方式

1、线程是什么 关于线程&#xff0c;Android开发经常遇到的一个和线程相关的异常报错&#xff1a;NetworkOnMainThreadException&#xff0c;因为网络请求不可以运行在主线程&#xff08;又称UI线程&#xff09;。和网络请求一样的还有I/O操作、数据库操作等耗时任务一样都只能…

奈氏准则和香农定理

一、奈奎斯特和香农 哈里奈奎斯特&#xff08;Harry Nyquist&#xff09;(左) 克劳德艾尔伍德香农&#xff08;Claude Elwood Shannon&#xff09;(右) 我们应该在心里记住他们&#xff0c;记住所有为人类伟大事业做出贡献的人&#xff0c;因为他们我们的生活变得越来越精彩&…

使用xshell工具连接ubuntu的root账户被拒绝的解决方法

问题描述&#xff1a; 我在使用xshell工具远程连接Ubuntu虚拟机的过程中&#xff0c;如果连接的是的普通用户则xshell工具可以正常连接&#xff0c;但是当我向连接ubuntu系统的root用户&#xff0c;即便是密码输入正确但还是不能连接成功。不能连接成功的截图如下&#xff1a; …

从零开始学AI绘画,万字Stable Diffusion终极教程(二)

【第2期】关键词 欢迎来到SD的终极教程&#xff0c;这是我们的第二节课 这套课程分为六节课&#xff0c;会系统性的介绍sd的全部功能&#xff0c;让你打下坚实牢靠的基础 1.SD入门 2.关键词 3.Lora模型 4.图生图 5.controlnet 6.知识补充 在第一节课里面&#xff0c;我们…

PS 2018

软件安装 文件太大&#xff0c;分批上传了&#xff0c;后续下载下来文件目录是这样的&#xff0c; 三个文件夹.7z 分批上传&#xff0c;exe也压缩分批上传&#xff0c; 其中products文件夹太大&#xff0c;里面子目录继续压缩分批上传 都下好了&#xff0c;就exe执行安装就行…

如何使用提示测试为LLMs构建单元测试?

原文地址&#xff1a;how-to-build-unit-tests-for-llms-using-prompt-testing 确保您的人工智能交付&#xff1a;快速测试完美生成应用程序的基本指南 2024 年 4 月 26 日 如果你曾经编写过软件&#xff0c;你就会知道测试是开发过程中必不可少的一部分。特别是单元测试&#…

Git推送本地项目到gitee远程仓库

Git 是一个功能强大的分布式版本控制系统&#xff0c;它允许多人协作开发项目&#xff0c;同时有效管理代码的历史版本。开发者可以克隆一个公共仓库到本地&#xff0c;进行更改后将更新推送回服务器&#xff0c;或从服务器拉取他人更改&#xff0c;实现代码的同步和版本控制。…

BUUCTF:Web 解析(一)

前言 Buuctf Web 是一个在线安全挑战平台&#xff0c;旨在提高参与者对网络安全的理解和实践能力。本文将详细介绍 Buuctf Web 的特点、挑战和机遇&#xff0c;帮助读者更好地了解这一领域。 一、Buuctf Web 的特点 多样化的挑战场景&#xff1a;Buuctf Web 提供了多种挑战场…

金属表面粗糙度对信号的影响

在进行PCB的传输线设计时&#xff0c;如果希望仿真结果更加贴合于实际的效果&#xff0c;就需要考虑很多的附加因素&#xff0c;比如&#xff0c;真实的叠构参数、介电常数、损耗角正切值、蚀刻因子、金属表面粗糙度、玻纤效应等&#xff0c;在常规的信号仿真中&#xff0c;前三…