2010-03-13 105 views
3

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

  1. 工厂接收枚举(并使用开关创建函数内部)和d负责将字符串转换为枚举。
  2. 工厂收到一个字符串并检查匹配到一组有效字符串(使用ifs')
  3. 其他实现我没有想到。

回答

0

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

+0

工厂仍然必须根据字符串构造正确的对象。它必须做如果/开关 – amitlicht 2010-03-13 12:46:10

+1

有可能有字符串 - > creator函数比ifs /开关好得多。 – 2010-03-13 12:52:07

+0

我认为切换枚举是不可避免的。无论如何,无论如何,你都会明确或隐含地做到这一点。 – abatishchev 2010-03-13 12:58:04

1

我会分开字符串转换为枚举成一个不同的对象。这可以很容易地通过地图顺便解决。但是错误处理等仍然是D和工厂都不应该担心的事情。

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

这引发了一个问题:你是否真的需要枚举(在D和工厂以外的地方)?如果没有,那么也许enum可能被排除在图片之外,并且您可以使用映射将字符串直接转换为类型(即 - 因为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。但我希望这个简短的例子足以向你展示基本的想法。这里是a tutorial如果你想要更多...

+0

你是什么意思的字符串类型映射? 那张地图应该是什么样子? – amitlicht 2010-03-13 16:34:21

+0

@eriks我添加了一个例子。 – 2010-03-13 21:16:17

0

我的项目VC++/Qt有大量的XML文件,其中包含字符串的Enum表示到源。因此,对于每一个枚举

我们与重载运营商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; 
} 

但在去年上市的做法是非常难看。

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

0

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

+1

我不同意。工厂不一定是单身人士。事实上,由于Singletons使单元测试变得困难,所以除非确实需要,否则最好避免它们。 – 2010-03-13 13:59:39

+0

谢谢佩特(评论+1)。我同意工厂本身不需要是单身,但类的列表必须以某种方式存在,我宁愿用静态初始化来构造它。而不是在一个函数或数据文件中。在大型团队中工作时,会阻止人们与其他文件进行交锋。根据需要,其他方法可能更合乎需要。 – 2010-03-13 18:17:34

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