概述

在学长博客里看到了使用自动注册工厂替代switch语句的文章,想到可以将其用到自己的项目里,就照猫画虎学习着也实现了一个。
这里并不是用其替代创建派生类的传统Factory,而是为了替代服务器中的业务逻辑处理。
记得以前实现的第一个服务器项目:聊天室,里面在解包后,是一长串的switch语句,根据包里类型标志,来决定该如何处理,写起来舒服,可看起来,包括后期维护,实在是太不方便,因为想使用自动注册工厂这种模式来解决这个问题。

我这里的自动注册工厂是针对服务器项目收到包之后进行逻辑处理的语句冗余问题。
但思路与代码基本适用于绝大多数需要使用自动注册工厂的情况。

以前的方式

switch(type)
{case 1:do_login();case 2:do_register();case 3:do_something();case ...........
}

显然,在业务逻辑并不多的时候,这样的方式也无伤大雅,那么假设业务逻辑很多呢,switch语句该有多长,无论是维护还是阅读都很不便。
那么,我们来看下一种方式。

自动注册工厂

逻辑处理基类

这里只简单的实现了基本的构造函数,和逻辑处理函数

class action
{
public:action(){std::cout<<"action"<<std::endl;}virtual void doAction(){std::cout<<"doAction"<<std::endl;}};

登陆逻辑处理派生类

class login_action : public action {public:login_action(){std::cout<<"login_action"<<std::endl;}void doAction(){std::cout<<"do_login_action"<<std::endl;}
};REGISTER_ACTION(login_action, "login_action");

注册逻辑处理派生类

class register_action : public action
{
public:register_action(){std::cout<<"register_action"<<std::endl;}void doAction(){std::cout<<"do_register_action"<<std::endl;}
};REGISTER_ACTION(register_action, "register_action");

工厂类

这个类是我们自动注册工厂的核心类。

第一步

首先,我们要将其设计为单例模式,为了规范,我们将其拷贝构造函数和移动构造函数都设置为私有的,令其不可拷贝与构造,类似于boost::noncopyable。
并定义一个私有变量,为map类型,键为string,值为可返回一个派生类对象的function。
如下:

{
public:
private:factory() = default;factory(const factory&) = delete;factory(factory&&) = delete;static factory &get(){static factory instance;return instance;}std::map<std::string, std::function<action*(void)>> m_map;
};
第二步

在factory内实现一个内部类Register,便于扩展,我将其设置为了模板类型。
为什么要设置为内部类呢,因为设置为内部类我们就可以使用外部类的私有成员(我们为规范,将map设置为私有的),同时也因为其与工厂类本身就是一体,写在一起也更合逻辑。
构造函数:传入一个标志key,将其作为键写入map,值为一个lambda表达式,返回一个派生类对象指针。

    template <typename F>struct Register{Register(const std::string& key){factory::get().m_map.emplace(key, []{return new F();});}template<typename... Args>Register(const std::string& key, Args... args){factory::get().m_map.emplace(key, [&]{return new F(args...);});}};
注:emplace操作是C++11新特性,新引入的的三个成员emlace_front、empace 和 emplace_back,这些操作构造而不是拷贝元素到容器中,
这些操作分别对应push_front、insert 和push_back,允许我们将元素放在容器头部、一个指定的位置和容器尾部。
(目的是减少一次拷贝)
第三步

使用宏来简化工厂注册步骤

