設計模式之工廠方法模式

小熊345發表於2018-06-10

1.引入 考慮一個應用程式類Application,支援建立文字文件,編輯文字文件等操作。

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();
    }
複製程式碼

} 物件關係如下圖所示:

物件關係.png 現在,如果我們想新增對image文件的支援,我們如何設計程式呢。從設計模式的核心思想(封裝變化,解耦具體實現)出發,擴充套件程式我們需要兩步。 1.封裝變化 將程式中可能因為需求變化導致程式碼變動的部分封裝到具體類中。針對上述示例,如果新增對image文件的支援,建立文件部分的程式碼需要調整,所以我們將建立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();
    }
複製程式碼

} 物件關係如下圖所示:

物件關係引入TextDocumentFactory.png 2.解耦具體實現 根據面向介面程式設計原則,在Application和TextDocumentFactory之間引入一個抽象介面類DocumentFactory,就可以隔離Application和具體的XXXDocumentFactory之間的耦合關係。通過建立不同的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();
    }
複製程式碼

} 物件關係如下圖所示:

工廠方法模式.png 這樣,我們不僅很好支援了image文件的操作,還為其他各種文件的支援預留了擴充套件。

反思文章剛開始作者遇到的問題,你會發現,在開發專案的過程中,經常會遇到類似的情況。解決問題的思路很重要,但是,我們依然為上面的解決方案(類之間的關係模式)定義了一個名字---工廠方法模式

2.定義 基類中包含一個建立物件的介面(方法),具體建立何種型別的物件延遲到子類實現。類之間的這種關係模式,我們稱為工廠方法模式。

3.實現 從細節上來說,工廠方法有三種不同的實現方式。 1.抽象工廠不提供工廠方法的預設實現。它避免了不得不例項化不可預見類的問題。 2.抽象工廠提供工廠方法的預設實現。它遵循的準則是,"用一個獨立的操作建立物件,這樣子類才能重定義它們的建立方式" 3.引數化工廠方法。工廠方法接收一個標識要被建立的物件種類的引數,這樣,工廠方法就可以建立多種產品。重定義一個引數化的工廠方法使你可以簡單而有選擇性的擴充套件或改變一個工廠生產的產品。 一個引數化的工廠方法具有如下的一般形式,此處 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;
    }
複製程式碼

} 我們可以通過建立DocumentFactory子類的方式交換MyDocument和YourDocument並且支援一個新的子類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);
    }
複製程式碼

} 注意這個操作所做的最後一件事是呼叫父類的createDocument。這是因為MyDocumentFactory.createDocument僅在對YOURS , MINE 和THEIRS的處理上和父類不同。它對其他類不感興趣。因此 MyDocumentFactory 擴充套件了所建立產品的種類,並且將除少數產品以外所有產品的建立職責延遲給了父類。

4.效果 工廠方法不再將與特定實現有關的類繫結到你的程式碼中。程式碼僅處理Document介面;因此它可以與使用者定義的任何ConcreteDocument類一起使用。 工廠方法的一個潛在缺點在於客戶可能僅僅為了建立一個特定的ConcreteDocument物件,就不得不建立DocumentFactory的子類。

為子類提供hook。用工廠方法在一個類的內部建立物件通常比直接建立物件更靈活。Factory Method給子類一個hook以提供物件的擴充套件版本。 在Document的例子中,Document類可以定義一個稱為createFileDialog的工廠方法,該方法為開啟一個已有的文件建立預設的檔案對話方塊物件。Document的子類可以重定義這個工廠方法以定義一個與特定應用相關的檔案對話方塊。在這種情況下,工廠方法就不再抽象了而是提供了一個合理的預設實現。

5.後記 很多人建議我多舉幾個工廠方法的例子,加深理解。但是我的理論是,看透一個例子,理解其中的精髓,用剩下的時間,多用用不同的模式設計程式,你才可以事半功倍。建議讀懂,經常重複溫習上面的文章,每一個字對於理解工廠方法模式都有一定的分量,加油!

相關文章