百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程网 > 正文

C++ 深入浅出工厂模式(进阶篇) c++实现工厂模式

yuyutoo 2024-10-20 13:08 7 浏览 0 评论

介绍

前文初始篇C++ 深入浅出工厂模式(初始篇),主要阐述了简单工厂模式、工厂方法模式和抽象工厂模式的结构、特点和缺陷等。以上三种方式,在新增产品时,要么修改工厂类,要么需新增具体的工厂类,说明工厂类的封装性还不够好。

本文进阶篇,主要是将工厂类的封装性提高,达到新增产品时,也不需要修改工厂类,不需要新增具体的工厂类。封装性高的工厂类特点是扩展性高、复用性也高。

模板工厂

针对工厂方法模式封装成模板工厂类,那么这样在新增产品时,是不需要新增具体的工厂类,减少了代码的编写量。

UML图:

代码:

Shoes和Clothe,分别为鞋子和衣服的抽象类(基类)

NiKeShoes和UniqloClothe,分别为耐克鞋子和优衣库衣服具体产品类。

// 基类 鞋子
class Shoes
{
public:
 virtual void Show() = 0;
 virtual ~Shoes() {}
};
// 耐克鞋子
class NiKeShoes : public Shoes
{
public:
 void Show()
 {
 std::cout << "我是耐克球鞋,我的广告语:Just do it" << std::endl;
 }
};
// 基类 衣服
class Clothe
{
public:
 virtual void Show() = 0;
 virtual ~Clothe() {}
};
// 优衣库衣服
class UniqloClothe : public Clothe
{
public:
 void Show()
 {
 std::cout << "我是优衣库衣服,我的广告语:I am Uniqlo" << std::endl;
 }
};

AbstractFactory为抽象模板工厂类,其中模板参数:AbstractProduct_t 产品抽象类,如Shoes、Clothe。

ConcreteFactory为具体模板工厂类,其中模板参数:AbstractProduct_t 产品抽象类(如Shoes、Clothe),ConcreteProduct_t 产品具体类(如NiKeShoes、UniqloClothe)。

// 抽象模板工厂类
// 模板参数:AbstractProduct_t 产品抽象类
template <class AbstractProduct_t>
class AbstractFactory
{
public:
 virtual AbstractProduct_t *CreateProduct() = 0;
 virtual ~AbstractFactory() {}
};
// 具体模板工厂类
// 模板参数:AbstractProduct_t 产品抽象类,ConcreteProduct_t 产品具体类
template <class AbstractProduct_t, class ConcreteProduct_t>
class ConcreteFactory : public AbstractFactory<AbstractProduct_t>
{
public:
 AbstractProduct_t *CreateProduct()
 {
 return new ConcreteProduct_t();
 }
};

main函数,根据不同类型的产品,构造对应的产品的工厂对象,便可通过对应产品的工厂对象创建具体的产品对象。

int main()
{
 // 构造耐克鞋的工厂对象
 ConcreteFactory<Shoes, NiKeShoes> nikeFactory;
 // 创建耐克鞋对象
 Shoes *pNiKeShoes = nikeFactory.CreateProduct();
 // 打印耐克鞋广告语
 pNiKeShoes->Show();
 // 构造优衣库衣服的工厂对象
 ConcreteFactory<Clothe, UniqloClothe> uniqloFactory;
 // 创建优衣库衣服对象
 Clothe *pUniqloClothe = uniqloFactory.CreateProduct();
 // 打印优衣库广告语
 pUniqloClothe->Show();
 // 释放资源
 delete pNiKeShoes;
 pNiKeShoes = NULL;
 delete pUniqloClothe;
 pUniqloClothe = NULL;
 return 0;
}

输出结果:

[root@lincoding factory]# ./templateFactory 
我是耐克球鞋,我的广告语:Just do it
我是优衣库衣服,我的广告语:I am Uniqlo

产品注册模板类+单例工厂模板类

前面的模板工厂虽然在新增产品的时候,不需要新增具体的工厂类,但是缺少一个可以统一随时随地获取指定的产品对象的类。

还有改进的空间,我们可以把产品注册的对象用std::map的方式保存,通过key-valve的方式可以轻松简单的获取对应的产品对象实例。

实现大致思路:

  • 把产品注册的功能封装成产品注册模板类。注册的产品对象保存在工厂模板类的std::map,便于产品对象的获取。
  • 把获取产品对象的功能封装成工厂模板类。为了能随时随地获取指定产品对象,则把工厂设计成单例模式。

UML图:

产品注册模板类+单例工厂模板类:

IProductRegistrar为产品注册抽象类,模板参数 ProductType_t 表示的类是产品抽象类(如Shoes、Clothe)。提供了产品对象创建的纯虚函数CreateProduct。

ProductFactory为工厂模板类,模板参数 ProductType_t 表示的类是产品抽象类(如Shoes、Clothe)。用于保存注册产品对象到std::map中和获取对应的产品对象。