#define REGISTER_ACTION_NAME(T) msg_name_##T##_
#define REGISTER_ACTION(T, key, ...) \
static factory::Register<T> REGISTER_ACTION_NAME(T)(key,##__VA_ARGS__)
注:##起将左右字符衔接的作用__VA_ARGS__ 是一个可变参数的宏,很少人知道这个宏,这个可变参数的宏是新的C99规范中新增的。宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的","去掉的作用

工厂类完整代码

class factory
{
public:template <typename F>struct Register{Register(const std::string& key){factory::get().m_map.emplace(key, []{return new F();});}template<typename... Args>Register(const std::string& key, Args... args){factory::get().m_map.emplace(key, [&]{return new F(args...);});}};static action* produce(const std::string& key){auto map = factory::get().m_map;if(map.find(key) == map.end()){throw std::invalid_argument("error");}return map[key]();}private:factory() = default;factory(const factory&) = delete;factory(factory&&) = delete;static factory &get(){static factory instance;return instance;}std::map<std::string, std::function<action*(void)>> m_map;
};#define REGISTER_ACTION_NAME(T) msg_name_##T##_
#define REGISTER_ACTION(T, key, ...) \
static factory::Register<T> REGISTER_ACTION_NAME(T)(key,##__VA_ARGS__)

使用

int main()
{action *login = factory::produce("login_action");action *rter = factory::produce("register_action");login->doAction();rter->doAction();delete(login);delete(rter);
}

输出

结语

我们在学习中可能业务逻辑并不会太多,也就是说,switch语句并不会影响什么,但我们应当在一开始时就养成这么一个好的习惯,用最好的方式去实现自己想要的功能。

三步实现自动注册工厂替代switch语句(c++)相关推荐

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

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

  2. idea switch 没有自动提示 Idea switch 语句设置自动提示 Idea 配置switch语句快捷键 swi idea常用快捷键设置

    idea switch 没有自动提示 Idea switch 语句设置自动提示 Idea 配置switch语句快捷键 idea常用快捷键设置 一.前言 最近在用Idea搬砖(写代码)时,发现写 swi ...

  3. 基于模板的自动注册工厂模式(C++11实现)

    工厂类 factory.h #pragma once#include <functional> #include <memory> #include <unordered ...

  4. 打开word2007总是出现配置进度_电脑玩《原神》出现掉帧延迟卡顿内存不足等问题 教你三步搞定_游戏369...

    全新开放世界RPG<原神>自正式公测以来,凭借丰富的剧情.细腻的建模.多彩的游戏世界,受到了大量玩家的热捧.但是也有一些玩家吐槽说,自己玩原神的时候总是会出现卡顿.延迟等问题,游戏体验极差 ...

  5. 三步搞定【MySql插入中文字符显示???】

    前言 不知道你有没有遇到过这样的情况,插入到数据库的中文数据通通变成了问号. 如果你也有一样的问题,请继续观看我接下来的内容. [第一步]确保visual studio中的编码是utf-8       ...

  6. 用重构指导Clean Code(二):依恋情结和switch语句

    书接上回,我们继续聊如何用重构指导Clean Code. 在Clean Code的3.4节中有这样一段代码(代码清单3-4).(第3章主要讲的是函数,而3.4节讨论的是switch语句.) publi ...

  7. c语言习题---(switch语句)

    这里写目录标题 前言 题目合集 第一题解析: 第二题解析 第三题解析 第四题解析 第五题解析 前言 >亲爱的小伙伴们大家好啊,这篇文章都是关于switch语句的一些练习题,目的为了让大家能够学的 ...

  8. [转载] Python中的switch语句的替代品

    参考链接: Java中的switch语句 Python 中没有 switch语句. 一般用if-else 语句可以替代switch语句,今天学习了使用字典的映射来代替switch语句.Mark一下 d ...

  9. 仅需三步完成微信小程序注册

    因为自己做些项目,所以边做边整理一些笔记,方便随时查阅及后期内容补充. 微信小程序注册步骤分为三步: 1,帐号信息 2,邮箱激活 3,信息登记 1,帐号信息-进入微信公众平台官方网址,填写账号信息,注 ...

最新文章

  1. 让机器“看见”:图像数据的特征提取方法
  2. Altair Compose 2020直装中文版
  3. 11.python并发入门(part4 死锁与递归锁)
  4. 在python中使用csv库以字典格式读写csv文件
  5. 在datagrid里面生成指定的DataView ,进行过滤.把符合条件的数据显示出来
  6. NYOJ-491 幸运三角形
  7. ubuntu shell脚本出错 dash
  8. (树莓派、Arduino、物联网、智能家居、机器人)传感器、机械装置、电子元件
  9. 【GD32F303开发之串口通信】
  10. 期货开户公司受到证监会的监管
  11. 网页img 居中办法
  12. 代码性能优化--NENO编程
  13. 免费edu邮箱申请注册地址
  14. JavaMail实现发送邮件程序
  15. mongodb知识点汇总
  16. 常见的exceptions总结
  17. 添加 polygon 网路
  18. 面试题:为什么ConcurrentHashMap的读操作不需要加锁?
  19. Packet Tracer - 交换机和终端设备的基本配置
  20. linux无法打开或写入文件格式,Centos系统下“无法打开并写入文件”问题的解决...

热门文章

  1. 阿里云建站产品有哪些?如何选择?
  2. GN+NINJA环境搭建(MacOS Windows)
  3. 【Redis】客户端RedisClient
  4. 纯Css制作tab选项卡
  5. Blender插件开发:bpy.utils模块
  6. 通过修改程序解决win7下应用程序兼容性助手弹出
  7. 了解 Tornado 框架
  8. 天兔(Lepus)监控操作系统(OS)安装配置
  9. 绝技不是一天练成 新手入库停车技巧图解
  10. 【唐诗学习】五、山水田园诗派代表