设计模式复习

单例模式

确保一个类最多只有一个实例,并提供一个全局访问点。

(某个类的对象有且仅有一个,单例的对象充当的是全局变量的角色,为什么在C++里面不直接使用全局变量,而是使用单例来代替全局变量,因为如果直接使用全局变量会破坏类的封装,全局变量没有被封装,他的访问权限是不受限制的,任何模块在任意位置都可以对全局变量进行读或者写的操作,如果在程序中大量使用全局变量,全局变量在一个位置被恶意篡改,在其他位置获取全局变量会产生影响,其他模块在工作的时候就得不到正确的值了,解决方案就是使用单例模式,全局变量被封装到一个类里面,并且被private修饰,就不会在类外被随意访问,在单例模式的类里面提供对私有成员的访问函数,对变量的读和写设定指定的规则,这样类里面的数据不是直接被访问的,而是间接被访问的,间接的通过单例模式的类提供的成员函数进行访问,这样既把数据进行了封装又可以保证数据的安全性)

创建一个单例模式的类

构造拷贝构造私有,提供静态公有的获取方法 

类外new来创建对象已经不行了,只能通过类名得到对象,所以对象是静态的

饿汉模式 :定义类的时候创建单例对象

还没有使用该单例对象,该单例对象就已经被加载到内存了,在对象过多时会造成内存浪费,在多线程模式下,饿汉模式没有线程安全问题

#include<iostream>
using namespace std;//饿汉模式->定义类的时候创建单例对象
//定义一个单例模式的任务队列
class TaskQueue
{
public:static TaskQueue* getInstance(){return m_taskQ;}void pirntf() {cout << "sss" << endl;}private:TaskQueue() = default;TaskQueue(const TaskQueue& t) = default;static TaskQueue* m_taskQ;
};
TaskQueue* TaskQueue::m_taskQ = new TaskQueue;int main() {TaskQueue* taskQ = TaskQueue::getInstance();taskQ->pirntf();return 0;
}

懒汉模式:什么时候使用这个单例对象,在使用的时候再去创建对应的实例

解决了饿汉式内存浪费问题,但是线程不安全的,可以通过互斥量mutex.lock()和mutex.unlock()来解决 

#include<iostream>
using namespace std;//定义一个单例模式的任务队列
class TaskQueue
{
public:static TaskQueue* getInstance(){if(m_taskQ == nullptr){m_taskQ = new TaskQueue;}return m_taskQ;}void pirntf() {cout << "sss" << endl;}private:TaskQueue() = default;TaskQueue(const TaskQueue& t) = default;static TaskQueue* m_taskQ;
};
TaskQueue* TaskQueue::m_taskQ = nullptr;int main() {TaskQueue* taskQ = TaskQueue::getInstance();taskQ->pirntf();return 0;
}

类加载的时候,没有立刻实例化,第一次调用getInstance()的时候,才真的实例化.

如果要是代码一整场都没有调用getInstance,此时实例化的过程也就被省略掉了,

我们一般成懒汉模式为“懒加载”或者“延时加载”,“懒汉模式"比“饿汉模式"效率更高,有很大的可能是“实例用不到",此时就节省了实例化的开销。

饿汉模式,懒汉模式线程安全吗?
了解了线程安全之后,对于饿汉模式来说,多线程同时调用getInstance(),由于getInstance()里只做了一件事:读取instance实例的地址,也就是多个线程在同时读取同一个变量,并没有构成多个线程同时修改同一个变量这一情况,所以说饿汉模式是线程安全的。

而对于懒汉模式来说,多线程调用getInstance(),getInstance()做了四件事情~

1.读取 instance 的内容

2.判断 instance 是否为 null

3.如果 instance 为null,就 new实例 (这就会修改 intance 的值)

4.返回实例的地址

由于懒汉模式造成了多个线程同时修改同一个变量这一情况,所以说懒汉模式是线程不安全的。

懒汉模式-使用双重检查锁定解决线程安全问题 

比如说三个线程A B C 同时访问 ,第一次判空,到锁那阻塞,A先进去第二次判空创建对象 B C等A完事锁解开才能进去 第一次ABC是一个接一个进去,如果再来三个线程的话会直接判断非空,直接到return m_taskQ了,使程序的效率提升。

