問題
在物件導向系統設計中經常可以遇到以下的兩類問題:
1)為了提高內聚(Cohesi on)和松耦合(Coupli ng),我們經常會抽象出一些類的公用介面以形成抽象基類或者介面。這樣我們可以通過聲明一個指向基類的指標來指向實際的子類實現,達到了多態的目的。這裡很容易出現的一個問題n 多的子類繼承自抽象基類,我們不得不在每次要用到子類的地方就編寫諸如ne w ×××; 的代碼。這裡帶來兩個問題:1 客戶程式員必須知道實際子類的名稱(當系統複雜後,命名將是一個很不好處理的問題,為了處理可能的名字衝突,有的命名可能並不是具有很好的可讀性和可記憶性,就姑且不論不同程式員千奇百怪的個人偏好了。),2程式的擴充性和維護變得越來越困難。
2)還有一種情況就是在父類中並不知道具體要執行個體化哪一個具體的子類。這裡的意思為:假設我們在類A 中要使用到類B ,B 是一個抽象父類,在A 中並不知道具體要執行個體化那一個B 的子類,但是在類A 的子類D 中是可以知道的。在A 中我們沒有辦法直接使用類似於new ×××的語句,因為根本就不知道×××是什麼。
以上兩個問題也就引出了Factory模式的兩個最重要的功能:
1 定義建立對象的介面,封裝了對象的建立;
2 使得具體化類的工作延遲到了子類中。
模式選擇
我們通常使用Factory模式來解決上面給出的兩個問題。在第一個問題中,我們經常就是聲明一個建立對象的介面,並封裝了對象的建立過程。Fact ory 這裡類似於一個真正意義上的工廠(生產對象)。在第二個問題中,我們需要提供一個對象建立對象的介面,並在子類中提供其具體實現(因為只有在子類中可以決定到底執行個體化哪一個類)。
第一中情況的Factory的結構為:
圖1 所以的Factory模式經常在系統開發中用到,但是這並不是Fact ory 模式的最大威力所在(因為這可以通過其他方式解決這個問題)。Fact ory 模式不單是提供了建立對象的介面,其最重要的是延遲了子類的執行個體化(第二個問題),以下是這種情況的一個Factory的結構:
圖2 中關鍵中Factory模式的應用並不是只是為了封裝對象的建立,而是要把對象的建立放到子類中實現:Fact ory 中只是提供了對象建立的介面,其實現將放在Factory的子類ConcreteFactory 中進行。這是圖2 和圖 1 的區別所在。
實現
完整程式碼範例(code )
Factory模式的實現比較簡單,這裡為了方便初學者的學習和參考,將給出完整的實現代碼(所有代碼採用C++ 實現,並在VC 6.0 下測試回合)。
// Product.h: interface for the CProduct class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_PRODUCT_H__86E59C0A_EB85_4D2E_AE11_F77FF2ADF20C__INCLUDED_)
#define AFX_PRODUCT_H__86E59C0A_EB85_4D2E_AE11_F77FF2ADF20C__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CProduct
{
public:
virtual ~CProduct()=0;
protected:
CProduct();
};
class CConcreateProduct:public CProduct
{
public:
CConcreateProduct();
~CConcreateProduct();
};
#endif // !defined(AFX_PRODUCT_H__86E59C0A_EB85_4D2E_AE11_F77FF2ADF20C__INCLUDED_)
// Product.cpp: implementation of the CProduct class.
//
//////////////////////////////////////////////////////////////////////
#include "Product.h"
#include <iostream>
using namespace std;
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CProduct::CProduct()
{
}
CProduct::~CProduct()
{
}
CConcreateProduct::CConcreateProduct()
{
cout<<"CConcreateProduct"<<endl;
}
CConcreateProduct::~CConcreateProduct()
{
}
// Factory.h: interface for the CFactory class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_FACTORY_H__8369A217_8E1A_449D_BEBF_76F27F862A3A__INCLUDED_)
#define AFX_FACTORY_H__8369A217_8E1A_449D_BEBF_76F27F862A3A__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
class CProduct;
class CFactory
{
public:
virtual ~CFactory()=0;
virtual CProduct* CreateProduct()=0;
protected:
CFactory();
};
class CConcreateFactory:public CFactory
{
public:
CConcreateFactory();
~CConcreateFactory();
CProduct* CreateProduct();
};
#endif // !defined(AFX_FACTORY_H__8369A217_8E1A_449D_BEBF_76F27F862A3A__INCLUDED_)
// Factory.cpp: implementation of the CFactory class.
//
//////////////////////////////////////////////////////////////////////
#include "Factory.h"
#include "Product.h"
#include <iostream>
using namespace std;
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CFactory::CFactory()
{
}
CFactory::~CFactory()
{
}
CConcreateFactory::CConcreateFactory()
{
cout<<"CConcreateFactory"<<endl;
}
CConcreateFactory::~CConcreateFactory()
{
}
CProduct* CConcreateFactory::CreateProduct()
{
return new CConcreateProduct();
}
#include "Factory.h"
#include "Product.h"
#include <iostream>
using namespace std;
int main()
{
CFactory *pFactory=new CConcreateFactory();
CProduct *pProduct=pFactory->CreateProduct();
return 0;
}
代碼說明
範例程式碼中給出的是Factory模式解決父類中並不知道具體要執行個體化哪一個具體的子類的問題,至於為建立對象提供介面問題,可以由Factory 中附加相應的建立操作例如Create* **Produc t()即可。具體請參加討論內容。
討論
Factory 模式在實際開發中應用非常廣泛,物件導向的系統經常面臨著對象建立問題:
要建立的類實在是太多了。而Factory提供的建立對象的介面封裝(第一個功能),以及其將類的執行個體化延遲到子類(第二個功能)都部分地解決了實際問題。一個簡單的例子就是筆者開開發Vi su a l CM C S 系統的語義分析過程中,由於要為文法中的每個非終結符構造一個類處理,因此這個過程中對象的建立非常多,採用Factory模式後系統可讀性性和維護都變得eleg ant 許多。
Factory 模式也帶來至少以下兩個問題:
1 )如果為每一個具體的Concrete Product 類的執行個體化提供一個函數體,那麼我們可能不得不在系統中添加了一個方法來處理這個建立的Concre teProd uct,這樣Factory的介面永遠就不肯能封閉(Close)。當然我們可以通過建立一個Factory的子類來通過多態實現這一點,但是這也是以建立一個類作為代價的。
2)在實現中我們可以通過參數化Factory 方法,即給Fact oryMethod()傳遞一個參數用以決定是建立具體哪一個具體的Product (實際上筆者在Vi su a l CM C S 中也正是這樣做的)。當然也可以通過模板化避免1中的子類建立子類,其方法就是將具體Produc t 類作為模板參數,實現起來也很簡單。
可以看出,Factory 模式對於對象的建立給予開發人員提供了很好的實現策略,但是Factory模式僅僅局限於一類類(就是說Product 是一類,有一個共同的基類),如果我們要為不同類的類提供一個對象建立的介面,那就要用AbstractFactory了。