当前位置: 首页 > news >正文

c++模板实现抽象工厂

类似于rime的rime::Class<factory type, product type>实现方式。

 

C++模板实现的通用工厂方法模式

1.工厂方法(Factory Method)模式

工厂方法模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中。核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品。

工厂方法模式结构示意图

工厂方法模式是简单工厂模式的衍生,解决了许多简单工厂模式的问题。首先完全实现‘开-闭 原则’,实现了可扩展。其次更复杂的层次结构,可以应用于产品结果复杂的场合。

工厂方法模式很好用,但在代码层面上,为每一个产品都编写一个对应的工厂,这无疑很麻烦,而且不同的Product要写不同Creator。想想你在写一个GUI库,基本控件为Widget类,由其派生下了TextBox,Button,Label等几十个控件,那你需要为这几十个控件分别编写对应的ConcreteCreator。天,这太麻烦了吧!而如果你还要写一个RPG小游戏,需要从Person类派生下如NPC,Hero,Emeny,Ally等又是十几个类,而且还要编写一个对应于Person的Creator....

 

2.模板实现工厂方法模式

   面对这种类型造成的多态,C++模板就要出场了。现在情况是这样的,Product和ConcreteProduct都已经写好,我们要为其编写对应的工厂类。可以看出来,Product对应对Creator,ConcreteProduct对应于ConcreteCreator。也就是说,如果想编写通用的ConcreteCreator,就得将ConcreteProduct抽象,想编写通用的Creator,就得将Product抽象。而我们最后会将这2者都抽象了。

   首先编写通用的ConcreteCreator

 

[cpp]  view plain copy
 
  1. // 具体工厂类  
  2. // Product为抽象产品,ConcreteProduct为具体产品  
  3. template<typename Product, typename ConcreteProduct>  
  4. class ConcreteCreator  
  5. {  
  6. public:  
  7.     // 生产一个具体产品  
  8.     static Product* createProduct()  
  9.     {  
  10.         return new ConcreteProduct();  
  11.     }  
  12. };  

 

注意createProduct()是静态的,是因为该函数要被保存起来的,静态函数只需保存其函数指针即可,而普通的成员函数还要额外保存一个对象指针,这无疑更麻烦了。 也就是我们不需要这样用: 

 

[cpp]  view plain copy
 
  1. ConcreteCreator<Product, ConcreteProduct> concreteCreator;     
  2. Product* p = concreteCreator.createProduct();  

 

 

只需要这样写:

 

[cpp]  view plain copy
 
  1. Product* p = ConcreteCreator<Product, ConcreteProduct>::createProduct();  

 

   下面是Creator的实现

 

[cpp]  view plain copy
 
  1. // 抽象工厂  
  2. // Product为抽象产品  
  3. template<typename Product>  
  4. class Creator  
  5. {  
  6.     // 单例实现  
  7. public:  
  8.     static Creator& Instance()  
  9.     {  
  10.         static Creator<Product> instance;  
  11.         return instance;  
  12.     }  
  13. private:  
  14.     Creator() {}  
  15.     ~Creator() {}  
  16.     Creator(Creator&);  
  17.     // 对外接口  
  18. public:  
  19.     typedef Product* (*CreateProductDelegate)( ); // 生产产品的产品 
  20.     typedef std::map<std::string, CreateProductDelegate> MapRegisterCreatorItem;  
  21.     // 根据具体产品生成具体工厂  
  22.     // 并将具体产品注册进抽象工厂  
  23.     // ConcreteProduct为具体产品  
  24.     template<typename ConcreteProduct>  
  25.     void registerCreator(const std::string& _type)  
  26.     {  
  27.         mConcreteCreators[_type] = ConcreteCreator<Product, ConcreteProduct>::createProduct;  
  28.     }  
  29.     // 删除所有具体工厂  
  30.     void unregisterAllCreators()  
  31.     {  
  32.         mConcreteCreators.clear();  
  33.     }  
  34.     // 生产类型为_type的产品  
  35.     // 失败返回0  
  36.     Product* createProduct(const std::string& _type)  
  37.     {  
  38.         MapRegisterCreatorItem::iterator type = mConcreteCreators.find(_type);  
  39.         if (type != mConcreteCreators.end())  
  40.         {  
  41.             CreateProductDelegate create = type->second;  
  42.             if (create != 0)  
  43.                 return create();  
  44.         }  
  45.         return 0;  
  46.     }  
  47. private:  
  48.     MapRegisterCreatorItem mConcreteCreators; // 保存所有注册过的具体产品 
  49. };  

 

下面来简单解释一下上面的代码。

首先Creator实现了单例模式,这只是为了方便使用,你也可以不实现为单例。

Creator里面保存了所有注册过的具体工厂,具体工厂在注册时被构建,每个具体工厂对应一个名字(string类型)。

