1.引入
考慮一個(gè)應(yīng)用程序類Application,支持創(chuàng)建文本文檔,編輯文本文檔等操作雅任。
public class TextDocument{
public void editDocument(){
.........
}
public void saveDocument(){
.........
}
}
public class Application{
private TextDocument mDoc;
public TextDocument createDocument(){
mDoc = new TextDocument();
}
public boolean isDocumentAvailable(){
return mDoc != null;
}
public void editDocument(){
mDoc.editDocument();
}
public void saveDocument(){
mDoc.saveDocument();
}
}
對(duì)象關(guān)系如下圖所示:
現(xiàn)在辽幌,如果我們想添加對(duì)image文檔的支持,我們?nèi)绾卧O(shè)計(jì)程序呢椿访。從設(shè)計(jì)模式的核心思想(封裝變化乌企,解耦具體實(shí)現(xiàn))出發(fā),擴(kuò)展程序我們需要兩步成玫。
1.封裝變化 將程序中可能因?yàn)樾枨笞兓瘜?dǎo)致代碼變動(dòng)的部分封裝到具體類中加酵。針對(duì)上述示例,如果添加對(duì)image文檔的支持哭当,創(chuàng)建文檔部分的代碼需要調(diào)整猪腕,所以我們將創(chuàng)建TextDocument的操作封裝到類中,命名 TextDocumentFactory
public class TextDocument{
public void editDocument(){
.........
}
public void saveDocument(){
.........
}
}
public class TextDocumentFactory{
public TextDocument createDocument(){
return new TextDocument();
}
}
public class Application{
private TextDocument mDoc;
public TextDocument createDocument(TextDocumentFactory factory){
mDoc =factory.createDocument();
}
public boolean isDocumentAvailable(){
return mDoc != null;
}
public void editDocument(){
mDoc.editDocument();
}
public void saveDocument(){
mDoc.saveDocument();
}
}
對(duì)象關(guān)系如下圖所示:
2.解耦具體實(shí)現(xiàn) 根據(jù)面向接口編程原則,在Application和TextDocumentFactory之間引入一個(gè)抽象接口類DocumentFactory钦勘,就可以隔離Application和具體的XXXDocumentFactory之間的耦合關(guān)系陋葡。通過創(chuàng)建不同的DocumentFactory子類,我們就可以支持不同的文檔類型彻采。
public interface Document{
void editDocument();
void saveDocument();
}
/**
* text 文檔
**/
public class TextDocument implements Document {
public void editDocument(){
.........
}
public void saveDocument(){
.........
}
}
/**
* image 文檔
**/
public class ImageDocument implements Document {
public void editDocument(){
.........
}
public void saveDocument(){
.........
}
}
public interface DocumentFactory{
Document createDocument();
}
/**
* text 文檔 工廠
**/
public class TextDocumentFactory implements DocumentFactory {
public TextDocument createDocument(){
return new TextDocument();
}
}
/**
* image 文檔 工廠
**/
public class ImageDocumentFactory implements DocumentFactory {
public TextDocument createDocument(){
return new TextDocument();
}
}
public class Application{
private TextDocument mDoc;
public TextDocument createDocument(TextDocumentFactory factory){
mDoc =factory.createDocument();
}
public boolean isDocumentAvailable(){
return mDoc != null;
}
public void editDocument(){
mDoc.editDocument();
}
public void saveDocument(){
mDoc.saveDocument();
}
}
對(duì)象關(guān)系如下圖所示:
這樣腐缤,我們不僅很好支持了image文檔的操作,還為其他各種文檔的支持預(yù)留了擴(kuò)展肛响。
反思文章剛開始作者遇到的問題岭粤,你會(huì)發(fā)現(xiàn),在開發(fā)項(xiàng)目的過程中特笋,經(jīng)常會(huì)遇到類似的情況剃浇。解決問題的思路很重要,但是,我們依然為上面的解決方案(類之間的關(guān)系模式)定義了一個(gè)名字---工廠方法模式
2.定義
基類中包含一個(gè)創(chuàng)建對(duì)象的接口(方法)虎囚,具體創(chuàng)建何種類型的對(duì)象延遲到子類實(shí)現(xiàn)角塑。類之間的這種關(guān)系模式,我們稱為工廠方法模式淘讥。
3.實(shí)現(xiàn)
從細(xì)節(jié)上來說圃伶,工廠方法有三種不同的實(shí)現(xiàn)方式。
1.抽象工廠不提供工廠方法的缺省實(shí)現(xiàn)适揉。它避免了不得不實(shí)例化不可預(yù)見類的問題。
2.抽象工廠提供工廠方法的缺省實(shí)現(xiàn)煤惩。它遵循的準(zhǔn)則是,"用一個(gè)獨(dú)立的操作創(chuàng)建對(duì)象嫉嘀,這樣子類才能重定義它們的創(chuàng)建方式"
3.參數(shù)化工廠方法。工廠方法接收一個(gè)標(biāo)識(shí)要被創(chuàng)建的對(duì)象種類的參數(shù)魄揉,這樣剪侮,工廠方法就可以創(chuàng)建多種產(chǎn)品。重定義一個(gè)參數(shù)化的工廠方法使你可以簡(jiǎn)單而有選擇性的擴(kuò)展或改變一個(gè)工廠生產(chǎn)的產(chǎn)品洛退。
一個(gè)參數(shù)化的工廠方法具有如下的一般形式瓣俯,此處 MyDocument和YourDocument是Document的子類:
public class DocumentFactory {
public Document createDocument(DocumentId id){
if (id == MINE) return new MyDocument();
if (id == YOURS) return new YourDocument();
return null;
}
}
我們可以通過創(chuàng)建DocumentFactory子類的方式交換MyDocument和YourDocument并且支持一個(gè)新的子類TheirDocument:
public class MyDocumentFactory extends DocumentFactory {
public Document createDocument(DocumentId id){
if (id == YOURS) return new MyDocument();
if (id == MINE) return new YourDocument();
// switch YOURS and MINE
if (id == THEIRS) return new TheirDocument();
return super.createDocument(id);
}
}
注意這個(gè)操作所做的最后一件事是調(diào)用父類的createDocument。這是因?yàn)镸yDocumentFactory.createDocument僅在對(duì)YOURS , MINE 和THEIRS的處理上和父類不同兵怯。它對(duì)其他類不感興趣彩匕。因此 MyDocumentFactory 擴(kuò)展了所創(chuàng)建產(chǎn)品的種類,并且將除少數(shù)產(chǎn)品以外所有產(chǎn)品的創(chuàng)建職責(zé)延遲給了父類媒区。
4.效果
工廠方法不再將與特定實(shí)現(xiàn)有關(guān)的類綁定到你的代碼中驼仪。代碼僅處理Document接口;因此它可以與用戶定義的任何ConcreteDocument類一起使用袜漩。
工廠方法的一個(gè)潛在缺點(diǎn)在于客戶可能僅僅為了創(chuàng)建一個(gè)特定的ConcreteDocument對(duì)象绪爸,就不得不創(chuàng)建DocumentFactory的子類。
為子類提供hook宙攻。用工廠方法在一個(gè)類的內(nèi)部創(chuàng)建對(duì)象通常比直接創(chuàng)建對(duì)象更靈活奠货。Factory Method給子類一個(gè)hook以提供對(duì)象的擴(kuò)展版本。
在Document的例子中座掘,Document類可以定義一個(gè)稱為createFileDialog的工廠方法递惋,該方法為打開一個(gè)已有的文檔創(chuàng)建默認(rèn)的文件對(duì)話框?qū)ο蟆ocument的子類可以重定義這個(gè)工廠方法以定義一個(gè)與特定應(yīng)用相關(guān)的文件對(duì)話框溢陪。在這種情況下丹墨,工廠方法就不再抽象了而是提供了一個(gè)合理的缺省實(shí)現(xiàn)。
5.后記
很多人建議我多舉幾個(gè)工廠方法的例子嬉愧,加深理解贩挣。但是我的理論是,看透一個(gè)例子,理解其中的精髓王财,用剩下的時(shí)間卵迂,多用用不同的模式設(shè)計(jì)程序,你才可以事半功倍绒净。建議讀懂见咒,經(jīng)常重復(fù)溫習(xí)上面的文章,每一個(gè)字對(duì)于理解工廠方法模式都有一定的分量挂疆,加油改览!