自动注册机制的工厂模式
Ruichen Ni
需求来源
以计算力学的材料库为例,所有的材料都继承于材料基类,具体计算时使用的材料由输入文件指定。采用工厂模式可以很好地将子类对象的创建聚合在一起,通过输入文件读入的材料名来确定具体创建哪一种材料。
class Material_Base;
class Material_A : public Material_Base;
class Material_B : public Material_Base;
class Material_C : public Material_Base;
class MaterialFactory
{
public:
static Material_Base* CreateMaterial(string name)
{
if (name == "A")
return new Material_A();
else if (name == "B")
return new Material_B();
else if (name == "C")
return new Material_C();
else
return nullptr;
}
}
当我需要去扩充材料库的时候,除了新写一个子类继承Material_Base
之外,还需要去工厂MaterialFactory
中修改对应的if-else
语句。如果材料库有上百种材料,那么这个if-else
语句块也会有几百行,并且每个if
分支都需要进行语句判断,特别耗时。另外,由于每种材料的属性和参数个数都是不同的,一般都会通过属性卡来实现材料类的初始化
class MaterialProperty_Base;
class MaterialProperty_A : public MaterialProperty_Base;
class MaterialProperty_B : public MaterialProperty_Base;
class MaterialProperty_C : public MaterialProperty_Base;
class Material_Base
{
public:
virtual void Initialization(MaterialProperty_Base* property);
};
因此增加一种材料的时候对应地还得增加一种材料的属性卡,通常材料的属性卡也会通过工厂类进行管理。增加一种材料的操作需要修改代码的很多地方,并且这些地方往往在不同的文件夹层级下,例如属性卡一般倾向于放在IO相关的文件夹下。
综上所述,需要一种代码结构能够将“增加一种材料”的这种需求最方便地实现,最好相应的代码修改都能够在一个地方,而不需要去代码的各个地方修改。
注册机制
注册机制允许我们在运行时动态地添加或修改程序的行为,它会维护一张注册表,用于储存一组函数或对象,这组函数或对象可以在运行时被调用或实例化。注册机制提供了一种灵活的方式来管理和解耦代码,使代码更加模块化和易于管理。
自动注册机制的工厂模式
参考这篇CSDN博客实现基于模板的自动注册工厂模式
工厂类
#include <functional>
#include <memory>
#include <unordered_map>
#include <string>
template<typename T, typename... Args>
class Factory
{
public:
static Factory& Instance()
{
if (!instance_)
instance_ = new Factory<T, Args...>();
return *instance_;
}
void Register(const std::string& name, std::function<std::shared_ptr<T>(Args...)> creator)
{
creators_[name] = creator;
}
std::shared_ptr<T> Create(const std::string& name, Args... args)
{
return creators_.find(name) == creators_.end() ? std::shared_ptr<T>() : creators_[name](args...);
}
private:
Factory() {}
static Factory<T, Args...> *instance_;
//!> Constructor of register class
std::unordered_map<std::string, std::function<std::shared_ptr<T>(Args...)>> creators_;
};
template <typename T, typename... Args>
Factory<T, Args...> *Factory<T, Args...>::instance_ = nullptr;
注册类
#include "Factory.h"
#include <string>
#include <memory>
template <typename Base, typename Impl, typename... Args>
class Register
{
public:
explicit Register(const std::string& name)
{
Factory<Base, Args...> &factory = Factory<Base, Args...>::Instance();
factory.Register(name, [](Args... args)
{ return std::shared_ptr<Base>(new Impl(args...)); });
}
};
使用方式
在材料子类实现的.cpp
文件中增加注册表,这样可以保证所有修改是紧跟着子类实现,而不需要再去工厂类中增加if-else
语句
// Material_A.cpp
Register<Material_Base, Material_A> REG_A("A");
// Material_B.cpp
Register<Material_Base, Material_B> REG_A("B");
// Material_C.cpp
Register<Material_Base, Material_C> REG_A("C");
然后就可以在任何需要材料类实例化的地方通过以下方式创建
auto material = Factory<Material_Base>::Create(material_name);