具有切换枚举的工厂,用于实例化模板

时间:2016-06-01 19:02:14

标签: c++ templates design-patterns factory c++03

我在代码库的不同位置有以下层次结构模式:

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()实现复制粘贴到处。是否有优雅,易于理解的方法来避免重复?

3 个答案:

答案 0 :(得分:1)

您可以将其设为模板

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;
  }
}

但是假设A中只有BCstruct enum Derive

答案 1 :(得分:1)

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

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

然后您可以使用switch (t) ...代替t->createDerived()

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

答案 2 :(得分:1)

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

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 << "Foo\n";
    }
};

typedef Factory<ClassType, Foo> FooFactory ;

struct DerivedOne : public Foo
{
    virtual void PrintName() const
    {
        std::cout << "DerivedOne\n";
    }
};

struct DerivedTwo : public Foo
{
    virtual void PrintName() const
    {
        std::cout << "DerivedTwo\n";
    }
};

然后像这样使用它:

// 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();

Live Demo

当然,这会稍微简化您的问题,即将工厂细节移出基类,并忽略孩子们自己模仿的一分钟。根据您的域中为每种类型创建良好CreateDerived函数的难度,您可能不会最终节省大量的输入。

编辑:我们可以修改Create函数,以便利用std::map::find返回NULL。为简洁起见,我省略了它。如果你担心性能,那么是的,O(log n)搜索比简单的开关渐近地慢,但我强烈怀疑这将成为热门的道路。