带有实例化模板的切换枚举的工厂

Factory with switch over enum that instantiates templates

本文关键字:枚举 工厂 实例化      更新时间:2023-10-16

我在代码库的各个位置都有以下层次结构模式:

enum DerivedType {
A, B, C };
class Base {
public:
static Base* Create(DerivedType t);
};
template <DerivedType T>
class Derived : public Base {
};

Create方法根据其参数返回Derived<A>Derived<B>Derived<C>类的新对象:

Base* Base::Create(DerivedType t) {
switch (t) {
case A: return new Derived<A>;
case B: return new Derived<B>;
case C: return new Derived<C>;
default: return NULL;
}
}

问题是有许多这样的Base -> Derived层次结构,基本上相同的Create()副本实现被粘贴到各处。这里有没有一种优雅但易于理解的方法来避免重复?

我们可以抽象掉工厂的详细信息,并依靠客户端为我们提供枚举到类类型的映射:

template<typename ENUM, typename T>
struct Factory
{
typedef std::map<ENUM, T*(*)()> map_type;
static map_type factoryMapping_;
static T* Create(ENUM c)
{
return factoryMapping_[c]();
}
static void Init(map_type _mapping)
{
factoryMapping_ = _mapping;
}
};
template<typename ENUM, typename T>
typename Factory<ENUM, T>::map_type Factory<ENUM,T>::factoryMapping_;

现在,客户端的工作是为我们提供在给定枚举值的情况下创建Base*的方法。

如果你愿意并且能够用模板抽象出派生类的创建,那么你可以节省相当多的打字。

我的意思是,让我们创建一个模板化函数来创建一个派生类(不需要任何真正的正确性检查):

template<typename Base, typename Derived>
Base* CreateDerived()
{
return new Derived();
}

现在我可以定义一个枚举和相关的类层次结构:

enum ClassType {A, B};
struct Foo
{
virtual void PrintName() const
{
std::cout << "Foon";
}
};
typedef Factory<ClassType, Foo> FooFactory ;
struct DerivedOne : public Foo
{
virtual void PrintName() const
{
std::cout << "DerivedOnen";
}
};
struct DerivedTwo : public Foo
{
virtual void PrintName() const
{
std::cout << "DerivedTwon";
}
};

然后像这样使用:

// set up factory
std::map<ClassType, Foo*(*)()> mapping;
mapping[A] = &CreateDerived<Foo, DerivedOne>;
mapping[B] = &CreateDerived<Foo, DerivedTwo>;
FooFactory::Init(mapping);
// Use the factory
Foo* f = FooFactory::Create(A);
f->PrintName();

现场演示

当然,这会稍微简化您的问题,即将工厂详细信息从基类中移出,并在一分钟内忽略子级本身是模板化的。根据在您的域中为每种类型创建一个好的CreateDerived函数的难度,您最终可能不会节省大量的键入。

EDIT:我们可以利用std::map::find修改Create函数以返回NULL。为了简洁起见,我省略了它。如果你关心性能,那么是的,O(logn)搜索渐进地比简单的切换慢,但我强烈怀疑这最终会成为热门路径。

您可以将其作为模板

template<typename Base, typename Derive, template<Derive> class Derived>
Base* Base::Create(Derive t) {
switch (t) {
case Derive::A: return new Derived<Derive::A>;
case Derive::B: return new Derived<Derive::B>;
case Derive::C: return new Derived<Derive::C>;
default: return nullptr;
}
}

但假设struct enum Derive中只有ABC

您可以使用Java样式的枚举,而不是使用C++样式的枚举。在该枚举中,每个值都是从公共基派生的类的单例。

然后在基类上定义一组纯虚拟函数,以创建派生类的所需风格,并在每个singleton中适当地实现它们。

然后可以使用t->createDerived()而不是switch (t) ...

或者更简洁地说:用多态性代替switch。