createProduct即为工厂方法,外部通过此接口创建产品,创建时仅需提供具体工厂的名字即可。

我们以RPG游戏那个作为例子。要创建一个Person的Creator,可以这样写

 

[cpp]  view plain copy
 
  1. typedef Creator<Person> PersonCreator;  
  2. PersonCreator& factory = PersonCreator::Instance();  
  3. factory.registerCreator<Person>("Person");  
  4. factory.registerCreator<Hero>("Hero");  
  5. factory.registerCreator<NPC>("NPC");  

 

这样即完成了注册过程,你可以继续为其他类型注册具体工厂。

要创建一个NPC,可以这样写

 

[cpp]  view plain copy
 
  1. Person* npc = factory.createProduct("NPC");  
[cpp]  view plain copy
 
  1.   

 

3.优缺点

此方法的优点很明显,就是用起来很方便。由于Creator支持不同类型,你不要为不同的产品编写不同的Creator。而且其提供了注册具体工厂的方法,你仅需要写一行代码即可为具体产品生成具体的工厂,而不需要编写专门的类。

而且其注册过程是可以在运行时修改的,也就是说你可以在运行时动态地注册或反注册具体工厂,这样灵活性就很大了。

缺点的话就是不支持构造函数参数,也就是说在创建产品时不支持参数。当然可以为不同数量参数编写具体的模板Creator。但其实个人并不提倡提供无参构造函数。我认为像这类产品类,都应该提供一个无参的构造函数版本,然后提供get,set函数来修改内部成员变量。

转载于:https://www.cnblogs.com/springbarley/p/3365571.html

相关文章:

  • 1、工程构建、打包的一些经验
  • VI 你不知道的事
  • try{}----------catch{}的作用
  • Docker Compose 原理
  • SQLSERVER 里SELECT COUNT(1) 和SELECT COUNT(*)哪个性能好?
  • hfrk2410_a1.1开发板移植linux-2.6.32.27--网卡篇(cs8900)
  • VS2005相关----不能添加新项
  • nexus启动错报:1067 与jdk9相关
  • 谈谈VIP漂移那点破事
  • 程序员的几款利器
  • 数据库系统设计_银行业务管理系统
  • 云桌面、云课堂究竟是什么?企业追捧是坑还是福?
  • 对hibernate框架的认知及总结
  • 三个常用的PHP图表类库
  • 在android源码中单独编译modkoid工程遇见的问题
  • [Vue CLI 3] 配置解析之 css.extract
  • 【React系列】如何构建React应用程序
  • 2017-09-12 前端日报
  • canvas 绘制双线技巧
  • const let
  • ReactNativeweexDeviceOne对比
  • Spring声明式事务管理之一:五大属性分析
  • Vue实战(四)登录/注册页的实现
  • 安装python包到指定虚拟环境
  • 动手做个聊天室,前端工程师百无聊赖的人生
  • 两列自适应布局方案整理
  • 手机端车牌号码键盘的vue组件
  • 学习HTTP相关知识笔记
  • 一个JAVA程序员成长之路分享
  • 用 vue 组件自定义 v-model, 实现一个 Tab 组件。
  • nb
  • 大数据全解:定义、价值及挑战
  • ###STL(标准模板库)
  • #我与Java虚拟机的故事#连载13:有这本书就够了
  • (DenseNet)Densely Connected Convolutional Networks--Gao Huang
  • (HAL)STM32F103C6T8——软件模拟I2C驱动0.96寸OLED屏幕
  • (poj1.2.1)1970(筛选法模拟)
  • (二)Eureka服务搭建,服务注册,服务发现
  • (附源码)springboot金融新闻信息服务系统 毕业设计651450
  • (企业 / 公司项目)前端使用pingyin-pro将汉字转成拼音
  • .Net 8.0 新的变化
  • .NET Compact Framework 多线程环境下的UI异步刷新
  • .NET Core 版本不支持的问题
  • .NET Core实战项目之CMS 第十二章 开发篇-Dapper封装CURD及仓储代码生成器实现
  • .NET Windows:删除文件夹后立即判断,有可能依然存在
  • .NET3.5下用Lambda简化跨线程访问窗体控件,避免繁复的delegate,Invoke(转)
  • .net6+aspose.words导出word并转pdf
  • .Net8 Blazor 尝鲜
  • .Net语言中的StringBuilder:入门到精通
  • [2016.7 Day.4] T1 游戏 [正解:二分图 偏解:奇葩贪心+模拟?(不知如何称呼不过居然比std还快)]
  • [2021ICPC济南 L] Strange Series (Bell 数 多项式exp)
  • [AndroidStudio]_[初级]_[修改虚拟设备镜像文件的存放位置]
  • [ASP]青辰网络考试管理系统NES X3.5
  • [C++] 默认构造函数、参数化构造函数、拷贝构造函数、移动构造函数及其使用案例
  • [CentOs7]iptables防火墙安装与设置