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

2024-04-20 11:58

本文主要是介绍三步实现自动注册工厂替代switch语句(c++),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

概述

在学长博客里看到了使用自动注册工厂替代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++)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/920204

相关文章

Oracle查询优化之高效实现仅查询前10条记录的方法与实践

《Oracle查询优化之高效实现仅查询前10条记录的方法与实践》:本文主要介绍Oracle查询优化之高效实现仅查询前10条记录的相关资料,包括使用ROWNUM、ROW_NUMBER()函数、FET... 目录1. 使用 ROWNUM 查询2. 使用 ROW_NUMBER() 函数3. 使用 FETCH FI

Python脚本实现自动删除C盘临时文件夹

《Python脚本实现自动删除C盘临时文件夹》在日常使用电脑的过程中,临时文件夹往往会积累大量的无用数据,占用宝贵的磁盘空间,下面我们就来看看Python如何通过脚本实现自动删除C盘临时文件夹吧... 目录一、准备工作二、python脚本编写三、脚本解析四、运行脚本五、案例演示六、注意事项七、总结在日常使用

Java实现Excel与HTML互转

《Java实现Excel与HTML互转》Excel是一种电子表格格式,而HTM则是一种用于创建网页的标记语言,虽然两者在用途上存在差异,但有时我们需要将数据从一种格式转换为另一种格式,下面我们就来看看... Excel是一种电子表格格式,广泛用于数据处理和分析,而HTM则是一种用于创建网页的标记语言。虽然两

Java中Springboot集成Kafka实现消息发送和接收功能

《Java中Springboot集成Kafka实现消息发送和接收功能》Kafka是一个高吞吐量的分布式发布-订阅消息系统,主要用于处理大规模数据流,它由生产者、消费者、主题、分区和代理等组件构成,Ka... 目录一、Kafka 简介二、Kafka 功能三、POM依赖四、配置文件五、生产者六、消费者一、Kaf

使用Python实现在Word中添加或删除超链接

《使用Python实现在Word中添加或删除超链接》在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能,本文将为大家介绍一下Python如何实现在Word中添加或... 在Word文档中,超链接是一种将文本或图像连接到其他文档、网页或同一文档中不同部分的功能。通过添加超

windos server2022里的DFS配置的实现

《windosserver2022里的DFS配置的实现》DFS是WindowsServer操作系统提供的一种功能,用于在多台服务器上集中管理共享文件夹和文件的分布式存储解决方案,本文就来介绍一下wi... 目录什么是DFS?优势:应用场景:DFS配置步骤什么是DFS?DFS指的是分布式文件系统(Distr

NFS实现多服务器文件的共享的方法步骤

《NFS实现多服务器文件的共享的方法步骤》NFS允许网络中的计算机之间共享资源,客户端可以透明地读写远端NFS服务器上的文件,本文就来介绍一下NFS实现多服务器文件的共享的方法步骤,感兴趣的可以了解一... 目录一、简介二、部署1、准备1、服务端和客户端:安装nfs-utils2、服务端:创建共享目录3、服

C#使用yield关键字实现提升迭代性能与效率

《C#使用yield关键字实现提升迭代性能与效率》yield关键字在C#中简化了数据迭代的方式,实现了按需生成数据,自动维护迭代状态,本文主要来聊聊如何使用yield关键字实现提升迭代性能与效率,感兴... 目录前言传统迭代和yield迭代方式对比yield延迟加载按需获取数据yield break显式示迭

Python实现高效地读写大型文件

《Python实现高效地读写大型文件》Python如何读写的是大型文件,有没有什么方法来提高效率呢,这篇文章就来和大家聊聊如何在Python中高效地读写大型文件,需要的可以了解下... 目录一、逐行读取大型文件二、分块读取大型文件三、使用 mmap 模块进行内存映射文件操作(适用于大文件)四、使用 pand

python实现pdf转word和excel的示例代码

《python实现pdf转word和excel的示例代码》本文主要介绍了python实现pdf转word和excel的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价... 目录一、引言二、python编程1,PDF转Word2,PDF转Excel三、前端页面效果展示总结一