关于工厂模式的问题

时间:2010-03-13 12:37:01

标签: c++ design-patterns factory-pattern

我有一个工厂类来构建基类B的对象。 使用此工厂的对象(D)接收表示实际类型的字符串列表。 什么是正确的实施:

  1. 工厂收到Enum(并在Create函数中使用switch),D负责将字符串转换为Enum。
  2. 工厂收到一个字符串并检查是否匹配一组有效字符串(使用ifs')
  3. 我没想到的其他实现。

5 个答案:

答案 0 :(得分:1)

我会将字符串转换为枚举转换为不同的对象。这可以通过地图btw轻松解决。但错误处理等仍然是D和工厂都不应该担心的事情。

然后D调用转换器来获取它的枚举,或者它已经预先转换,所以D只需要将枚举传递给工厂。 (顺便说一下工厂也会更好地使用地图而不是内部的开关)。

这提出了一个问题:你真的需要枚举(在D和工厂以外的地方)吗?如果没有,也许枚举可以被排除在图片之外,你可以使用地图直接从字符串转换为类型(即 - 因为C ++不支持动态类加载 - 为函数创建必要的具体类型实例的对象您)。一个粗略的例子(我没有IDE来测试它,所以如果有任何错误,请耐心等待):

// Function type returning a pointer to B
typedef (B*)(*func)() StaticConstructor;

// Function creating instances of subclass E
B* createSubclassE() {
    return new E(...);
}

// Function creating instances of subclass F
B* createSubclassF() {
    return new F(...);
}

// Mapping from strings to constructor methods creating specific subclasses of B
map<string, StaticConstructor> factoryMap;
factoryMap["E"] = &createSubclassE;
factoryMap["F"] = &createSubclassF;

当然,创建的实例也应该正确处理 - 在生产代码中,返回的对象可以是例如。括在auto_ptr中。但我希望这个简短的例子足以向您展示基本思想。如果你想要更多...... {/ 3>,这是a tutorial

答案 1 :(得分:0)

您可以将所有匹配的字符串放入集合或列表中,并检查它是否包含字符串,而不是编写ifs / switches。

答案 2 :(得分:0)

我在VC++/Qt上的项目包含大量XML文件,其中包含在源中具有Enum表示的字符串。

因此,对于每个枚举,我们都有一个包含重载运算符QString的包装器&lt;&gt; Enum

enum DataColumnTypeEnum
{
    DataColumnTypeNotSet,
    ColumnBinary,
    ColumnBoolean,
    ColumnDate,
    ColumnDateTime,
    ColumnNumber,
    ColumnFloat,
    ColumnPrimary,
    ColumnString,
    ColumnText,
};

class DataColumnType
{
public:
    DataColumnType();
    DataColumnType(DataColumnTypeEnum);
    DataColumnType(const QString&);

    DataColumnType& operator = (DataColumnTypeEnum);
    DataColumnType& operator = (const QString&);

    operator DataColumnTypeEnum() const;
    operator QString() const;
private:
    DataColumnTypeEnum type;
};

DataColumnType& DataColumnType::operator = (const QString& str)
{
    str.toLower();
    if(str.isEmpty()) type = DataColumnTypeNotSet;
    else if(str == "binary") type = ColumnBinary;
    else if(str == "bool") type = ColumnBoolean;
    else if(str == "date") type = ColumnDate;
    else if(str == "datetime") type = ColumnDateTime;
    else if(str == "number") type = ColumnNumber;
    else if(str == "float") type = ColumnFloat;
    else if(str == "primary") type = ColumnPrimary;
    else if(str == "string") type = ColumnString;
    else if(str == "text") type = ColumnText;
    return *this;
}

但是上一次上市的方法非常难看。

最好创建一个静态哈希表或字典并查找。

答案 3 :(得分:0)

正常的方法是让你的工厂成为一个单身人士。然后,基于B类的每个类在静态初始化时将工厂的创建函数和名称注册。这通常用宏来完成。然后工厂可以创建这些名称的快速哈希表来创建函数。等等......你得到漂移。

答案 4 :(得分:0)

我个人使用增强的枚举,因为我总是发现缺少C ++的枚举:像Type 3 - method -begin这样的消息没有多少信息。

通过这种方式,我使用了一个简单的模板化类:

template <class Holder>
class Enum
{
public:
  typedef typename Holder::type enum_type;

  Enum(): mValue(Invalid()) {}
  Enum(enum_type i): mValue(Get(i)) {}
  explicit Enum(const std::string& s): mValue(Get(s)) {}

  bool isValid() const { return mValue != Invalid(); }
  enum_type getValue() const { return mValue->first; }

private:
  typedef typename Holder::mapping_type mapping_type;
  typedef typename mapping_type::const_iterator iterator;
  static const mapping_type& Mapping() { static mapping_type MMap = Holder::Initialize(); return MMap; }

  static iterator Invalid() { return Mapping().end(); }
  static iterator Get(enum_type i) { // search }
  static iterator Get(const std::string& s) { // search }

  iterator mValue;
};

您可以像这样定义Holder

struct Example
{
  typedef enum {
    Value1,
    Value2,
    Value3
  } type;

  typedef std::vector< std::pair< type, std::string > > mapping_type;

  static mapping_type Initialize() {
    return builder<mapping_type>()(Value1,"Value1")(Value2,"Value2")(Value3,"Value3");
  }
};

您可以为它定义一个宏:

DEFINE_ENUM(Example, (Value1)(Value2)(Value3))

但我将实施作为练习(Boost.Preprocessor是你的朋友)。

很酷的是使用它!

int main(int argc, char* argv[])
{
  std::string s;
  std::cin >> s;
  Enum<Example> e(s);

  switch(e.getValue())
  {
  case Example::Value1:
  case Example::Value2:
    ++e;
  case Example::Value3:
    std::cout << e << std::endl;
  default:
  }
}