ProductRegistrar为产品注册模板类,模板参数 ProductType_t 表示的类是产品抽象类(如Shoes、Clothe),ProductImpl_t 表示的类是具体产品(如NikeShoes、UniqloClothe)。用于注册产品到工厂类和创建产品实例对象。

// 基类,产品注册模板接口类
// 模板参数 ProductType_t 表示的类是产品抽象类
template <class ProductType_t>
class IProductRegistrar
{
public:
 // 获取产品对象抽象接口
 virtual ProductType_t *CreateProduct() = 0;
protected:
 // 禁止外部构造和虚构, 子类的"内部"的其他函数可以调用
 IProductRegistrar() {}
 virtual ~IProductRegistrar() {}
private:
 // 禁止外部拷贝和赋值操作
 IProductRegistrar(const IProductRegistrar &);
 const IProductRegistrar &operator=(const IProductRegistrar &);
};
// 工厂模板类,用于获取和注册产品对象
// 模板参数 ProductType_t 表示的类是产品抽象类
template <class ProductType_t>
class ProductFactory
{
public:
 // 获取工厂单例,工厂的实例是唯一的
 static ProductFactory<ProductType_t> &Instance()
 {
 static ProductFactory<ProductType_t> instance;
 return instance;
 }
 // 产品注册
 void RegisterProduct(IProductRegistrar<ProductType_t> *registrar, std::string name)
 {
 m_ProductRegistry[name] = registrar;
 }
 // 根据名字name,获取对应具体的产品对象
 ProductType_t *GetProduct(std::string name)
 {
 // 从map找到已经注册过的产品,并返回产品对象
 if (m_ProductRegistry.find(name) != m_ProductRegistry.end())
 {
 return m_ProductRegistry[name]->CreateProduct();
 }
 // 未注册的产品,则报错未找到
 std::cout << "No product found for " << name << std::endl;
 return NULL;
 }
private:
 // 禁止外部构造和虚构
 ProductFactory() {}
 ~ProductFactory() {}
 // 禁止外部拷贝和赋值操作
 ProductFactory(const ProductFactory &);
 const ProductFactory &operator=(const ProductFactory &);
 // 保存注册过的产品,key:产品名字 , value:产品类型
 std::map<std::string, IProductRegistrar<ProductType_t> *> m_ProductRegistry;
};
// 产品注册模板类,用于创建具体产品和从工厂里注册产品
// 模板参数 ProductType_t 表示的类是产品抽象类(基类),ProductImpl_t 表示的类是具体产品(产品种类的子类)
template <class ProductType_t, class ProductImpl_t>
class ProductRegistrar : public IProductRegistrar<ProductType_t>
{
public:
 // 构造函数,用于注册产品到工厂,只能显示调用
 explicit ProductRegistrar(std::string name)
 {
 // 通过工厂单例把产品注册到工厂
 ProductFactory<ProductType_t>::Instance().RegisterProduct(this, name);
 }
 // 创建具体产品对象指针
 ProductType_t *CreateProduct()
 {
 return new ProductImpl_t();
 }
};

main函数,通过ProductRegistrar注册各种不同类型产品,在统一由ProductFactory单例工厂获取指定的产品对象。

 int main()
{
 // ========================== 生产耐克球鞋过程 ===========================//
 // 注册产品种类为Shoes(基类),产品为NiKe(子类)到工厂,产品名为nike
 ProductRegistrar<Shoes, NiKeShoes> nikeShoes("nike");
 // 从工厂获取产品种类为Shoes,名称为nike的产品对象
 Shoes *pNiKeShoes = ProductFactory<Shoes>::Instance().GetProduct("nike");
 // 显示产品的广告语
 pNiKeShoes->Show();
 // 释放资源
 if (pNiKeShoes)
 {
 delete pNiKeShoes;
 }
 // ========================== 生产优衣库衣服过程 ===========================//
 // 注册产品种类为Clothe(基类),产品为UniqloClothe(子类)到工厂,产品名为uniqlo
 ProductRegistrar<Clothe, UniqloClothe> adidasShoes("uniqlo");
 // 从工厂获取产品种类为Shoes,名称为adidas的产品对象
 Clothe *pUniqloClothe = ProductFactory<Clothe>::Instance().GetProduct("uniqlo");
 // 显示产品的广告语
 pUniqloClothe->Show();
 // 释放资源
 if (pUniqloClothe)
 {
 delete pUniqloClothe;
 }
 return 0;
}

输出结果:

[root@lincoding factory]# ./singleFactory 
我是耐克球鞋,我的广告语:Just do it
我是优衣库衣服,我的广告语:I am Uniqlo

总结

将工厂方法模式改良成模板工厂,虽然可以解决产品新增时,不需要新增具体工厂类,但是缺少一个可以随时随地获取产品对象的方式,说明还有改进的空间。

