三步实现自动注册工厂替代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

相关文章

使用Python实现矢量路径的压缩、解压与可视化

《使用Python实现矢量路径的压缩、解压与可视化》在图形设计和Web开发中,矢量路径数据的高效存储与传输至关重要,本文将通过一个Python示例,展示如何将复杂的矢量路径命令序列压缩为JSON格式,... 目录引言核心功能概述1. 路径命令解析2. 路径数据压缩3. 路径数据解压4. 可视化代码实现详解1

PyQt6/PySide6中QTableView类的实现

《PyQt6/PySide6中QTableView类的实现》本文主要介绍了PyQt6/PySide6中QTableView类的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学... 目录1. 基本概念2. 创建 QTableView 实例3. QTableView 的常用属性和方法

PyQt6/PySide6中QTreeView类的实现

《PyQt6/PySide6中QTreeView类的实现》QTreeView是PyQt6或PySide6库中用于显示分层数据的控件,本文主要介绍了PyQt6/PySide6中QTreeView类的实现... 目录1. 基本概念2. 创建 QTreeView 实例3. QTreeView 的常用属性和方法属性

Android使用ImageView.ScaleType实现图片的缩放与裁剪功能

《Android使用ImageView.ScaleType实现图片的缩放与裁剪功能》ImageView是最常用的控件之一,它用于展示各种类型的图片,为了能够根据需求调整图片的显示效果,Android提... 目录什么是 ImageView.ScaleType?FIT_XYFIT_STARTFIT_CENTE

pandas中位数填充空值的实现示例

《pandas中位数填充空值的实现示例》中位数填充是一种简单而有效的方法,用于填充数据集中缺失的值,本文就来介绍一下pandas中位数填充空值的实现,具有一定的参考价值,感兴趣的可以了解一下... 目录什么是中位数填充?为什么选择中位数填充?示例数据结果分析完整代码总结在数据分析和机器学习过程中,处理缺失数

Golang HashMap实现原理解析

《GolangHashMap实现原理解析》HashMap是一种基于哈希表实现的键值对存储结构,它通过哈希函数将键映射到数组的索引位置,支持高效的插入、查找和删除操作,:本文主要介绍GolangH... 目录HashMap是一种基于哈希表实现的键值对存储结构,它通过哈希函数将键映射到数组的索引位置,支持

Pandas使用AdaBoost进行分类的实现

《Pandas使用AdaBoost进行分类的实现》Pandas和AdaBoost分类算法,可以高效地进行数据预处理和分类任务,本文主要介绍了Pandas使用AdaBoost进行分类的实现,具有一定的参... 目录什么是 AdaBoost?使用 AdaBoost 的步骤安装必要的库步骤一:数据准备步骤二:模型

使用Pandas进行均值填充的实现

《使用Pandas进行均值填充的实现》缺失数据(NaN值)是一个常见的问题,我们可以通过多种方法来处理缺失数据,其中一种常用的方法是均值填充,本文主要介绍了使用Pandas进行均值填充的实现,感兴趣的... 目录什么是均值填充?为什么选择均值填充?均值填充的步骤实际代码示例总结在数据分析和处理过程中,缺失数

Java对象转换的实现方式汇总

《Java对象转换的实现方式汇总》:本文主要介绍Java对象转换的多种实现方式,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录Java对象转换的多种实现方式1. 手动映射(Manual Mapping)2. Builder模式3. 工具类辅助映

Go语言开发实现查询IP信息的MCP服务器

《Go语言开发实现查询IP信息的MCP服务器》随着MCP的快速普及和广泛应用,MCP服务器也层出不穷,本文将详细介绍如何在Go语言中使用go-mcp库来开发一个查询IP信息的MCP... 目录前言mcp-ip-geo 服务器目录结构说明查询 IP 信息功能实现工具实现工具管理查询单个 IP 信息工具的实现服