用C++实现动态生成对象的工厂类
今天工作中遇到一个问题:客户现场有几十种仪表和工控设备需要采集数据,每种仪表和设备都要实现一个通信协议,这个好解决使用工厂模式即可,即定义统一通信协议接口类实现具体的通信协议子类,定义工厂基类实现每个具体通信协议的工厂子类,由具体工厂类创建通信协议对象。但是用户需要根据现场实际情况修改配置文件来实现不同种类不同数量的仪表设备进行通信,这就牵扯到需要根据配置文件动态生成不同的仪表通信协议,普通工厂模式无法完成需求。C++又没有像C#和JAVA一样的反射机制来根据类信息动态创建对象,参考了网上几位博主的代码,我重新写了一个动态生成对象的工厂类,比较适合我遇到的情况。
先定义通信协议接口基类和具体协议类:
#ifndef PROTOCOL_H_
#define PROTOCOL_H_
// protocol.h
// 通信协议接口基类
class ICommProtocolBase {
public:
ICommProtocolBase(){}
virtual ~ICommProtocolBase(){}
virtual bool readdata()=0;
virtual bool writedata()=0;
public:
std::string strProtocolName_; //通信协议类名
std::string strProtocolDesc_; //通信协议类描述
};
// 通信协议类1
class Protocol1 : ICommProtocolBase {
public:
Protocol1() : strProtocolName_("Protocol1"), strProtocolDesc_("通信协议1") {}
virtual ~Protocol1(){}
bool readdata() { return true;}
bool writedata() { return true;}
}
// 通信协议类2
class Protocol2 : ICommProtocolBase {
public:
Protocol2() : strProtocolName_("Protocol2"), strProtocolDesc_("通信协议2") {}
virtual ~Protocol2(){}
bool readdata() { return true;}
bool writedata() { return true;}
}
#endif
动态生成通信协议对象的工厂类的思路是这样的:
1.首先工厂类是个单实例类,所有通信协议对象都由此工厂类构建。
2.工厂类内部保存一个静态映射表map,映射表的key是具体通信协议类的类名,映射表的value是一个lambda函数对象,用于构建通信协议实例。
3.工厂类再定义一个内部模版类,用于通过模版的模版参数对具体通信协议类型进行注册,生成构建函数对象。
4.用宏定义来生成这个内部模版类的静态实例,构造这个内部模版类的对象实例的过程就完成了通信协议类的注册。
5.调用工厂类的生产函数,通过类名构建通信协议对象。
具体工厂类代码如下:
// ptlobjfactory.h
#ifndef _OBJECTFACTORY_H_
#define _OBJECTFACTORY_H_
#include <map>
#include <string>
#include <functional>
#include <memory>
#include "protocol.h"
using IOCommProtocol::ICommProtocolBase;
class IOCommPtlFactory {
public:
//工厂内部模版类,用于注册协议类和其lambda类型的对象构建函数
template<typename T>
struct register_t {
register_t(const std::string& key) {
IOCommPtlFactory::get().getmap().emplace(key, []()->T*{ return new T(); });
}
template<typename... Args>
register_t(const std::string& key, Args... args) {
IOCommPtlFactory::get().getmap().emplace(key, [&]()->T*{ return new T(args...);});
}
};
//生成协议类实例原始指针函数
static ICommProtocolBase* produce(const std::string& key) {
if (getmap().find(key) == getmap().end()) {
return nullptr;
}
return getmap()[key]();
}
//生成协议类实例智能指针函数
static std::unique_ptr<ICommProtocolBase> produce_unique(const std::string& key) {
if (getmap().find(key) == getmap().end()) {
return std::unique_ptr<ICommProtocolBase>(nullptr);
}
return std::unique_ptr<ICommProtocolBase>(produce(key));
}
//生成协议类实例智能指针函数
static std::shared_ptr<ICommProtocolBase> produce_shared(const std::string& key) {
if (getmap().find(key) == getmap().end()) {
return std::shared_ptr<ICommProtocolBase>(nullptr);
}
return std::shared_ptr<ICommProtocolBase>(produce(key));
}
private:
IOCommPtlFactory(){};
IOCommPtlFactory(const IOCommPtlFactory&) = delete;
IOCommPtlFactory(IOCommPtlFactory&&) = delete;
IOCommPtlFactory& operator = (const IOCommPtlFactory&) = delete;
IOCommPtlFactory& operator = (const IOCommPtlFactory&&) = delete;
//工厂类单实例获取函数
static IOCommPtlFactory& get() {
static IOCommPtlFactory instance;
return instance;
}
//将静态映射对象封装在函数内,防止多处引用本头文件时造成的重复定义错误
static std::map<std::string, std::function<ICommProtocolBase*()>>& getmap(){
static std::map<std::string, std::function<ICommProtocolBase*()>> map_;
return map_;
}
};
//生成register_t实例名称的宏
#define REGISTER_PROTOCOL_VNAME(T) reg_ptl_##T##_
//通信协议类名注册宏
#define REGISTER_PROTOCOL(T, classname, ...) static IOCommPtlFactory::register_t<T> REGISTER_PROTOCOL_VNAME(T)(classname, ##__VA_ARGS__);
#endif //_OBJECTFACTORY_H_
对象工厂需要C++11标准或更高标准的编译器。因为使用了智能指针和可变模版参数包。
测试代码如下:
#include "protocol.h"
#include "ptlobjfactory.h"
REGISTER_PROTOCOL(Protocol1, "Protocol1");
REGISTER_PROTOCOL(Protocol2, "Protocol2");
int main() {
ICommProtocolBase* pPtl1 = IOCommPtlFactory::produce("Protocol1");
ICommProtocolBase* pPtl2 = IOCommPtlFactory::produce("Protocol2");
std::cout << pPtl1->strProtocolName_ << " " << pPtl1->strProtocolDesc_ << "\n";
std::cout << pPtl2->strProtocolName_ << " " << pPtl2->strProtocolDesc_ << "\n";
}
测试结果应该是打印出两个通信协议类的类名和描述信息。
工厂类代码还可以进一步改进,让工厂类适应所有类型。目前已经满足我的需求。
如果文章对您有用,请随手点个赞,谢谢!^_^