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注册即可。

浙公网安备 33010602011771号