#include<iostream>
#include<mutex>
using namespace std;//定义一个单例模式的任务队列
class TaskQueue
{
public:static TaskQueue* getInstance(){if (m_taskQ == nullptr) {m_mutex.lock();if (m_taskQ == nullptr) {m_taskQ = new TaskQueue;}m_mutex.unlock();}return m_taskQ;}void pirntf() {cout << "sss" << endl;}private:TaskQueue() = default;TaskQueue(const TaskQueue& t) = default;static TaskQueue* m_taskQ;static mutex m_mutex;
};
TaskQueue* TaskQueue::m_taskQ = nullptr;
mutex TaskQueue::m_mutex;int main() {TaskQueue* taskQ = TaskQueue::getInstance();taskQ->pirntf();return 0;
}

工厂模式

主要是对对象的创建进行了一个封装,提供了一种创建对象的方式。

(1)在没有工厂的时代,如果客户需要一款宝马车,那么就需要客户去创建一款宝马车,然后拿来用。

(2)简单工厂模式:后来出现了工厂,用户不再需要去创建宝马车,由工厂进行创建,想要什么车,直接通过工厂创建就可以了。比如想要320i系列车,工厂就创建这个系列的车。

(3)工厂方法模式:为了满足客户,宝马车系列越来越多,如320i、523i等等系列,一个工厂无法创建所有的宝马系列,于是又单独分出来多个具体的工厂,每个具体工厂创建一种系列,即具体工厂类只能创建一个具体产品。但是宝马工厂还是个抽象,你需要指定某个具体的工厂才能生产车出来。

(4)抽象工厂模式:随着客户要求越来越高,宝马车必须配置空调,于是这个工厂开始生产宝马车和需要的空调。最终是客户只要对宝马的销售员说:我要523i空调车,销售员就直接给他523i空调车了。而不用自己去创建523i空调车宝马车。

简单工厂模式

简单工厂模式的结构组成

1. 工厂类:工厂模式的核心类,会定义一个用于创建指定的具体实例对象的接口。

2. 抽象产品类:是具体产品类的继承的父类或实现的接口。

3. 具体产品类:工厂类所创建的对象就是此具体产品实例

#include<iostream>
using namespace std;// 产品类的父亲 - 人造恶魔果实
class AbstractSmile
{
public:virtual void transform() = 0;virtual void ability() = 0;virtual ~AbstractSmile(){}
};class SheepSmile :public AbstractSmile
{
public:void transform() override{cout << "变身-山羊形态。。" << endl;}void ability() override{cout << "将手臂变成羊角。。" << endl;}
};class LionSmile :public AbstractSmile
{
public:void transform() override{cout << "变身-狮子人形态。。" << endl;}void ability() override{cout << "火遁。。" << endl;}
};class BatSmile :public AbstractSmile
{
public:void transform() override{cout << "变身-蝙蝠人形态。。" << endl;}void ability() override{cout << "吸血。。" << endl;}
};//定义工厂类
enum class Type:char{Sheep,Lion,Bat};
class SmileFactory
{
public:AbstractSmile* createSmile(Type type){AbstractSmile* ptr = nullptr;switch (type){case Type::Sheep:ptr = new SheepSmile;break;case Type::Lion:ptr = new LionSmile;break;case Type::Bat:ptr = new BatSmile;break;default:break;}return ptr;}
};int main()
{SmileFactory* factroy = new SmileFactory;AbstractSmile* obj = factroy->createSmile(Type::Lion);obj->transform();obj->ability();return 0;
}

可以看到,简单工厂可以做到,让用户创建对象的时候只需要知道对象的名称(BMW、AUDI)就好,而不需要关心创建对象的细节(BMW是如何建造的、型号是什么等等细节)。

当然缺点也很明显:
每当我们想要扩展对象的时候(增加BENZ的对象)就需要在SimpleFactory类中添加代码,增加switch后面的case选项。这样一来,就需要修改源代码。灵活性非常的差!!!

那么,能不能做到添加对象的时候,不对现有代码进行修改呢?(也就是我们开发软件时候需要遵守的开-闭原则)

工厂模式

工厂方法模式将工厂抽象化,并定义一个创建对象的接口。每增加新产品,只需增加该产品以及对应的具体实现工厂类,由具体工厂类决定要实例化的产品是哪个,将对象的创建与实例化延迟到子类,这样工厂的设计就符合“开闭原则”了,扩展时不必去修改原来的代码。在使用时,用于只需知道产品对应的具体工厂,关注具体的创建过程,甚至不需要知道具体产品类的类名,当我们选择哪个具体工厂时,就已经决定了实际创建的产品是哪个了。

        但缺点在于,每增加一个产品都需要增加一个具体产品类和实现工厂类,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。

工厂方法模式的结构组成:

1. 抽象工厂类:工厂方法模式的核心类,提供创建具体产品的接口,由具体工厂类实现。

2. 具体工厂类:继承于抽象工厂,实现创建对应具体产品对象的方式。

3. 抽象产品类:它是具体产品继承的父类(基类)。

4. 具体产品类:具体工厂所创建的对象,就是此类。

(自己理解就是,工厂模式相当于简单工厂模式,如果需要增加新产品,只需要增加新的产品类继承抽象产品类,再增加生产该类型产品的工厂继承抽象工厂类,只需要拓展代码而不需要更改原先的代码)

#include<iostream>
using namespace std;// 产品类的父亲 - 人造恶魔果实
class AbstractSmile
{
public:virtual void transform() = 0;virtual void ability() = 0;virtual ~AbstractSmile(){}
};class SheepSmile :public AbstractSmile
{
public:void transform() override{cout << "变身-山羊形态。。" << endl;}void ability() override{cout << "将手臂变成羊角。。" << endl;}
};class LionSmile :public AbstractSmile
{
public:void transform() override{cout << "变身-狮子人形态。。" << endl;}void ability() override{cout << "火遁。。" << endl;}
};class BatSmile :public AbstractSmile
{
public:void transform() override{cout << "变身-蝙蝠人形态。。" << endl;}void ability() override{cout << "吸血。。" << endl;}
};//定义工厂类  - 父类
class AbstractFactory
{
public:virtual AbstractSmile* createSmile() = 0;virtual ~AbstractFactory(){}
};// 生产山羊的恶魔果实
class SheepFactory:public AbstractFactory
{
public:AbstractSmile* createSmile(){return new SheepSmile;}~SheepFactory(){cout << "SheepFactory 被析构了。。" << endl;}
};// 生产狮子的恶魔果实
class LionFactory :public AbstractFactory
{
public:AbstractSmile* createSmile(){return new LionSmile;}~LionFactory(){cout << "LionFactory 被析构了。。" << endl;}
};// 生产蝙蝠的恶魔果实
class BatFactory :public AbstractFactory
{
public:AbstractSmile* createSmile(){return new BatSmile;}~BatFactory(){cout << "BatFactory 被析构了。。" << endl;}
};int main()
{AbstractFactory* factroy = new LionFactory;AbstractSmile* obj = factroy->createSmile();obj->transform();obj->ability();delete obj;delete factroy;return 0;
}

抽象工厂模式

抽象工厂模式的结构组成(和工厂方法模式一样):

1. 抽象工厂类:工厂方法模式的核心类,提供创建具体产品的接口,由具体工厂类实现。

2. 具体工厂类:继承于抽象工厂,实现创建对应具体产品对象的方式。

3. 抽象产品类:它是具体产品继承的父类(基类)。

4. 具体产品类:具体工厂所创建的对象,就是此类。

抽象工厂模式的特点:

提供一个接口,可以创建多个产品族中的产品对象。比如船工厂,可以创建基础款船,升级款船

