看透 "工廠方法" 模式

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)系如下圖所示:

對(duì)象關(guān)系.png

現(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)系如下圖所示:

對(duì)象關(guān)系引入TextDocumentFactory.png

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)系如下圖所示:

工廠方法模式.png

這樣,我們不僅很好支持了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ū)ο?。Document的子類可以重定義這個(gè)工廠方法以定義一個(gè)與特定應(yīng)用相關(guān)的文件對(duì)話框。在這種情況下,工廠方法就不再抽象了而是提供了一個(gè)合理的缺省實(shí)現(xiàn)。

5.后記

很多人建議我多舉幾個(gè)工廠方法的例子,加深理解。但是我的理論是,看透一個(gè)例子,理解其中的精髓,用剩下的時(shí)間,多用用不同的模式設(shè)計(jì)程序,你才可以事半功倍。建議讀懂,經(jīng)常重復(fù)溫習(xí)上面的文章,每一個(gè)字對(duì)于理解工廠方法模式都有一定的分量,加油!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容