4

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

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

5 回答 5

1

I would separate the conversion of strings to enum into a distinct object. This can easily be solved by a map btw. But error handling etc. is still something which neither D nor the factory should be worried about.

Then either D calls the converter to get its enum, or it is already converted beforehand, so D only needs to pass the enum to the factory. (Btw the factory would better use a map too instead of a switch internally).

This raises the question: do you actually need the enums at all (in places other than D and the factory)? If not, maybe the enum could be left out of the picture and you could use a map to convert directly from strings to types (i.e. - since C++ doesn't support dynamic class loading - to function objects which create the necessary concrete type instances for you). A rough example (I don't have an IDE to test it so bear with me if there are any errors in it):

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

Of course, the created instances should also be disposed of properly - in production code, the returned objects could be e.g. enclosed in an auto_ptr. But I hope this short example is enough to show you the basic idea. Here is a tutorial if you want more...

于 2010-03-13T12:47:24.027 回答
0

You can put all matching strings in the set or list and check if it contains your strings instead of writing ifs/switches.

于 2010-03-13T12:43:56.710 回答
0

My project on VC++/Qt had a large number of XML files containing strings that had a Enum representation into the source.

So for each Enum we had a wrapper with overloaded operator QString <> 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;
}

but the approach in last listing is very ugly.

Better to create a static hash table or dictionary and look up into.

于 2010-03-13T12:48:15.873 回答
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:
  }
}
于 2010-03-13T15:19:57.853 回答
0

正常的方法是将您的工厂设置为单例。然后每个基于类 B 的类在静态初始化时向工厂注册它的创建函数和名称。这通常通过宏来完成。然后,工厂可以创建这些名称的快速哈希表来创建函数。等等……你明白了。

于 2010-03-13T13:08:34.483 回答