将模板工厂改良成产品注册模板类+单例工厂模板类,产品注册模板类用于注册不同类型的产品,单例工厂模板类用于获取指定已注册的产品对象。这种方式,可以把工厂模式中产品的注册和获取的主要功能很好的抽象成两个类,并且使用单例模式使得工厂类可以随时随地获取已注册的产品对象。

所以产品注册模板类+单例工厂模板类的工厂模式,达到了开闭法则,并且扩展性高和封装度高。

相关推荐

Docker部署 chatgpt-web-midjourney-proxy:开启一站式AI与绘图

ChatGPT和Midjourney的结合无疑是创新性的突破。ChatGPT作为强大的语言模型,能够为用户提供智能的对话和文本生成服务。而Midjourney则以其出色的绘画能力,能够根据...

Cacti监控服务器配置教程(基于CentOS+Nginx+MySQL+PHP环境搭建)

具体案例:局域网内有两台主机,一台Linux、一台Windows,现在需要配置一台Cacti监控服务器对这两台主机进行监控...

那些少为人知却非常有意思好用的 Chrome 扩展

ChromeWebStoreSessionManager要睡觉了,还有网页没看完怎么办?等明天点开歷史记录重新加载?No!有这个保存当前打开的链接,下回直接打开一串网址,好顶赞有木有!!!chr...

分享一款轻量级 HTTP(S) 代理 TinyProxy

概述众所周知,我们常用的Web服务器Nginx/Apache都可以很方便的用来做为正向或反向代理服务器使用。但是它们都并不支持HTTPS的正向代理。Nginx做为正向代理不支持HT...

深入理解 WebSecurityConfigurerAdapter「源码篇」

我们继续来撸SpringSecurity源码,今天来撸一个非常重要的WebSecurityConfigurerAdapter。我们的自定义都是继承自WebSecurityConfigurer...

RPC、Web Service等几种远程监控通信方式对比

几种远程监控通信方式的介绍一.RPCRPC使用C/S方式,采用http协议,发送请求到服务器,等待服务器返回结果。这个请求包括一个参数集和一个文本集,通常形成“classname.meth...

老酒好喝,5G时代数据中心柴油发电机组以GPRS方式接入动环监控

背景:随着手机的普及,电信运营商基站越建越多,网络覆盖范围也越来越广,基本上随时随地都能通过运营商的网络上网冲浪,这给我们传统的通过有线方式实现的动环监控也提带来了新的启发:对于不具备有线传输条件的的...

为了春节红包大战,微信做了一次成功的预热!

经过上午的预告,微信在今天下午17:00正式推出了微信红包新玩法——红包照片。微信用户在朋友圈点击照片发送按钮,会看到“红包照片”选项,用户在选择发布照片之后,这张照片将被模糊处理后,再发送到朋友...

Proxy-Lite实战:3步部署+2个案例,轻松掌握轻量级网页自动化

大家好,我是何三,80后老猿,独立开发者一、Proxy-Lite模型概述...

会Python?那么你一定要试一试mitmproxy

mitmproxy是一款工具,也可以说是python的一个包,使用这个工具可以在命令行上进行抓包(现在也可以在web页面上查看上抓的数据包了),还可以对所抓到的包进行脚本处理,非常有用。和...

十大Web安全扫描工具,你知道哪些?

初入门时,喜欢将目标站点直接丢扫描器,慢慢等扫描结果,极度依赖Web扫描器;而有一些漏洞高手,善于运用运用各种工具但并不依赖工具,经常可以找到扫描工具发现不了的漏洞。一款好用的Web扫描器对于白帽子来...

鸿蒙5网页开发神器 ArkWeb:让 Web 和原生手拉手跳舞

你有没有想过,在鸿蒙应用里既能用原生代码写高性能界面,又能直接塞进一个网页?这听起来有点像把汉堡和披萨拼在一起,但ArkWeb(方舟Web)真的做到了!今天咱们就聊聊这个神奇的工具,看看它如何让...

MapReduce过程详解及其性能优化(详细)

从JVM的角度看Map和ReduceMap阶段包括:第一读数据:从HDFS读取数据1、问题:读取数据产生多少个Mapper??Mapper数据过大的话,会产生大量的小文件,由于Mapper是基于虚拟...

大数据平台建设需要掌握的14个知识

Q1、大数据分析中的实时推荐是如何实现的?@rein07某证券系统架构师:实时推荐需要使用实时处理框架结合推荐算法,从而做到对数据的实时处理和推荐。实时处理框架有Storm、Flink、Spark...

HDFS可视化管理系统设计与实现(hdfs的可靠性设计,主要依靠哪些机制来实现)

摘要:Hadoop工具核心模块包括分布式文件系统(HadoopDistributedFileSystem,HDFS)和分布式编程模型MapReduce,其中HDFS是Hadoop数据存储处理的...

取消回复欢迎 发表评论: