來個繪圖經典的例子吧。基類是Shape,子類是Circle,Triangle等等。肯定有人寫過這樣的代碼:
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()寫成靜態函數。於是Shpae和Circle類長的是這個樣子:
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。在上面的新產品添加過程中,能不能省略第二步?能否實現產品在工廠中的自動登記?這將是我們要討論的第二個問題。
目前的實現對JAVA和PYTHON程序員來說不是難事。但是產品自動登記可就不簡單了。有誰可以在1周內做出來,我就轉行學JAVA和PYTHON。
最後貼出完整代碼(VC6和GCC都編譯通過):
#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;
}