#include<iostream>
using namespace std;//船体
class shipBody
{
public:virtual string getBody() = 0;virtual ~shipBody(){}
};//木头船体
class WoodBody :public shipBody
{
public:string getBody() override{return "使用<木头>制作船体";}
};//钢铁船体
class IronBody :public shipBody
{
public:string getBody() override{return "使用<钢铁>制作船体";}
};//引擎
class Engine
{
public:virtual string getEngine() = 0;virtual ~Engine(){}
};class Human :public Engine
{
public:string getEngine(){return "船体动力方式是<手动>";}
};class Diesel :public Engine
{
public:string getEngine(){return "船体动力方式是<内燃机>";}
};//船
class Ship
{
public:Ship(shipBody*body,Engine* engine):m_body(body),m_engine(engine){}~Ship(){delete m_body;delete m_engine;}string getProperty(){string Info = m_body->getBody() + m_engine->getEngine();return Info;}private:shipBody* m_body;Engine* m_engine;
};//工厂类 - 抽象
class AbstractFactory
{
public:virtual Ship* createShip() = 0;virtual ~AbstractFactory(){}
};class BasicFactory :public AbstractFactory
{
public:Ship* createShip() override{Ship* ship = new Ship(new WoodBody, new Human);cout << "<基础形>海贼船已经打造完毕" << endl;return ship;}
};class StandardFactory :public AbstractFactory
{
public:Ship* createShip() override{Ship* ship = new Ship(new IronBody, new Diesel);cout << "<标准形>海贼船已经打造完毕" << endl;return ship;}
};int main()
{//下单标准型海贼船AbstractFactory* factory = new StandardFactory;Ship* ship = factory->createShip();cout<<ship->getProperty();delete ship;delete factory;return 0;
}

抽象工厂模式可以向客户端提供一个接口,使得客户端在不必指定产品的具体类型的情况下,能够创建多个产品族的产品对象

工厂模式只能生产一个产品。例如:产品要么香蕉、要么苹果,但抽象工厂可以一下生产一个产品族,如水果、动物、蔬菜等

#include<iostream>
using namespace std;class AbstractFactory;//抽象产品族Tea,茶可以是都匀毛尖、铁观音、普洱等品种。
class Tea{
public:virtual void getName() = 0;
};//抽象产品族Fruit
class Fruit{
public:virtual void getName() = 0;
};//抽象工厂,声明具体可以生产的产品族
class AbstractFactory{
public:virtual Tea* CreateTea() = 0;//生产产品族Teavirtual Fruit* CreateFruit() = 0 ;//生产产品族Fruit
};//水果族的具体水果
class AppleFruit:public Fruit{
public:void getName(){cout<<"I'm Apple"<<endl;}
};
class BananaFruit:public Fruit{
public:void getName(){cout<<"I'm Banana"<<endl;}
};//茶族的具体茶
class DuyunTea:public Tea{
public:void getName(){cout<<"I'm maojianTea"<<endl;}
};
class PuerhTea:public Tea{
public:void getName(){cout<<"I'm PuerhTea"<<endl;}
};//具体工厂,都匀工厂
class DunyunFactory:public AbstractFactory{
public:Tea* CreateTea(){return new DuyunTea;}Fruit* CreateFruit(){return new BananaFruit();}
};
//具体工厂,云南工厂
class YunnanFactory:public AbstractFactory{
public:Tea* CreateTea(){return new PuerhTea;}Fruit* CreateFruit(){return new AppleFruit();}
};
int main(){AbstractFactory* factory = nullptr;Fruit *fruit = nullptr;Tea   *tea = nullptr;//都匀工厂factory = new DunyunFactory;tea = factory->CreateTea();tea->getName();delete tea;fruit = factory->CreateFruit();fruit->getName();delete fruit;delete factory;//云南工厂factory = new YunnanFactory;tea = factory->CreateTea();tea->getName();delete tea;fruit = factory->CreateFruit();fruit->getName();delete fruit;delete factory;return 0;
}

 

以上只是我的学习过程,真正写的牛逼还清晰的还是下边这个 。。。。。

C++ 深入浅出工厂模式(初识篇) - 知乎 (zhihu.com)

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

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

相关文章

Linux+Win双系统远程重启到Win

背景 电脑安装了双系统&#xff08;ubuntu 22.04 win11&#xff09;&#xff0c;默认进入ubuntu系统。给电脑设置了WoL(Wake-on-LAN)&#xff0c;方便远程开机远程控制。 但是ubuntu的引导程序grub无法远程控制&#xff0c;远程开机会默认进入ubuntu。 虽然说可以进入ubuntu后…

