原文地址:http://geek.csdn.net/news/detail/48963

工厂方法是最简单地创建派生类对象的方法,也是很常用的,工厂方法内部使用switch-case根据不同的key去创建不同的派生类对象,下面是一个伪代码。

Message* create(int type)
{switch (type) {case MSG_PGSTATS:m = new MPGStats;break;case MSG_PGSTATSACK:m = new MPGStatsAck;break;case CEPH_MSG_STATFS:m = new MStatfs;break;case CEPH_MSG_STATFS_REPLY:m = new MStatfsReply;break;case MSG_GETPOOLSTATS:m = new MGetPoolStats;break;default:break;}
}

随着时间的流逝,消息种类越来越多,这个switch-case会越来越长,我在一个开源项目中看到过一百多个case语句,显然这种简单工厂已经不堪负荷,这样的代码对于维护者来说也是一个噩梦。要消除这些长长的switch-case语句是一个需要解决的问题,而自动注册的对象工厂则是一个比较优雅的解决方案。
自动注册的对象工厂遵循了开放-封闭原则,新增对象时无需修改原有代码,仅仅需要扩展即可,彻底地消除了switch-case语句。

实现方法
自动注册的对象工厂的实现思路如下:
1. 提供一个单例工厂对象。
2. 工厂注册对象(保存创建对象的key和构造器)。
3. 利用辅助类,在辅助类对象的构造过程中实现目标对象地注册。
4. 利用一个宏来生成辅助对象。
5. 在派生类文件中调用这个宏实现自动注册。
其中,需要注意的是,对象工厂并不直接接存对象,而是对象的构造器,因为对象工厂不是对象池,是对象的生产者,允许不断地创建实例,另外,这样做还实现了延迟创建。另外一个要注意的地方是借助宏来实现自动注册,本质上是通过宏来定义了很多全局的静态变量,而这些静态变量仅仅是为了实现自动注册,并没有实际的意义。

下面来看看如何用C++11来实现这个自动注册的对象工厂。
一个单例的对象工厂代码

struct factory
{static factory& get(){static factory instance;return instance;}
private:factory() {};factory(const factory&) = delete;factory(factory&&) = delete;static std::map<std::string, std::function<Message*()>> map_;
};

在C++11中单例的实现非常简单,返回一个静态局部变量的引用即可,而且这个方法还是线程安全的, 因为C++11中静态局部变量的初始化是线程安全的。工厂内部有一个map,map的值类型为一个function,是对象的构造器。

对象工厂的辅助类的代码

struct factory
{template<typename T>struct register_t{register_t(const std::string& key){factory::get().map_.emplace(key, []{ return new T; });}};
private:inline static factory& get(){static factory instance;return instance;}static std::map<std::string, FunPtr> map_;
};

对象工厂的辅助类register_t是工厂类的一个内部模版类,非常简单,只有一个构造函数,这个构造函数中调用了factory的私有变量map_,并往map_中插入了key和泛型对象的构造器。 这里用到了C++11的一个新特性:内部类可以通过外部类的实例访问外部类的私有成员,所以register_t可以直接访问factory的私有变量map_。

自动注册的代码

#define REGISTER_MESSAGE_VNAME(T) reg_msg_##T##_
#define REGISTER_MESSAGE(T, key, ...) static factory::register_t<T> REGISTER_MESSAGE_VNAME(T)(key, __VA_ARGS__);

在派生类中调用宏注册自己:

class Message1 : public Message
{//……
};REGISTER_MESSAGE(Message1, "message1");

自动注册的关键是通过一个宏来生成静态全局的register_t的实例,因为register_t的实例是用来向工厂注册目标对象的构造器。所以仅仅需要在派生类中调用这个宏就可以实现自动至注册了,而无需修改原有代码。
我们还可以添加智能指针接口,无需让用户管理原始指针,甚至让工厂能创建带任意参数的对象。

Factory最终的实现

#include <map>
#include <string>
#include <functional>
#include <memory>
#include "Message.hpp"struct factory
{template<typename T>struct register_t{register_t(const std::string& key){factory::get().map_.emplace(key, [] { return new T(); });}template<typename... Args>register_t(const std::string& key, Args... args){factory::get().map_.emplace(key, [&] { return new T(args...); });}};static Message* produce(const std::string& key){if (map_.find(key) == map_.end())throw std::invalid_argument("the message key is not exist!");return map_[key]();}static std::unique_ptr<Message> produce_unique(const std::string& key){return std::unique_ptr<Message>(produce(key));}static std::shared_ptr<Message> produce_shared(const std::string& key){return std::shared_ptr<Message>(produce(key));}private:factory() {};factory(const factory&) = delete;factory(factory&&) = delete;static factory& get(){static factory instance;return instance;}static std::map<std::string, std::function<Message*()>> map_;
};std::map<std::string, std::function<Message*()>> factory::map_;#define REGISTER_MESSAGE_VNAME(T) reg_msg_##T##_
#define REGISTER_MESSAGE(T, key, ...) static factory::register_t<T> REGISTER_MESSAGE_VNAME(T)(key, __VA_ARGS__);

示例

