【问题标题】:Factory with switch over enum that instantiates templates具有实例化模板的切换枚举的工厂
【发布时间】:2016-06-01 19:02:14
【问题描述】:

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

enum DerivedType {
    A, B, C };

class Base {
public:
  static Base* Create(DerivedType t);
};

template <DerivedType T>
class Derived : public Base {
};

Create 方法根据其参数返回 Derived&lt;A&gt;Derived&lt;B&gt;Derived&lt;C&gt; 类的新对象:

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 -&gt; Derived 层次结构,基本上相同的Create() 实现复制粘贴在所有地方。是否有一种优雅且易于理解的方式来避免这里的重复?

【问题讨论】:

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


    【解决方案1】:

    我们可以抽象出Factory细节,依赖客户端提供枚举到类类型的映射:

    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 函数的难度,您最终可能不会节省大量的输入。

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

    【讨论】:

      【解决方案2】:

      你可以把它做成一个模板

      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

      【讨论】:

      • 谢谢。这与 C++03 兼容吗?对于Create,只有一个模板参数如何工作?
      【解决方案3】:

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

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

      那么你可以用t-&gt;createDerived()代替switch (t) ...

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

      【讨论】:

      • 谢谢。我仍然需要至少一个 switch(t),因为我仍然得到普通的旧枚举作为输入。没关系,我猜。您想为两个 Base -> Derived hierarchies 绘制一个实现吗?
      • 不是真的;我完全有信心您可以填写详细信息。
      猜你喜欢
      • 2023-04-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-05
      相关资源
      最近更新 更多