目标
了解C++里的Factory模式应用场景、实现方法,采用模板实现带来的便利。
来源
- A C++ Object Factory
- Automatic object factory in C++
- Register an object creator in object factory
- C++11实现一个自动注册的工厂
用途
一系列类继承自基类,均可以执行某个动作,可以根据类的Key获取类实例进行动作。
原始版本及存在的问题
class IObject
{
public:
virtual void run() = 0;
virtual ~IObject() {};
};
class Object1:public IObject
{
public:
~Object1()
{
;
}
virtual void run() override
{
std::cout<<"Object1\n";
}
};
class Object2 :public IObject
{
public:
~Object2()
{
;
}
virtual void run() override
{
std::cout << "Object2\n";
}
};
class ObjectFactory
{
public:
static std::shared_ptr<IObject> create(const std::string& key)
{
if(key == "Object1")
return std::make_shared<Object1>();
if(key == "Object2")
return std::make_shared<Object2>();
return nullptr;
}
};
void original_test()
{
auto pVal1 = ObjectFactory::create("Object1");
auto pVal2 = ObjectFactory::create("Object2");
if(pVal1)
pVal1->run();
if(pVal2)
pVal2->run();
}
这是最常规的实现方法,一旦新增类,则需要调整factory的创建方法,一旦这些代码被封装成库就没有扩展的可能;
我们希望没有非必要的依赖,避免调整类定义,尽可能简单就能够注册新类。
实现思路
- 准备基类
- Factory单例
- 注册类构造方法
- 获取类实例
调整版本
基类
class IObject
{
public:
virtual void run() = 0;
virtual ~IObject() {};
};
Factory单例
class ObjectFactory
{
public:
static ObjectFactory* Instance()
{
static ObjectFactory factory;
return &factory;
}
};
注册类构造方法
void registerObjectFunc(const std::string& name, std::function<IObject*()> func)
{
funcs_[name] =func;
}
private:
std::map<std::string,std::function<IObject*()>> funcs_;
构造类实例
std::shared_ptr<IObject> create(const std::string& key)
{
auto it = funcs_.find(key);
if (it != funcs_.end())
return std::shared_ptr<IObject>(it->second());
return nullptr;
}
使用方法
//类1
class Object1 :public IObject
{
public:
~Object1()
{
;
}
virtual void run() override
{
std::cout << "Object1\n";
}
};
//类2
class Object2 :public IObject
{
public:
~Object2()
{
;
}
virtual void run() override
{
std::cout << "Object2\n";
}
};
//使用
auto pVal1= ObjectFactory::Instance()->create("Object1");
auto pVal2 = ObjectFactory::Instance()->create("Object2");
if (pVal1)
pVal1->run();
if (pVal2)
pVal2->run();
新类注册
static ObjectRegisterHelper Object1_ObjectRegisterHelper("Object1", []()->IObject* { return new Object1(); });
static ObjectRegisterHelper Object2_ObjectRegisterHelper("Object2", []()->IObject* { return new Object2(); });
使用宏进行新类注册
辅助注册:
class ObjectRegisterHelper
{
public:
ObjectRegisterHelper(const char* key, std::function<IObject*()> func)
{
ObjectFactory::Instance()->registerObjectFunc(key,func);
}
};
#define REGISTER_OBJECT(className,key) \
static ObjectRegisterHelper className##ObjectRegisterHelper(key,[]()->className*{ return new className();})
使用方式:
REGISTER_OBJECT(Object1,"Object1");
REGISTER_OBJECT(Object2, "Object2");
使用模板进行新类注册
辅助注册:
template<typename T>
class ObjectRegister
{
public:
ObjectRegister(const char* key)
{
ObjectFactory::Instance()->registerObjectFunc(key,[](){
return new T();
});
}
};
使用方式:
static ObjectRegister<Object1> register1("Object1");
static ObjectRegister<Object2> register2("Object2");
自动注册的实现
以上的方案都需要在外部定义静态变量用来进行构造方法注册,还有方法无需外部定义:
将静态变量包裹在类中
约束Key获取接口
每个类定义均有静态接口Key()
来获取Key供注册使用
template<typename T>
void registerT()
{
//std::cout << "RegisterT " << T::Key() << "\n";
funcs_[T::Key()] = []()->IEntry* { return new T(); };
}
定义辅助类
template<typename T>
struct RegisterClass
{
RegisterClass()
{
Factory::Instance().registerT<T>();
}
};
自动注册类
I为基类,新类继承自AutoRegister
自动附带注册类
template<typename T, typename I>
struct AutoRegister :public I
{
AutoRegister()
{
//®ister_;
}
public:
static RegisterClass<T> register_;
};
//模板类静态变量声明->(C++14的变量模板)
template<typename T, typename I>
RegisterClass<T> AutoRegister<T, I>::register_;
使用
class Object :public AutoRegister<Object, IEntry>
{
public:
void run()
{
std::cout << "Object\n";
}
//Key配置接口
static const char* Key()
{
//由于VisualStudio2015不支持变量模板,这个可以用来声明静态变量
Object::register_;
//在支持变量模板的C++编译器中这个是不需要的
return "Object";
}
};
调整注册辅助类
C++11新特性:内部类可以通过外部类的实例访问外部类的私有成员;
struct Factory
{
public:
template<typename T>
struct register_helper
{
register_helper(const char* key)
{
//获取外部类实例的私有成员变量
Instance().funcs_[key] = []()->IEntry* { return new T(); };
}
};
模板注册免命名
之前使用宏进行新类注册会自动合并出静态辅助类实例名称,而使用模板进行新类注册则需要保证名称不重复,否则编译报错,可以采用以下方式避免该问题:
template<typename T>
struct register_agent
{
static Factory::register_helper<T> helper;
};
//TObject的注册辅助类静态实例
decltype(register_agent<TObject>::helper) register_agent<TObject>::helper("TObject");
//Object的
decltype(register_agent<Object>::helper) register_agent<Object>::helper("Object");
其它调整
通过实现factory模板能够实现统一的factory。
学到的内容
- Factory模式的实现
- 静态变量在自动注册等处理方面的应用
- 在一些场景下如何使用模板替代宏