4

我的问题与Need a design pattern to remove enums and switch statement in object creation 中的问题或多或少相同但是我认为抽象工厂模式在这里并不适合。

我目前正在计划重构/重新实现一些现有的 DAL/ORM 混合库。在现有代码的某处,有如下代码:

class Base
{
  static Base * create(struct Databasevalues dbValues)
  {
    switch(dbValues.ObjectType)
    {
    case typeA:
      return new DerivedA(dbValues);
      break;
    case typeB:
      return new DerivedB(dbValues);
      break;
    }
  }
}

class DerivedA : public Base
{
  // ...
}

class DerivedB : public Base
{
  // ...
}

因此,负责数据库通信的库会使用有关数据库实体的所有信息填充一个结构,然后调用上述 create() 方法在 ORM 中实际创建相应的对象。但我不喜欢基类知道其所有派生类的想法,我也不喜欢 switch 语句。我也想避免仅仅为了创建这些对象而创建另一个类。您如何看待目前的做法?您将如何实现此功能?

4

4 回答 4

7

这已经在这里讨论了数百万次。如果你不想创建一个单独的工厂类,你可以这样做。

class Base
{

public:
    template <class T>
    static void Register  (TObjectType type)
    {
       _creators[type] = &creator<T>;
    }

    static Base* Create (TObjectType type)
    {
        std::map <TObjectType, Creator>::iterator C = _creators.find (type);
        if (C != _creators.end())
          return C->second ();

        return 0;        
    }

private:
    template <class T>
    static Base* creator ()
    {
        return new T;
    }

private:
    typedef Base* (::*Creator) ();
    static std::map <TObjectType, Creator> _creators;
};

int main ()
{
   Base::Register <Derived1> (typeA);
   Base::Register <Derived2> (typeB);

   Base* a = Base::Create (typeA);
   Base* b = Base::Create (typeB);
}
于 2012-11-08T16:22:26.143 回答
3

假设您将开关替换为映射,例如map<ObjectType, function<Base* (DatabaseValues&)>>.

现在,工厂(可能存在也可能不存在于基类中)不需要了解所有子类。

但是,必须以某种方式填充地图。这意味着要么填充它(因此您对所有子类问题的了解刚刚从一个地方推到另一个地方),或者您需要子类使用静态初始化来在映射中注册它们的工厂函数。

于 2012-11-08T16:17:09.223 回答
1

无论您做什么,都需要 switch-case 或其他一些只会隐藏类似逻辑的构造。

但是,您可以并且应该做的是从您的 Base 中删除 create 方法 - 您完全正确,它不应该知道它是派生的。此逻辑属于另一个实体,例如工厂或控制器。

于 2012-11-08T16:40:14.030 回答
1

只是不要使用枚举。它们不是面向对象的构造,这就是为什么JAVA一开始没有它们的原因(不幸的是压力太大而无法添加它们)。

考虑而不是这样的枚举:

enum Types {
  typeA,
  typeB
};

这个结构不需要开关(我认为另一个非 OO 结构)和映射:

类型.h

class Base;
class BaseFactory {
public:
   virtual Base* create() = 0;
};     
class Types {
public:
  // possible values 
  static Types typeA;
  static Types typeB;
  // just for comparison - if you do not need - do not write...
  friend bool operator == (const Types & l, const Types & r) 
  { return l.unique_id == r.unique_id; }
  // and make any other properties in this enum equivalent - don't add them somewhere else 
  Base* create() { return baseFactory->create(); }

private:
   Types(BaseFactory* baseFactory, unsigned unique_id);
   BaseFactory* baseFactory;
   unsigned unique_id; // don't ever write public getter for this member variable!!!
};   

类型.cpp

#include "Types.h"
#include "Base.h"
#include "TypeA.h"
#include "TypeB.h"

namespace {
  TypeAFactory typeAFactory;
  TypeBFactory typeAFactory;
   unsigned unique_id = 0;
}
Types Types::typeA(&typeAFactory, unique_id++);
Types Types::typeA(&typeBFactory, unique_id++);

所以你的例子(如果你真的需要这个功能):

class Base
{
  static Base * create(struct Databasevalues dbValues)
  {
    return dbValues.ObjectType.create();
  }
};

缺少的部分应该很容易实现。

于 2012-11-08T16:55:31.000 回答