盘点c++几种常见的设计模式及具体实现
wptr33 2025-04-27 16:40 18 浏览
1.单例模式
作用:保证一个类只有一个实例,并提供一个访问它的全局访问点,使得系统中只有唯一的一个对象实例。
应用:常用于管理资源,如日志、线程池
实现要点:
在类中,要构造一个实例,就必须调用类的构造函数,并且为了保证全局只有一个实例,
需防止在外部调用类的构造函数而构造实例,需要将构造函数的访问权限标记为private,
同时阻止拷贝创建对象时赋值时拷贝对象,因此也将它们声明并权限标记为private;
另外,需要提供一个全局访问点,就需要在类中定义一个static函数,返回在类内部唯一构造的实例。
class Singleton{
public:
static Singleton& getInstance(){
static Singleton instance;
return instance;
}
void printTest(){
cout<<"do something"<<endl;
}
private:
Singleton(){}//防止外部调用构造创建对象
Singleton(Singleton const &singleton);//阻止拷贝创建对象
Singleton& operator=(Singleton const &singleton);//阻止赋值对象
};
int main()
{
Singleton &a=Singleton::getInstance();
a.printTest();
return 0;
}
首先,构造函数声明成private的目的是只允许内部调用,getInstance()中的静态局部变量创建时调用,但不允许外部调用构造创建第二个实例;
然后,拷贝构造和拷贝赋值符是声明成了private而不给出定义,其目的是阻止拷贝,如果企图通过拷贝构造来创建第二个实例,编译器会报错。
阻止拷贝的另一种写法是声明后接一个"=delete",也能起到相同的作用(C++11)。
2.工厂模式
工厂模式包括三种:简单工厂模式、工厂方法模式、抽象工厂模式。
工厂模式的主要作用是封装对象的创建,分离对象的创建和操作过程,用于批量管理对象的创建过程,便于程序的维护和扩展。
(1)简单工厂模式
简单工厂是工厂模式最简单的一种实现,对于不同产品的创建定义一个工厂类,将产品的类型作为参数传入到工厂的创建函数,根据类型分支选择不同的产品构造函数。
//简单工厂模式
typedef enum ProductTypeTag
{
TypeA,
TypeB,
TypeC
}PRODUCTTYPE;
class Product//产品抽象基类
{
public:
virtual void Show() = 0;
};
class ProductA : public Product
{
public:
void Show()
{
cout<<"I'm ProductA"<<endl;
}
};
class ProductB : public Product
{
public:
void Show()
{
cout<<"I'm ProductB"<<endl;
}
};
class ProductC : public Product
{
public:
void Show()
{
cout<<"I'm ProductC"<<endl;
}
};
class Factory//工厂类
{
public:
Product* CreateProduct(PRODUCTTYPE type)
{
switch (type)
{
case TypeA:
return new ProductA();
case TypeB:
return new ProductB();
case TypeC:
return new ProductC();
default:
return NULL;
}
}
};
int main()
{
Factory productCreator;
Product *productA=productCreator.CreateProduct(TypeA);
Product *productB=productCreator.CreateProduct(TypeB);
Product *productC=productCreator.CreateProduct(TypeC);
productA->Show();
productB->Show();
productC->Show();
if(productA){
delete productA;
productA=NULL;
}
if(productB){
delete productB;
productB=NULL;
}
if(productC){
delete productC;
productC=NULL;
}
return 0;
}
需要C/C++ Linux服务器架构师学习资料后台私信“资料”(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等),免费分享
(2)工厂方法模式
其实这才是正宗的工厂模式,简单工厂模式只是一个简单的对创建过程封装。工厂方法模式在简单工厂模式的基础上增加对工厂的基类抽象,不同的产品创建采用不同的工厂创建(从工厂的抽象基类派生),这样创建不同的产品过程就由不同的工厂分工解决:FactoryA专心负责生产ProductA,FactoryB专心负责生产ProductB,FactoryA和FactoryB之间没有关系;如果到了后期,如果需要生产ProductC时,我们则可以创建一个FactoryC工厂类,该类专心负责生产ProductC类产品。
该模式相对于简单工厂模式的优势在于:便于后期产品种类的扩展。
//工厂方法模式
typedef enum ProductTypeTag
{
TypeA,
TypeB,
TypeC
}PRODUCTTYPE;
class Product//产品抽象基类
{
public:
virtual void Show() = 0;
};
class ProductA : public Product
{
public:
void Show()
{
cout<<"I'm ProductA"<<endl;
}
};
class ProductB : public Product
{
public:
void Show()
{
cout<<"I'm ProductB"<<endl;
}
};
class Factory//工厂类
{
public:
virtual Product *createProduct()=0;
};
class FactoryA:public Factory{
public:
Product *createProduct(){
return new ProductA();
}
};
class FactoryB:public Factory{
public:
Product *createProduct(){
return new ProductB();
}
};
class FactoryC:public Factory{
public:
Product *createProduct(){
return new ProductC();
}
};
int main()
{
Factory *factoryA=new FactoryA();
Product *productA = factoryA->createProduct();
productA->Show();
Factory *factoryB=new FactoryB();
Product *productB = factoryB->createProduct();
productB->Show();
if (factoryA)
{
delete factoryA;
factoryA = NULL;
}
if (factoryB)
{
delete factoryB;
factoryB = NULL;
}
if (productA)
{
delete productA;
productA = NULL;
}
if (productB)
{
delete productB;
productB = NULL;
}
return 0;
}
(3)抽象工厂模式
抽象工厂模式对工厂方法模式进行了更加一般化的描述。工厂方法模式适用于产品种类结构单一的场合,为一类产品提供创建的接口;而抽象工厂方法适用于产品种类结构多的场合,就是当具有多个抽象产品类型时,抽象工厂便可以派上用场。
抽象工厂模式更适合实际情况,受生产线所限,让低端工厂生产不同种类的低端产品,高端工厂生产不同种类的高端产品。
//抽象工厂模式
class ProductA
{
public:
virtual void Show() = 0;
};
class ProductA1 : public ProductA//A类低端产品
{
public:
void Show()
{
cout<<"I'm ProductA1"<<endl;
}
};
class ProductA2 : public ProductA//A类高端产品
{
public:
void Show()
{
cout<<"I'm ProductA2"<<endl;
}
};
class ProductB
{
public:
virtual void Show() = 0;
};
class ProductB1 : public ProductB//B类低端产品
{
public:
void Show()
{
cout<<"I'm ProductB1"<<endl;
}
};
class ProductB2 : public ProductB//B类高端产品
{
public:
void Show()
{
cout<<"I'm ProductB2"<<endl;
}
};
class Factory
{
public:
virtual ProductA *CreateProductA() = 0;
virtual ProductB *CreateProductB() = 0;
};
class Factory1 : public Factory//1号工厂用于生产低端产品
{
public:
ProductA *CreateProductA()
{
return new ProductA1();
}
ProductB *CreateProductB()
{
return new ProductB1();
}
};
class Factory2 : public Factory//2号工厂用于生产高端产品
{
ProductA *CreateProductA()
{
return new ProductA2();
}
ProductB *CreateProductB()
{
return new ProductB2();
}
};
int main()
{
Factory *factory1 = new Factory1();
ProductA *productA1 = factory1->CreateProductA();
ProductB *productB1 = factory1->CreateProductB();
productA1->Show();
productB1->Show();
Factory *factory2 = new Factory2();
ProductA *productA2 = factory2->CreateProductA();
ProductB *productB2 = factory2->CreateProductB();
productA2->Show();
productB2->Show();
if (factory1)
{
delete factory1;
factory1 = NULL;
}
if (productA1)
{
delete productA1;
productA1= NULL;
}
if (productB1)
{
delete productB1;
productB1 = NULL;
}
if (factory2)
{
delete factory2;
factory2 = NULL;
}
if (productA2)
{
delete productA2;
productA2 = NULL;
}
if (productB2)
{
delete productB2;
productB2 = NULL;
}
}
3 策略模式
策略模式也是一种非常常用的设计模式,而且也不复杂。下面我们就来看看这种模式。
定义:策略模式定义了一系列的算法,并将每一个算法封装起来,而且使它们还可以相互替换。策略模式让算法独立于使用它的客户而独立变化。
角色:
抽象策略角色(Strategy): 抽象策略类。
具体策略角色(ConcreteStrategy):封装了继续相关的算法和行为。
环境角色(Context):持有一个策略类的引用,最终给客户端调用。
UML图:
事例: (该事例改编自一道网络设计模式面试题)
如现在你是一个设计师,你正在设计一种空调。但是你们的空调要支持3种模式。冷风模式(ColdWind), 热风模式(WramWind),无风模式(NoWind)。
当选择ColdWind模式,将输送冷风;当选择WarmWind模式,将输送热风;在选择NoWind模式时,空调什么都不做。你将考虑如何为空调设计应用程序?如果将来空调需要增加支持新的模式呢?
这道面试题,其实可以用各种模式实现,然而在这里我理解策略模式比较合适。我们将冷风模式,和热风模式以及无风模式可以理解为各种不同的算法。显然策略模式非常符合。
这里ColdWind, WramWind, NoWind 其实就是ConcreteStrategy。 IWnd 是抽象策略类。 所以我们开始这么封装我们策略类
#include <iostream>
using namespace std;
#define free_ptr(p) \
if(p) delete p; p = NULL;
class IWind{
public:
virtual ~IWind(){};
virtual void blowWind() = 0;
};
class ColdWind : public IWind{
public:
void blowWind(){
cout<<"Blowing cold wind!"<<endl;
};
};
class WarmWind : public IWind{
public:
void blowWind(){
cout<<"Blowing warm wind!"<<endl;
}
};
class NoWind : public IWind{
public:
void blowWind(){
cout<<"No Wind!"<<endl;
}
};
然后我们实现一个windmode 的类,作为 wind 系列的环境类:
class WindMode{
public:
WindMode(IWind* wind): m_wind(wind){};
~WindMode(){free_ptr(m_wind);}
void blowWind(){
m_wind->blowWind();
};
private:
IWind* m_wind;
};
最后客户端代码:
int main(int argc, char* argv[])
{
WindMode* warmWind = new WindMode(new WarmWind());
WindMode* coldWind = new WindMode(new ColdWind());
WindMode* noWind = new WindMode(new NoWind());
warmWind->BlowWind();
coldWind->BlowWind();
noWind->BlowWind();
free_ptr(warmWind);
free_ptr(coldWind);
free_ptr(noWind);
system("pause");
return 0;
}
(这个实例网上也有人用命令模式实现。命令模式请看我后面的博客。把冷风,热风,无风作为一种命令。当然这是另外一种思路,也未尝不可。但是我觉得如果采用命令模式。类的个数会相应增加(增加系列的命令类),造成额外的开销。当添加一个新模式的时候,你需要添加的类过多。或多或少不是那么明智。所以我个人认为在这里策略模式更好一些。)
总的说来策略模式:
优点:
1、 使用策略模式可以避免使用多重条件转移语句。多重转移语句不易维护。
2、 策略模式让你可以动态的改变对象的行为,动态修改策略
缺点:
1、客户端必须知道所有的策略类,并自行决定使用哪一个策略类。
2、类过多---策略模式造成很多的策略类,每个具体策略类都会产生一个新类。(这点可以通过享元模式来克服类过多)
模式定义:
命令模式将“请求”封装成对象,以便使用不同的请求、队列或者日志来参数化其他对象。命令模式也支持可撤销的操作。
命令对象将动作和接受者包进对象中,这个对象只暴露一个execute()方法。
当需要将发出请求的对象和执行请求的对象解耦的时候,使用命令模式。
模式结构:
举例:
遥控器上有一个插槽,可以放上不同的装置,然后用按钮控制。我们这里放置电灯,并有开和关按钮。可以命令模式实现。
UML设计:
其中,RemoteControl为遥控器,LightOnCommand为开灯请求对象,LightOffCommand为关灯请求对象,他们继承自基类Command,这样设计可以使插槽在以后防止其他的装置。
#include <iostream>
using namespace std;
//电灯类
class Light
{
public:
void on()
{
cout << "Light on !" << endl;
}
void off()
{
cout << "Light off !" << endl;
}
};
//命令类
class Command
{
public:
virtual void execute(){}
};
//具体命令类
class LigthOnCommand : public Command
{
public:
LigthOnCommand(Light* lig):light(lig){}
//execute方法
void execute()
{
light->on();
}
private:
Light* light;
};
class LigthOffCommand : public Command
{
public:
LigthOffCommand(Light* lig):light(lig){}
void execute()
{
light->off();
}
private:
Light* light;
};
//遥控器类
class RemoteControl
{
public:
void setCommand(Command* command)
{
slot = command;
}
void buttonOn()
{
slot->execute();
}
private:
Command* slot;
};
//客户代码
int main()
{
RemoteControl lightOnControl;
RemoteControl lightOffControl;
Command* onCommand = new LigthOnCommand(new Light());
Command* offCommand = new LigthOffCommand(new Light());
lightOnControl.setCommand(onCommand);
lightOffControl.setCommand(offCommand);
lightOnControl.buttonOn();
lightOffControl.buttonOn();
return 0;
}
执行结果:
Lighton !
Lightoff !
请按任意键继续. .
- 上一篇:怎样才算学会了C++基础,一篇文章学习了解(包含Qt内容)
- 下一篇:用C实现协程库
相关推荐
- redis的八种使用场景
-
前言:redis是我们工作开发中,经常要打交道的,下面对redis的使用场景做总结介绍也是对redis举报的功能做梳理。缓存Redis最常见的用途是作为缓存,用于加速应用程序的响应速度。...
- 基于Redis的3种分布式ID生成策略
-
在分布式系统设计中,全局唯一ID是一个基础而关键的组件。随着业务规模扩大和系统架构向微服务演进,传统的单机自增ID已无法满足需求。高并发、高可用的分布式ID生成方案成为构建可靠分布式系统的必要条件。R...
- 基于OpenWrt系统路由器的模式切换与网页设计
-
摘要:目前商用WiFi路由器已应用到多个领域,商家通过给用户提供一个稳定免费WiFi热点达到吸引客户、提升服务的目标。传统路由器自带的Luci界面提供了工厂模式的Web界面,用户可通过该界面配置路...
- 这篇文章教你看明白 nginx-ingress 控制器
-
主机nginx一般nginx做主机反向代理(网关)有以下配置...
- 如何用redis实现注册中心
-
一句话总结使用Redis实现注册中心:服务注册...
- 爱可可老师24小时热门分享(2020.5.10)
-
No1.看自己以前写的代码是种什么体验?No2.DooM-chip!国外网友SylvainLefebvre自制的无CPU、无操作码、无指令计数器...No3.我认为CS学位可以更好,如...
- Apportable:拯救程序员,IOS一秒变安卓
-
摘要:还在为了跨平台使用cocos2d-x吗,拯救objc程序员的奇葩来了,ApportableSDK:FreeAndroidsupportforcocos2d-iPhone。App...
- JAVA实现超买超卖方案汇总,那个最适合你,一篇文章彻底讲透
-
以下是几种Java实现超买超卖问题的核心解决方案及代码示例,针对高并发场景下的库存扣减问题:方案一:Redis原子操作+Lua脚本(推荐)//使用Redis+Lua保证原子性publicbo...
- 3月26日更新 快速施法自动施法可独立设置
-
2016年3月26日DOTA2有一个79.6MB的更新主要是针对自动施法和快速施法的调整本来内容不多不少朋友都有自动施法和快速施法的困扰英文更新日志一些视觉BUG修复就不翻译了主要翻译自动施...
- Redis 是如何提供服务的
-
在刚刚接触Redis的时候,最想要知道的是一个’setnameJhon’命令到达Redis服务器的时候,它是如何返回’OK’的?里面命令处理的流程如何,具体细节怎么样?你一定有问过自己...
- lua _G、_VERSION使用
-
到这里我们已经把lua基础库中的函数介绍完了,除了函数外基础库中还有两个常量,一个是_G,另一个是_VERSION。_G是基础库本身,指向自己,这个变量很有意思,可以无限引用自己,最后得到的还是自己,...
- China's top diplomat to chair third China-Pacific Island countries foreign ministers' meeting
-
BEIJING,May21(Xinhua)--ChineseForeignMinisterWangYi,alsoamemberofthePoliticalBureau...
- 移动工作交流工具Lua推出Insights数据分析产品
-
Lua是一个适用于各种职业人士的移动交流平台,它在今天推出了一项叫做Insights的全新功能。Insights是一个数据平台,客户可以在上面实时看到员工之间的交流情况,并分析这些情况对公司发展的影响...
- Redis 7新武器:用Redis Stack实现向量搜索的极限压测
-
当传统关系型数据库还在为向量相似度搜索的性能挣扎时,Redis7的RedisStack...
- Nginx/OpenResty详解,Nginx Lua编程,重定向与内部子请求
-
重定向与内部子请求Nginx的rewrite指令不仅可以在Nginx内部的server、location之间进行跳转,还可以进行外部链接的重定向。通过ngx_lua模块的Lua函数除了能实现Nginx...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
因果推断Matching方式实现代码 因果推断模型
-
git pull命令使用实例 git pull--rebase
-
面试官:git pull是哪两个指令的组合?
-
git pull 和git fetch 命令分别有什么作用?二者有什么区别?
-
git 执行pull错误如何撤销 git pull fail
-
git fetch 和git pull 的异同 git中fetch和pull的区别
-
git pull 之后本地代码被覆盖 解决方案
-
还可以这样玩?Git基本原理及各种骚操作,涨知识了
-
git命令之pull git.pull
-
- 最近发表
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)