設計模式之簡單工廠和工廠方法

從今天開始,我們就要正式開始學習設計模式了。關於設計模式的重要性,不言而喻,你寫的是代碼還是詩,一個重要的考察維度就是代碼的健壯性,可擴展性,這些都離不開設計模式的支持。

本文,我們就從最簡單的工廠方法模式開始,帶大家揭開設計模式的神祕面紗。

簡單工廠

要學習工廠方法,我們得先來學習下簡單工廠模式。工廠方法模式則是在簡單工廠的基礎上做的進一步優化。

模式定義

簡單工廠模式也叫靜態工廠方法模式,它是一種創建型模式。創建型模式還包括工廠方法模式、抽象工廠模式、建造者模式、單例模式以及原型模式。

在簡單工廠模式中,我們定義一個專門的類用來創建其他的實例,這個專門定義的類會根據不同的參數,返回不同的實例,這些不同的實例一般來說都有一個共同的父類。

但是需要注意的是,簡單工廠模式並不屬於 GOF 定義的 23 種設計模式。

模式抽象

我們先來通過一個簡單的 UML 圖來理解下簡單工廠模式到底是什麼樣子的:

圖-2
模式抽象
從這個 UML 圖中可以看到,在簡單工廠模式中,我們會首先定義一個產品的抽象類,然後每一個具體的產品都是實現這個抽象類,再通過工廠方法來創建一個產品的實例。

實例分析

我們通過一個生活中常見的場景來給大家描述一下這個模式。

例如我現在有兩臺電腦,一臺臺式機,另一臺是筆記本,兩臺電腦都有一個開機的方法,如下:

public class MacBook {
    public void open() {
        System.out.println("MacBook open...");
    }
}
public class MacPro {
    public void open() {
        System.out.println("MacPro open...");
    }
}

如果今天打算用 MacPro 來 coding 的話,那我就按照如下方式開機:

MacPro macPro = new MacPro();
macPro.open();

如果我今天打算用 MacBook 來 coding 的話,那我就按照如下方式開機:

MacBook macBook = new MacBook();
macBook.open();

上面的代碼看着沒問題,但是它畢竟是代碼,還不是詩,我們要將它變成詩。

首先,既然都是電腦,都有開機的方法,問什麼不能統一處理開機問題呢?

於是,我們首先來定義一個 Mac 接口,如下:

public interface Mac {
    void open();
}

這個 Mac 就相當於我們上文說的抽象產品,然後讓 MacPro 和 MacBook 分別實現這個接口,並且實現接口中的 open 方法,它們是具體的產品,如下:

public class MacPro implements Mac{
    public void open() {
        System.out.println("MacPro open...");
    }
}
public class MacBook implements Mac{
    public void open() {
        System.out.println("MacBook open...");
    }
}

最終的類結構如下圖:

圖-3

最後我們在通過一個工廠類來提供具體產品的實例:

public class MacFactory {
    public static Mac getInstance(String type) throws Exception {
        if ("macpro".equals(type)) {
            return new MacPro();
        } else if ("macbook".equals(type)) {
            return new MacBook();
        }
        throw new Exception("");
    }
}

這樣,當用戶需要獲取 MacBook 或者 MacPro 的實例時,只需要通過給工廠方法傳入不同的參數,就可以獲取到這個實例。如下:

Mac mac = MacFactory.getInstance("macbook");
mac.open();

這就是我們一直心心心念唸的簡單工廠。那麼這麼做到底有什麼好處呢?

優缺點分析

優點

在簡單工廠模式中,有三個重要的元素,就是我們上面 UML 圖中的抽象產品、具體產品以及具體工廠,三個元素中,最最核心的當屬工廠類了,工廠類根據具體的需要來創建不同的實例,需求方只需要告訴工廠它需要創建什麼實例即可,剩下的事情就交給工廠類去完成,需求方不需要去創建產品的實例,實現一個解耦。

缺點

有優點,當然就會有缺點,最大的缺點在於這個工廠類不夠靈活,當產品增加的時候,我們就得修改這個工廠類,不夠友好;並且由於我們使用了靜態方法,導致簡單工廠也沒法形成繼承結構。

對於這些問題,我們在下面的設計模式中將會逐個解決。

實際應用

簡單工廠在 Java 中還是有很多非常廣泛的應用,例如格式化一個本地日期:

public final static DateFormat getDateInstance();
public final static DateFormat getDateInstance(int style);
public final static DateFormat getDateInstance(int style,Locale locale);

這就是簡單工廠模式,接下來我們將在工廠模式種對此做進一步的優化。

工廠方法

工廠方法模式是對簡單工廠的一個升級。我們來具體看下。

模式定義

工廠方法模式也屬於創建型模式,又名工廠模式、虛擬構造器模式或者多態工廠模式。在工廠方法模式中,工廠父類負責定義創建產品對象的公共接口,而工廠子類則負責生成具體的產品對象,這樣做的目的是將產品類的實例化操作延遲到工廠子類中完成,即通過工廠子類來確定究竟應該實例化哪一個具體產品類。

在簡單工廠模式中,實例的創建都是在工廠類中完成的,如果添加了新產品就得修改工廠方法,現在,我們對工廠類也進行抽象,抽出一個接口,然後創建多個工廠類,不同的工廠類創建不同的產品,這樣,如果添加新的產品線,我們只需要提供一個相應的工廠類即可。

模式抽象

我們通過一個簡單的 UML 圖來看下工廠模式是什麼樣子:

圖-4

從這張圖中可以看到,比上面多了一個抽象工廠而已。

實例分析

接下來,我們在上文的基礎上,繼續來完善。

首先 MacPro 和 MacBook 的定義就不需要變了,我們只需要重新定義 MacFactory,首先我們來定義 MacFactory:

public interface MacFactory {
    Mac getMac();
}

然後再來定義 MacFactory 的實現類:

public class MacBookFactory implements MacFactory {
    public Mac getMac() {
        return new MacBook();
    }
}
public class MacProFactory implements MacFactory {
    public Mac getMac() {
        return new MacPro();
    }
}

在這裏我們分別定義了兩個實現類,不同的實現類用來創建不同的 Mac 對象。

最終,通過如下方式來創建實例:

MacFactory macProFactory = new MacProFactory();
Mac mac = macProFactory.getMac();
mac.open();
MacFactory macBookFactory = new MacBookFactory();
Mac mac2 = macBookFactory.getMac();
mac2.open();

這就是我們說的工廠方法模式,就比上面的多了一個工廠類的抽象,以後如果有新的產品上線,我們只需要提供相關工廠類即可。

優缺點分析

優點

  1. 工廠類滿足單一職責原則,一個工廠類只創建一個類。
  2. 符合開-閉原則。
  3. 實例方法,可以形成工廠類的等級結構。

缺點

  1. 每次添加新產品,都需要提供對應的工廠類。
  2. 一個具體的工廠只能創建一種產品。
  3. 如果要換 Mac 系的產品,還是要修改工廠類。

實際應用

工廠方法設計模式應用相當廣泛,最典型的莫過於 java.util.Collection 接口中的 iterator() 方法。

圖-5

在這張圖中,Collection 和 Iterator 分別相當於產品的抽象類和工廠的抽象類。

文章內容參考公衆號Java極客技術

其他設計模式可以參考以下文章

設計模式之單例設計模式
設計模式之裝飾器模式
設計模式之靜態代理和動態代理

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章