2010-03-13 51 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: 
    } 
}