Factory

來個繪圖經典的例子吧。基類是Shape,子類是CircleTriangle等等。肯定有人寫過這樣的代碼:

 

enum SHAPE_DEFINE

{CIRCLE, TRIANGLE, RECTANGLE };

 

someFunction()

{

      

       SHAPE_DEFINE shape_variable;

       ……

Shape * shap;

       switch(shape_variable)

       {

       case CIRCLE:

              shape = new Circle;

              break;

       case TRIANGLE:

              shape = new Trigangle;

              break;

       .....

       }

       //do something with shape

       .....

}

 

我們因該儘量避免在工廠模式的Factory類出現類似代碼。原因是:使用case語句不利於以後的擴充和維護。當添加新的產品的時候,我們最好不要修改Factory類中的代碼,包括添加新產品的頭文件和修改創建新產品的case語句。我們希望的代碼如下

 

someFunction()

{

       Shape * shap ShapeFactory::getInstance(“Circle”);

       //do something with shape

}

 

要達到以上目標,我們將討論在C++中兩個重要實現:

 

1)      由名字創建對象

2)      新產品在工廠模式中的自動登記功能(auto-register)

由名字創建對象其實的有挺強的背景,那就是軟件工程學中的對象持久性問題。本文討論的方法也是持久性的一個可能實現。廢話少說,看看C++是怎麼做的。

 

動態創建當然要用指針。我們不希望在程序中出現硬編碼:

 

Shape * pShape = new Circle;

 

那就用函數/對象包裝一下。函數包裝簡單、高效一些。於是有Circle類的成員函數:

 

Shape *createInstance() {return new Circle;}

 

我們需要用這個函數去創建Circle對象,同時只有Circle對象創建以後纔可以調用createInstance()。這是一對矛盾。解決的辦法是將createInstance()寫成靜態函數。於是ShpaeCircle類長的是這個樣子:

 

class Shape{

public:

       virtual void  Draw()  = 0;

};

 

class Circle : public Shape{

public:

       void Draw()  { std::cout << "Drawing a Circle......Done" << std::endl; }

       static Shape *createInstance() {return new Circle;}

};

 

指向createInstance()的函數指針當然要放在工廠類裏面,同時要和創建對象的名字關聯起來。std::map的鍵-值組合當然是最佳人選。我們還有如下定義:

 

typedef Shape *(*FactoryFunction)();

 

FactoryFunction就是一個指向函數的指針,而這個函數返回一個Shape *類型。如果你對FactoryFunction有疑問,可以看看關於函數指針的書。

 

有了FactoryFunction,我們來定義工廠類中的map

 

std::map<std::string,FactoryFunction> m_FactoryFunctions;

 

這樣的map對象是把名字(string類型)和函數指針作爲一個組合。向工廠登記產品的時候,函數可以寫成這樣:

 

void ShapeFactory::Register(std::string name, FactoryFunction instanceFunction){

m_FactoryFunctions[name] = instanceFunction;

}

 

登記Circle類的創建函數:

 

Register("circle", &Circle::createInstance);

 

ShapeFactory類中由名字得到函數指針可以寫成這樣:

 

Shape * ShapeFactory::getInstance(std::string name) {

if (m_FactoryFunctions.count(name))  return m_FactoryFunctions[name]();

       else  return NULL;

}

 

map.count()的用法可以看看STL的手冊。ShapeFacoty的完整定義如下:

 

class ShapeFactory

{

public:

       static void Register(std::string name, FactoryFunction instanceFunction)

              {m_FactoryFunctions[name] = instanceFunction;};

       static Shape * getInstance (std::string name)

              { if (m_FactoryFunctions.count(name))                     return m_FactoryFunctions[name]();

               else       return NULL; }

private:

       static std::map<std::string,FactoryFunction> m_FactoryFunctions;

};

 

std::map<std::string,FactoryFunction> ShapeFactory::m_FactoryFunctions;

 

其中Register()getInstance()是靜態函數,m_FactoryFunctions是靜態成員變量。之所以這樣做是避免創建ShapeFactory對象,以簡化編程。同時便於用單例(Singleton)模式實現ShapeFactory

 

