2011-10-21 84 views
0

類型映射假設我有兩套相關類型的,例如Animal S和他們的Offspring在對象工廠

/* Animal types */ 
struct Animal 
{ 
    virtual string getType() const = 0; 
}; 

struct Cat : public Animal 
{ 
    virtual string getType() const { return "Cat"; } 
}; 

struct Dog : public Animal 
{ 
    virtual string getType() const { return "Dog"; } 
}; 


/* Offspring types */ 
struct Offspring 
{ 
    virtual string getType() const = 0; 
}; 

struct Kitten : public Offspring 
{ 
    virtual string getType() const { return "Kitten"; } 
}; 

struct Puppy : public Offspring 
{ 
    virtual string getType() const { return "Puppy"; } 
}; 

我想實現一個製造工廠,它給予Animal將返回一個對象相關Offspring類型(例如,如果Animal事實上是Dog,工廠將返回Puppy)。

我在實現這樣一個工廠第一次嘗試是這樣的:

// First attempt at OffspringFactory 
class OffspringFactory1 
{ 
    static Offspring* createKitten() { return new Kitten(); } 
    static Offspring* createPuppy() { return new Puppy(); } 

public: 
    // Create an Offspring according to the Animal type 
    static Offspring* getOffspring(const Animal& a) 
    { 
    // Static mapping of Animal types to Offspring factory functions 
    static map<string, Offspring* (*)()> factoryMap; 
    if (factoryMap.empty()) 
    { 
     factoryMap["Dog"] = &createPuppy; 
     factoryMap["Cat"] = &createKitten; 
    } 

    // Lookup our Offspring factory function 
    map<string, Offspring* (*)()>::const_iterator fnIt = factoryMap.find(a.getType()); 
    if (fnIt != factoryMap.end()) 
     return fnIt->second(); 
    else 
     throw "Bad animal type"; 
    } 
}; 

它工作正常,但我已經使出了基於字符串的映射,而不是純粹基於類型的事。在試圖走向更基於類型的實現移動我來到這個:

// Second attempt at OffspringFactory 
class OffspringFactory2 
{ 
    // Mapping Animal types to Offspring types 
    template<typename TAnimal> struct OffspringMapper; 

    template<> 
    struct OffspringMapper<Cat> { 
    typedef Kitten offspring_type; 
    }; 

    template<> 
    struct OffspringMapper<Dog> { 
    typedef Puppy offspring_type; 
    }; 

    // Factory method 
    template<typename TAnimal> 
    static Offspring* create() { return new OffspringMapper<TAnimal>::offspring_type(); } 

public: 
    // Create an Offspring according to the Animal type 
    static Offspring* getOffspring(const Animal& a) 
    { 
    // Static mapping of Animal type strings to Offspring factory functions 
    static map<string, Offspring* (*)()> factoryMap; 
    if (factoryMap.empty()) 
    { 
     factoryMap["Dog"] = &create<Dog>; 
     factoryMap["Cat"] = &create<Cat>; 
    } 

    // Lookup our Offspring factory function 
    map<string, Offspring* (*)()>::const_iterator fnIt = factoryMap.find(a.getType()); 
    if (fnIt != factoryMap.end()) 
     return fnIt->second(); 
    else 
     throw "Bad animal type"; 
    } 
}; 

坦率地說,我不知道我在這裏的任何改進:更相當多的我還有我的字符串映射,沿可讀性較差的代碼行...

在第一次執行第二次執行時是否有任何優點,並且有什麼方法可以擺脫該映射嗎?

+0

我不太確定你想達到什麼目的?是否有某種你想要解決的現實生活問題? –

+0

你可以添加'靜態Offspring * Animal :: createOffspring()= 0;'?這將使這個_REALLY_變得容易。否則,你將不得不用一個枚舉來替換你的字符串。 –

+1

@MooingDuck我認爲你的意思是'virtual'而不是static'在你的評論中。 –

回答

1

這看起來像是雙派遣的經典案例。用C++解決這個問題的一種模式是Visitor pattern

class Offspring; 
class OffspringFactory; 

class Animal { 
public: 
    // ... rest of Animal class ... 

    virtual Offspring* acceptOffspringFactory(OffspringFactory& factory)const = 0; 
}; 

class OffspringFactory { 
public: 
    Offspring* createCatOffspring() 
    { 
     return new Kitten; 
    } 

    // ... one createXOffspring() for each type of Animal 

    Offspring* getOffspring(const Animal& a) 
    { 
     return a.acceptOffspringFactory(*this); 
    } 
}; 

Offspring* Cat::acceptOffspringFactory(OffspringFactory& factory)const 
{ 
    return factory.createCatOffspring(); 
} 

// etc for rest of Animal classes 

現在,我在你的問題再看看,你不表示該工廠是抽象的,所以真正你可以破除的工廠全部,如果你可以像@MooingDuck提到添加一個方法。

+0

我沒有看到雙重派遣來自哪裏。工廠方法在* one *類型上是多態的:動物類。 –

+0

@AndréCaron事實上,這就是爲什麼我用後續聲明修改了我的答案。我寫了原始代碼,期望OffspringFactory將是一個抽象工廠。 –

+0

@KurtS感謝您的建議,我覺得可能有些事情我可以在這裏用一點思考。主要的問題(我沒有真正意識到,直到閱讀這裏的回覆)是我不能直接修改Animal和Offspring類,因爲它們來自第三方庫。 – atkins