class Message
{
public:virtual ~Message() {}virtual void foo() {}
};#include "Message.hpp"
#include "MsgFactory.hpp"class Message1 : public Message
{
public:Message1(){std::cout << "message1" << std::endl;}Message1(int a){std::cout << "message1" << std::endl;}~Message1(){}void foo() override{std::cout << "message1" << std::endl;}
};//REGISTER_MESSAGE(Message1, "message1", 2);
REGISTER_MESSAGE(Message1, "message1");#include "Message1.hpp"int main()
{Message* p = factory::produce("message1");p->foo();   //Message1delete p;
...auto p2 = factory::produce_unique("message1");p2->foo();
}

总结: 
使用C++11,仅仅需要几十行代码就可以实现一个自动注册的对象工厂,消除了长长的swithc-case语句,还遵循了开闭原则,简洁而优雅。 
完整的代码: https://github.com/qicosmos/cosmos/tree/master/self-register-factory

用C++实现一个自动注册的工厂相关推荐

  1. c++11实现一个自动注册的工厂模式

    实现动机 最近项目中需要用到工厂模式,但是普通的工厂模式面临一个问题,每新增一个派生类,都需要在工厂中加一个case分支,这样就会频繁地修改工厂的代码,而且随着派生类越来越多,case分支也逐渐增多, ...

  2. C++11实现一个自动注册的工厂

    转自:https://www.cnblogs.com/qicosmos/p/5090159.html 实现动机 工厂方法是最简单地创建派生类对象的方法,也是很常用的,工厂方法内部使用switch-ca ...

  3. C++中实现自动注册的工厂

    很长时间没有更新博客了,不是博主懒,是因为博主在帮研究生妹子弄硕士论文,定位方面的,被imu,行人航迹,激光匹配各种虐,不说了,都是泪. Factory模式,在工程中多用了创建派生类实例,内部一般使用 ...

  4. 用python写一个自动注册脚本_js自己写脚本自动操作注册插件基于chrome浏览器

    大家好!又到了一周的福利时间,今天给大家一个福利,以后抢票不需要手动刷新页面了,直接用你自己写的插件来控制,事先声明,本人是js菜鸟,所以今天带来的例子都是低级的例子 我自己写了一个ajax添加的程序 ...

  5. 用python写一个自动注册脚本_python写的自动登录人人网的脚本

    我今天给大家分享一个我自己用 #!/usr/bin/env python #encoding=utf-8 import sys import re import urllib2 import urll ...

  6. 用python写一个自动注册脚本_python实现自动化上线脚本的示例

    程序说明: 本程序实现将开发程序服务器中的打包文件通过该脚本上传到正式生产环境(注:生产环境和开发环境不互通) 程序基本思路: 将开发环境中的程序包拷贝到本地堡垒机 将程序包进行解压 获得解压后的文件 ...

  7. 用python写一个自动注册脚本_python实现自动登录

    利用python,可以实现填充网页表单,从而自动登录WEB门户. (注意:以下内容只针对python3) 环境准备: (1)安装python (2)安装splinter,下载源码 python set ...

  8. 自动注册工厂消灭switch-case

    之前在代码重构书中有了解过java中的反射机制可以消除switch-case,详情可以参考这篇博客http://blog.csdn.net/wwh578867817/article/details/4 ...

  9. 三步实现自动注册工厂替代switch语句(c++)

    概述 在学长博客里看到了使用自动注册工厂替代switch语句的文章,想到可以将其用到自己的项目里,就照猫画虎学习着也实现了一个. 这里并不是用其替代创建派生类的传统Factory,而是为了替代服务器中 ...

最新文章

  1. FoxMail的25号端口被占用问题
  2. SQL Server:使用 PIVOT 行转列和 UNPIVOT 列转行
  3. 清除距今天7天的日志
  4. HDU 1285 - 确定比赛名次(拓扑排序)
  5. 一次性通过软考网工方案
  6. spark之2:原理介绍
  7. JAVA设置新视口,java – 在更大的图像上移动视口; JLablel JScrollPane
  8. LeetCode 1218. 最长定差子序列(哈希map)
  9. Java程序员的级别定义: 对号入座, 你在哪个阶段心里要有点数
  10. python没基础能自学吗-没学过编程可以自学python吗
  11. 小程序----API
  12. Fgui切割图集为散图
  13. 手把手带你SQLite3快速入门
  14. 用最火的python实现最常用、最靓、最实用图表~~
  15. java毕业设计蛋糕店会员系统Mybatis+系统+数据库+调试部署
  16. 北京化工大学2018年10月程序设计竞赛 F. 罗dalao的密码(递归)
  17. LeetCode 古生物血缘远近判定 ( dp )
  18. IIS服务器的安全保护措施
  19. HTML onsubmit 事件属性
  20. 官能化poss甲基丙烯酰氧丙基笼型聚倍半硅氧烷

热门文章

  1. 秒杀系统设计(分布式微服务)
  2. php ip 城市,php如何根据IP地址获取所在城市
  3. 合成作战系统开发解决方案,重点人员管控平台建设
  4. 03crawler02 爬取贴吧排名, 制作图片集
  5. 申请美国访问学者不可忽略的问题
  6. 低代码灵魂三问:熬不住了?骗不了了?技术不行了?
  7. 【postgresql数据库】数据库整体知识完整版-2023电子科技大学期末复习
  8. 可信区块链评测大揭晓 智链ChainNova获可信区块链预测试证书
  9. 大力哥谈 DALI - DALI 很牛?DALI 很 Low!
  10. Android(Lollipop/5.0) Material Design(七) 自定义动画