這些完成以後,就可以向工廠登記產品和使用產品了。看看下面代碼:

 

void someFunction()

{

       ShapeFactory::Register("circle", & Circle::createInstance);

       Shape * pShape = NULL;

       pShape = ShapeFactory::getInstance("circle");

       if (NULL == pShape)

       {

              std::cout << "can't find the product in the factory" << std::endl;

              delete pShape;

       }

       else

       {

              pShape->Draw();

              delete pShape;

       }

}

 

怎麼樣,還不算複雜吧。有人會認爲函數指針太不“面向對象”了,應該用其他方法。工廠模式的實現方法很多的,這裏只是做一個例子。

 

注意觀察上面的代碼。這裏已經沒有了switch/case語句。而且ShapeFactory類和Shape類的定義和實現是完全封閉的。新添加的產品類將不改變工廠類和基類的代碼。這是一個標誌性的變化。當用戶添加一個新產品的時候,他可以不管工廠已經有什麼產品,也不用寫醜陋的switch/case語句了。具體的產品是在運行時刻決定的。這樣的工廠模式才具備實用價值,而不是簡單的、對象/類之間的堆砌和關聯。

 

當我們添加一個新產品的時候,比如Triangle,可以這樣做:

 

1)      定義Triangle類:

class Triangle : public Shape

{

public:

    void Draw()  { std::cout << "Drawing a Triagnle......Done" << std::endl; }

    static Shape *createInstance() {return new Triangle;}

};

2)      在合適的地方登記Triangle

       ShapeFactory::Register("triangle", & Triangle::createInstance);

3)      給出名字,就可以創建Triangle對象了:

pShape = ShapeFactory::getInstance("triangle");

 

綜上所述,我們的第一個目標已經實現:由名字創建對象。同時我們也詳細討論的工廠模式的實現。注意我們的工廠只是一個ConcreteFactory。在上面的新產品添加過程中,能不能省略第二步?能否實現產品在工廠中的自動登記?這將是我們要討論的第二個問題

 

目前的實現對JAVAPYTHON程序員來說不是難事。但是產品自動登記可就不簡單了。有誰可以在1周內做出來,我就轉行學JAVAPYTHON

 

最後貼出完整代碼(VC6GCC都編譯通過):

 

#pragma warning (disable:4786)

 

#include <iostream>

#include <map>

#include <string>

 

class Shape;

 

typedef Shape *(*FactoryFunction)();

 

class ShapeFactory

{

public:

       static void Register(std::string name, FactoryFunction instanceFunction)

              {m_FactoryFunctions[name] = instanceFunction;};

       static Shape * getInstance(std::string name)

              { if (m_FactoryFunctions.count(name))                     return m_FactoryFunctions[name]();

               else       return NULL; }

private:

       static std::map<std::string,FactoryFunction> m_FactoryFunctions;

};

 

std::map<std::string,FactoryFunction> ShapeFactory::m_FactoryFunctions;

 

class Shape

{

public:

       virtual void  Draw()  = 0;

};

 

class Circle : public Shape

{

public:

       void Draw()  { std::cout << "Drawing a Circle......Done" << std::endl; }

       static Shape *createInstance() {return new Circle;}

};

 

class Triangle : public Shape

{

public:

       void Draw()  { std::cout << "Drawing a Triagnle......Done" << std::endl; }

       static Shape *createInstance() {return new Triangle;}

};

 

int main()

{

       ShapeFactory::Register("circle",   &   Circle::createInstance);

       ShapeFactory::Register("Triangle", & Triangle::createInstance);

 

       Shape * pShape = NULL;

 

       pShape = ShapeFactory::getInstance("circle");

       if (NULL == pShape)

       {

              std::cout << "can't find the product in the factory" << std::endl;

              delete pShape;

       }

       else

       {

              pShape->Draw();

              delete pShape;

       }

       return 0;

}

 
發佈了14 篇原創文章 · 獲贊 8 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章