【后端高频面试题--设计模式下篇】

&#x1f680; 作者 &#xff1a;“码上有前” &#x1f680; 文章简介 &#xff1a;后端高频面试题 &#x1f680; 欢迎小伙伴们 点赞&#x1f44d;、收藏⭐、留言&#x1f4ac; 后端高频面试题--设计模式下篇 往期精彩内容设计模式总览模板方法模式怎么理解模板方法模式模板方…

基于SpringBoot+WebSocket+Spring Task的前后端分离外卖项目-订单管理(十七)

订单管理 1. Spring Task1.1 介绍1.2 cron表达式1.3 入门案例1.3.1 Spring Task使用步骤1.3.2 代码开发1.3.3 功能测试 2.订单状态定时处理2.1 需求分析2.2 代码开发2.3 功能测试 3. WebSocket3.1 介绍3.2 入门案例3.2.1 案例分析3.2.2 代码开发3.2.3 功能测试 4. 来单提醒4.1 …

掌上新闻随心播控,HarmonyOS SDK助力新浪新闻打造精致易用的资讯服务新体验

原生智能是HarmonyOS NEXT的核心亮点之一&#xff0c;依托HarmonyOS SDK丰富全面的开放能力&#xff0c;开发者只需通过几行代码&#xff0c;即可快速实现AI功能。新浪新闻作为鸿蒙原生应用开发的先行者之一&#xff0c;从有声资讯入手&#xff0c;将基于Speech Kit朗读控件上线…

【Redis】深入理解 Redis 常用数据类型源码及底层实现(4.详解Hash数据结构)

