C++ 类生成管理器, 摆脱if/else
在开发中,经常遇到根据配置内容,生成对应的类, 例如:
name = "Product_A", 则生成产品A的类,
name = "Product_B", 则生成产品B的类,
产品A, 产品B都继承于同一个基类。
比较简单且常用的一个写法是这样的:
#include <iostream> using namespace std; class Base { public: Base(){ //...}; }; }; class A : public Base { public: A(){ //...}; }; }; class B : public Base { public: B(){ //...}; }; }; Base* creator(std::string name) { if (name == "Product_A") { return new A(); } else if (name == "Product_B") { return new B(); } return nullptr; }
这样写,虽然能满足需求,但是不够优雅,对于每一个产品大类(例如另外一种产品, 都继承于Base2),都要写这样一坨if/else。
有没有更优雅的写法呢? 利用模板跟可变参数实现一个创建管理器:
#include <map> using namespace std; /* B : 基类 * T : B的派生类 * P : T的构造函数所需参数 */ template <class B, class... P> class CreateFctBase { public: virtual ~CreateFctBase() = default; virtual B* create(P... args) = 0; }; template <class B, class T, class... P> class CreateFct : public CreateFctBase<B, P...> { public: virtual ~CreateFct() = default; B* create(P... args) { return new T(args...); } }; template <class B, class... P> class Factory { public: Factory() = default; virtual ~Factory() { for (auto& m : m_mapStr2Obj) { delete m.second; } m_mapStr2Obj.clear(); } public: void regist(std::string strName, CreateFctBase<B, P...>* creator) { m_mapStr2Obj[strName] = creator; } B* create(std::string strName, P... args) { auto it = m_mapStr2Obj.find(strName); if (it != m_mapStr2Obj.end()) { return it->second->create(args...); } return nullptr; } private: std::map<std::string, CreateFctBase<B, P...>*> m_mapStr2Obj; };
思路是, 通过模板的参数, 保存派生类的类型, 将字符串->类, 通过map<string, 模板类> 存储。
使用如下:
#include <iostream> #include "creater.h" class A { public: virtual void print() { std::cout << a << std::endl; } public: int a = 1; }; class A1 : public A { virtual void print() { A::print(); std::cout << a1 << std::endl; } public: int a1 = 11; }; class B { public: B(std::string str) { } public: virtual void print() { std::cout << a << std::endl; } public: int a = 2; }; class B1 : public B { public: B1(std::string str) : B(str) { } virtual void print() { B::print(); std::cout << a1 << std::endl; } public: int a1 = 22; }; class C { public: C(int i, std::string str) : a(i), name(str) { } public: virtual void print() { std::cout << a << std::endl; } public: int a = 0; std::string name; }; class C1 : public C { public: C1(int i, std::string str) : C(i, str) { } virtual void print() { std::cout << "this is C1:" << std::endl; std::cout << "--- members:" << a << " " << a1 << name << std::endl; } public: int a1 = 22; }; class C2 : public C { public: C2(int i, std::string str) : C(i, str) { } virtual void print() { std::cout << "this is C2:" << std::endl; std::cout << "--- members:" << a << " " << a12 << name << std::endl; } public: int a12 = 222; }; int main() { Factory<A> AFact; AFact.regist("a1", new CreateFct<A, A1>()); auto pA = AFact.create("a1"); pA->print(); Factory<B, std::string> BFact; BFact.regist("a1", new CreateFct<B, B1, std::string>()); auto pB = BFact.create("a1", "name"); pB->print(); Factory<C, int, std::string> CFact; CFact.regist("c1", new CreateFct<C, C1, int, std::string>()); CFact.regist("c2", new CreateFct<C, C2, int, std::string>()); auto pC1 = CFact.create("c1", 1, "name_c1"); auto pC2 = CFact.create("c2", 1, "name_c2"); pC1->print(); pC2->print(); return 0; }
这样基本摆脱了if/else的创建模式。但是像
CFact.regist("c1", new CreateFct<C, C1, int, std::string>());
CFact.regist("c2", new CreateFct<C, C2, int, std::string>());
这种,写法很冗余,且容易出错。继续优化一下, 使用类内置结构体, 共享类的模板参数。优化后如下:
#include <map>
using namespace std;
/* B : 基类
* T : B的派生类
* P : T的构造函数所需参数
*/
template <class B, class... P>
class CreateFctBase
{
public:
virtual ~CreateFctBase() = default;
virtual B* create(P... args) = 0;
};
template <class B, class T, class... P>
class CreateFct : public CreateFctBase<B, P...>
{
public:
virtual ~CreateFct() = default;
B* create(P... args)
{
return new T(args...);
}
};
template <class B, class... P>
class Factory
{
public:
Factory() = default;
virtual ~Factory()
{
for (auto& m : m_mapStr2Obj)
{
delete m.second;
}
m_mapStr2Obj.clear();
}
template <class T>
struct Registor
{
Registor(Factory* pFactory, std::string strName)
{
pFactory->regist(strName, new CreateFct<B, T, P...>());
}
};
public:
void regist(std::string strName, CreateFctBase<B, P...>* creator)
{
m_mapStr2Obj[strName] = creator;
}
B* create(std::string strName, P... args)
{
auto it = m_mapStr2Obj.find(strName);
if (it != m_mapStr2Obj.end())
{
return it->second->create(args...);
}
return nullptr;
}
private:
std::map<std::string, CreateFctBase<B, P...>*> m_mapStr2Obj;
};
相比一开始的实现, 只多了一个内置结构体:
template <class T> struct Registor { Registor(Factory* pFactory, std::string strName) { pFactory->regist(strName, new CreateFct<B, T, P...>()); } };
使用时, 会简单很多,如下:
#include <iostream>
#include "creater.h"
class ProductManager : public Factory<C, int, std::string>
{
public:
ProductManager() = default;
virtual ~ProductManager() = default;
public:
void regist()
{
Registor<C1>(this, "c1");
Registor<C2>(this, "c2");
// CFact.regist("c1", new CreateFct<C, C1, int, std::string>());
// CFact.regist("c2", new CreateFct<C, C2, int, std::string>());
}
};
int main()
{
ProductManager CFact;
CFact.regist();
auto pC1 = CFact.create("c1", 1, "name_c1");
auto pC2 = CFact.create("c2", 1, "name_c2");
pC1->print();
pC2->print();
return 0;
}
这样,如果再增加一个产品大类Base2, 则只需要写一个管理器 ProductManager2 继承于 Factory, 然后用Registor注册即可。