###由遇到的問題引出工廠模式
在面向對象系統設計中經常可以遇到以下的兩類問題:
**◆ 1.為了提高內聚(Cohesion)和松耦合(Coupling),我們經常會抽象出一些類的公共接口以形成抽象基類或者接口**。這樣我們可以通過聲明一個指向基類的指針來指向實際的子類實現,達到了多態的目的。這里很容易出現的一個問題 n 多的子類繼承自抽象基類,我們不得不在每次要用到子類的地方就編寫諸如 new ×××;的代碼。這里帶來兩個問題:
客戶程序員必須知道實際子類的名稱(當系統復雜后,命名將是一個很不好處理的問題,為了處理可能的名字沖突,有的命名可能并不是具有很好的可讀性和可記憶性,就姑且不論不同程序員千奇百怪的個人偏好了)。
程序的擴展性和維護變得越來越困難。
**◆ 2.還有一種情況就是在父類中并不知道具體要實例化哪一個具體的子類。**這里的意思為:假設我們在類 A 中要使用到類 B,B 是一個抽象父類,在 A 中并不知道具體要實例化那一個 B 的子類,但是在類 A 的子類 D 中是可以知道的。在 A 中我們沒有辦法直接使用類似于 new ×××的語句,因為根本就不知道×××是什么。
以上兩個問題也就引出了工廠模式的兩個最重要的功能:
定義創建對象的接口,封裝了對象的創建;
使得具體化類的工作延遲到了子類中。
###模式選擇
我們通常使用工廠模式來解決上面給出的兩個問題。在第一個問題中,我們經常就是聲明一個創建對象的接口,并封裝了對象的創建過程。工廠這里類似于一個真正意義上的工廠(生產對象)。在第二個問題中,我們需要提供一個對象創建對象的接口,并在子類中提供其具體實現(因為只有在子類中可以決定到底實例化哪一個類)。
第一中情況的工廠的結構示意圖為:

圖 1:工廠模式結構示意圖 1
圖 1 所以的工廠模式經常在系統開發中用到,但是這并不是工廠模式的最大威力所在(因為這可以通過其他方式解決這個問題)。工廠模式不單是提供了創建對象的接口,其最重要的是延遲了子類的實例化(第二個問題),以下是這種情況的一個工廠的結構示意圖:

圖 2:工廠模式結構示意圖 1
圖 2 中關鍵中工廠模式的應用并不是只是為了封裝對象的創建,而是要把對象的創建放到子類中實現:工廠中只是提供了對象創建的接口,其實現將放在工廠的子類Concrete工廠中進行。這是圖 2 和圖 1 的區別所在。
###工廠模式的實現
完整代碼示例(code):工廠模式的實現比較簡單,這里為了方便初學者的學習和參考,將給出完整的實現代碼(所有代碼采用 C++實現,并在 VC 6.0 下測試運行)。
~~~
代碼片斷 1:Product.h
//Product.h
#ifndef _PRODUCT_H_
#define _PRODUCT_H_
class Product{
public:
virtual ~Product() =0;
protected:
Product(); //屏蔽構造函數
private:
};
class ConcreteProduct:publicProduct{
public:
~ConcreteProduct();
ConcreteProduct();
protected:
private:
};
#endif //~_PRODUCT_H_
代碼片斷 2:Product.cpp
//Product.cpp
#include "Product.h"
#include<iostream>
using namespace std;
Product::Product(){
}
Product::~Product(){
}
ConcreteProduct::ConcreteProduct(){
cout<<"ConcreteProduct...."<<endl;
}
ConcreteProduct::~ConcreteProduct(){
}
代碼片斷 3:Factory.h
//Factory.h
#ifndef _FACTORY_H_
#define _FACTORY_H_
class Product;
class Factory{
public:
virtual ~Factory() = 0;
virtual Product* CreateProduct() = 0;
protected:
Factory();
private:
};
class ConcreteFactory:public Factory{
public:
~ConcreteFactory();
ConcreteFactory();
Product* CreateProduct();
protected:
private:
};
#endif //~_FACTORY_H_
代碼片斷 4:Factory.cpp
//Factory.cpp
#include "Factory.h"
#include "Product.h"
#include <iostream>
using namespace std;
Factory::Factory(){
}
Factory::~Factory(){
}
ConcreteFactory::ConcreteFactory(){
cout<<"ConcreteFactory....."<<endl;
}
ConcreteFactory::~ConcreteFactory(){
}
Product* ConcreteFactory::CreateProduct(){
return new ConcreteProduct();
}
代碼片斷 5:main.cpp
//main.cpp
#include "Factory.h"
#include "Product.h"
#include <iostream>
using namespace std;
int main(int argc,char* argv[]){
Factory* fac = new ConcreteFactory();
Product* p = fac->CreateProduct();
return 0;
}
~~~
代碼說明:示例代碼中給出的是工廠模式解決父類中并不知道具體要實例化哪一個具體的子類的問題,至于為創建對象提供接口問題,可以由工廠中附加相應的創建操作例如Create***Product()即可。具體請參加討論內容。
###關于工廠模式的討論
工廠模式在實際開發中應用非常廣泛,面向對象的系統經常面臨著對象創建問題:要創建的類實在是太多了。而工廠提供的創建對象的接口封裝(第一個功能),以及其將類的實例化推遲到子類(第二個功能)都部分地解決了實際問題。一個簡單的例子就是筆者開開發 VisualCMCS 系統的語義分析過程中,由于要為文法中的每個非終結符構造一個類處理,因此這個過程中對象的創建非常多,采用工廠模式后系統可讀性性和維護都變得elegant 許多。
工廠模式也帶來至少以下兩個問題:
1. 如果為每一個具體的 ConcreteProduct 類的實例化提供一個函數體,那么我們可能不得不在系統中添加了一個方法來處理這個新建的 ConcreteProduct,這樣工廠的接口永遠就不肯能封閉(Close)。當然我們可以通過創建一個工廠的子類來通過多態實現這一點,但是這也是以新建一個類作為代價的。
2. 在實現中我們可以通過參數化工廠方法,即給 工廠Method()傳遞一個參數用以決定是創建具體哪一個具體的 Product(實際上筆者在 VisualCMCS 中也正是這樣做的)。當然也可以通過模板化避免 1)中的子類創建子類,其方法就是將具體 Product 類作為模板參數,實現起來也很簡單。
可以看出,工廠模式對于對象的創建給予開發人員提供了很好的實現策略,但是工廠模式僅僅局限于一類類(就是說 Product 是一類,有一個共同的基類),如果我們要為不同類的類提供一個對象創建的接口,那就要用 Abstract工廠了。