Hash数据结构 看过前面的介绍,大家应该知道 Redis 的 Hash 结构的底层实现在 6 和 7 是不同的,Redis 6 是 ziplist 和 hashtable,Redis 7 是 listpack 和 hashtable。 我们先使用config get hash*看下 Redis 6 和 Redis 7 的 Hash 结构配置情况(在Redis客户端的命令行界面…

gem5学习(23):经典缓存——Classic Caches

目录 一、Interconnects 1、Crossbars 二、Debugging 默认缓存是一个带有MSHR&#xff08;未命中状态保持寄存器&#xff09;和WB&#xff08;写缓冲区&#xff09;的非阻塞缓存&#xff0c;用于读取和写入未命中。缓存还可以启用预取&#xff08;通常在最后一级缓存中&…

智慧公厕:让智慧城市的公共厕所焕发“智慧活力”

智慧城市的建设已经进入了一个新的阶段&#xff0c;不仅仅是智慧交通、智慧环保&#xff0c;如今甚至连公厕都开始迎来智慧化时代。智慧公厕作为智慧城市的神经末梢&#xff0c;正在通过信息化、数字化和智慧化的方式&#xff0c;实现全方位的精细化管理。本文以智慧公厕源头专…

WebSocket 通信流程,注解和Spring实现WebSocket ,实战多人聊天室系统

一、前言 实现即时通信常见的有四种方式-分别是&#xff1a;轮询、长轮询(comet)、长连接(SSE)、WebSocket。 ①短轮询 很多网站为了实现推送技术&#xff0c;所用的技术都是轮询。轮询是在特定的的时间间隔&#xff08;如每1秒&#xff09;&#xff0c;由客户端浏览器对服务…

Android 发布蒲公英平台自动更新

蒲公英官网&#xff1a;https://www.pgyer.com/ 首先弄明白蒲公英平台的SDK更新机制&#xff1a;蒲公英 - 文档中心 - SDK 自动更新机制 (pgyer.com) 下面直接开始代码操作 1.添加蒲公英maven库 maven { url "https://raw.githubusercontent.com/Pgyer/mvn_repo_pgyer…

【多线程】线程的概念与创建

多线程 1. 认识线程&#xff08;Thread&#xff09;线程是什么为啥要有线程进程和线程的区别Java 的线程 和 操作系统线程 的关系 2.第⼀个多线程程序3.创建线程⽅法1 继承 Thread 类⽅法2 实现 Runnable 接⼝方法3 匿名内部类创建 Thread ⼦类对象方法4 匿名内部类创建 Runnab…

【Vue前端】vue使用笔记0基础到高手第2篇:Vue知识点介绍(附代码,已分享)

本系列文章md笔记&#xff08;已分享&#xff09;主要讨论vue相关知识。Vue.js是前端三大新框架&#xff1a;Angular.js、React.js、Vue.js之一&#xff0c;Vue.js目前的使用和关注程度在三大框架中稍微胜出&#xff0c;并且它的热度还在递增。Vue.js是一个轻巧、高性能、可组件…

模型训练 —— AI算法初识

一、背景 AI算法中模型训练的主要目的是为了让机器学习算法从给定的标注数据中学习规律、特征和模式&#xff0c;并通过调整模型内部参数&#xff0c;使模型能够对未见过的数据进行准确预测或决策。具体来说&#xff1a; 1. **拟合数据**&#xff1a;模型通过训练来识别输入数…

集群聊天项目

不懂的一些东西 (const TcpConnectionPtr&)作为形参啥意思:接收一个常量引用,函数内部不允许修改该指针所指向的对象。 客户端与服务器如何联系? 优势 1.网络层与业务层分离:通过网络层传来的id,设计一个map存储id以及对印的业务处理器,处理器bind绑定处理函数,…

信息安全认证 | CISP证书怎么样?值得考吗?

HCIE考证研究所的朋友们&#xff0c;新年快乐&#xff01; 今天给大家说说CISP证书&#xff0c;新的一年祝大家逢考必过啊~ 01 考注册信息安全工程师证书的用处 CISP证书可作为学识和技能证明&#xff1b;求职、任职、晋升、加薪的资格凭证&#xff1b;用人单位招聘、录用劳动…

代码随想录算法训练营|二叉树总结

二叉树的定义&#xff1a; struct TreeNode {int val;TreeNode* left;TreeNode* right;TreeNode():val(0),left(nullptr),right(nullptr){}TreeNode(int val):val(val),left(nullptr),right(nullptr){}TreeNode(int val,TreeNode* left,TreeNode* right):val(val),left(left),…

第13章 网络 Page741~744 asio核心类 ip::tcp::socket

1. ip::tcp::socket liburl库使用"curl*" 代表socket 句柄 asio库使用ip::tcp::socket类代表TCP协议下的socket对象。 将“句柄”换成“对象”,因为asio库是不打折扣的C库 ip::tcp::socket提供一下常用异步操作都以async开头 表13-3 tcp::socket提供的异步操作 …

前端技巧之svg精灵图svg-sprite-loader

首先说明精灵图的必要性&#xff0c;其可以让我们只需要向服务器请求一次图片资源&#xff0c;就能加载很多图片&#xff0c;即能够减轻http请求造成的服务器压力。 然后这里要说明的是这个插件是webpack上面的&#xff0c;所以在vue2中比较好用&#xff0c;如果在vue3中&…

【python】网络爬虫与信息提取--正则表达式

一、正则表达式 正则表达式是用来简洁表达一组字符串的表达式。是通用的字符串表达框架&#xff0c;简洁表达一组字符串的表达式&#xff0c;针对字符串表达“简洁”和“特征”思想的工具&#xff0c;判断某字符串的特征归属。 用处&#xff1a;表达文本类型的特征&#xff1b;…

用Python和OpenCV搭建自己的一维码和QRCode扫描仪(步骤 + 源码)

导 读 本文主要介绍使用Python和OpenCV搭建自己的一维码和QRCode扫描仪&#xff08;步骤 源码&#xff09;。 项目简介 本文我们将创建一个程序来扫描图像中的二维码和条形码。对于这个程序&#xff0c;我们需要三个包&#xff0c;分别是OpenCV、NumPy和pyzbar。大多数 Pyth…

应用回归分析:岭回归

岭回归&#xff0c;也称为Tikhonov正则化&#xff0c;是一种专门用于处理多重共线性问题的回归分析技术。多重共线性是指模型中的自变量高度相关&#xff0c;这种高度的相关性会导致普通最小二乘法&#xff08;OLS&#xff09;估计的回归系数变得非常不稳定&#xff0c;甚至无法…