Java設計模式2:簡單工廠模式

五月的倉頡發表於2015-10-22

簡單工廠模式

簡單工廠模式是類的建立模式,又叫做靜態工廠方法模式。簡單工廠模式由一個工廠物件決定生產出哪一種產品類的例項。

 

為什麼要使用簡單工廠模式

原因很簡單:解耦。

A物件如果要呼叫B物件,最簡單的做法就是直接new一個B出來。這麼做有一個問題,假如C類和B類實現了同一個介面/繼承自同一個類,系統需要把B類修改成C類,程式不得不重寫A類程式碼。如果程式中有100個地方new了B物件,那麼就要修改100處。

這就是典型的程式碼耦合度太高導致的"牽一髮動全身"。所以,有一個辦法就是寫一個工廠IFactory,A與IFactory耦合,修改一下,讓所有的類都實現C介面並且IFactory生產出C的例項就可以了。

 

簡單工廠模式示例

以水果為例:

public interface Fruit
{
    void grow();// 生長
    void harveset(); // 收貨
    void plant();// 種植
}

有兩個子類蘋果和葡萄:

public class Apple implements Fruit
{
    public void grow()
    {
        System.out.println("Apple.grow()");
    }

    public void harveset()
    {
        System.out.println("Apple.harveset()");
    }

    public void plant()
    {
        System.out.println("Apple.plant()");
    }
}
public class Grape implements Fruit
{
    public void grow()
    {
        System.out.println("Grape.grow()");
    }

    public void harveset()
    {
        System.out.println("Grape.harveset()");
    }

    public void plant()
    {
        System.out.println("Grape.plant()");
    }
}

有一個園丁,專門負責生產出各種水果:

public class Gardener
{
    public static Fruit getFruit(String fruit)
    {
        if ("apple".equalsIgnoreCase(fruit))
        {
            return new Apple();
        }
        else if ("grape".equalsIgnoreCase(fruit))
        {
            return new Grape();
        }
        else
        {
            return null;
        }
    }
}

想要什麼水果就問園丁拿就好了:

public static void main(String[] args)
{
    Fruit fruit0 = Gardener.getFruit("APPLE");
    fruit0.grow();
    Fruit fruit1 = Gardener.getFruit("GRAPE");
    fruit1.harveset();
}

程式這麼寫優點就出來了:

1、使用者不自己去生產產品,只需要負責去拿自己需要的東西就好了,這樣使用者-->產品之間的耦合度就降低了

2、程式碼模組職責更明確了,有專門消費的模組、有專門生產的模組

 

改進

上面的程式碼雖然實現了使用者-->產品之間的分離,但還是有一個問題,工廠並不知道有多少種產品,所以每一次新增產品的時候,都需要新增else if分支,這樣是不是不便呢?所以我們又想了一個辦法,就是反射,園丁可以這麼修改:

public class Gardener
{
    public static Fruit getFruit(String fruitPath) throws Exception
    {
        Class<?> c = Class.forName(fruitPath);
        return (Fruit)c.newInstance();       
    }
}

呼叫的地方可以寫成:

public static void main(String[] args) throws Exception
{
    Fruit fruit0 = Gardener.getFruit("com.xrq.simplefactory.Apple");
    fruit0.grow();
    Fruit fruit1 = Gardener.getFruit("com.xrq.simplefactory.Grape");
    fruit1.harveset();
}

當然,這麼寫其實也有一點點問題,假如有一天我的專案想進行一個重構,重整類路徑,包路徑,比方說生產Apple的地方有100處,豈不是要修改100處?當然不用,有以下三種方法推薦:

1、寫一個介面FruitPath,裡面定義常量:

public interface FruitPath
{
    public final static String apple = "com.xrq.simplefactory.Apple";
    public final static String grape = "com.xrq.simplefactory.Grape";
}

2、寫一個Fruit.properties檔案,裡面定義水果和類路徑的對應關係:

Apple=com.xrq.simplefactory.Apple
Grape=com.xrq.simplefactory.Grape

3、寫一個Fruit.xml檔案,裡面定義水果和類路徑的對應關係:

<apple>com.xrq.simplefactory.Apple</apple>
<grape>com.xrq.simplefactory.Grape</grape>

第一種方式不說了,第二種方式.properties可以用Java自帶的Properties類來解析,第三種方式.xml可以用DOM4J來解析。這樣, 假設我以後要修改水果的路徑,修改一個檔案就可以了。

從設計模式的角度講,這麼修改也有很大的優點。現在不管我新增還是刪除水果,園丁(類工廠)都不用變了,只需要告訴工廠我需要哪種水果就夠了,工廠自然會給呼叫者返回。這種寫法,也是Spring的基礎。

最後說一點,希望大家明白,簡單工廠模式或者說工廠模式的關注點並不在於在工廠中是如何生產出來需要的類的,而在於將建立產品與消費產品分離。前面使用過if...else if...else、反射,除了這些方法,還可以有別的方法可以建立產品,比如傳入一個具體產品的標識,根據這個標識去資料庫裡面查詢。

 

簡單工廠模式在Java中的應用及解讀

以後每一篇文章儘可能地介紹設計模式在Java中的應用,因為我認為不是每種設計模式開發者都有機會可以用到,但是能在原有程式碼中敏銳地看出這是一種什麼設計模式,至少能說明對這種設計模式是理解了。這裡講一下JDK中的簡單工廠模式。

JDK中的簡單工廠模式有很多應用,比較典型的比如執行緒池,具體可以參見Java多執行緒18:執行緒池。我們使用執行緒池的時候,可以使用ThreadPoolExecutor,根據自己的喜好傳入corePoolSize、maximumPoolSize、keepAliveTimem、unit、workQueue、threadFactory、handler這幾個引數,new出一個指定的ThreadPoolExecutor出來。

JDK給開發者提供了Executors這個類,可以讓使用者產生ThreadPoolExecutor和使用ThreadPoolExecutor分離開,比如可以讓Executors提供一個單執行緒的執行緒池Executors.newSingleThreadExecutor()、讓Executors提供一個無界執行緒池Executors.newCachedThreadPool()等,這樣,開發者可以不用關心執行緒池是如何去實現的,直接使用Executors方法提供給開發者的ThreadPoolExecutor就可以了。

 

工廠模式的優缺點

優點:

1、簡單優化了軟體體系結構,明確了各自功能模組的職責和權利

2、通過工廠類,外界不需要直接建立具體產品物件,只需要負責消費,不需要關心內部如何建立物件

缺點:

1、改進前的簡單工廠模式全部建立邏輯都集中在一個工廠類中,能建立的類只能是考慮到的,如果需要新增新的類,就必須改變工廠類了

2、改進前的簡單工廠模式隨著具體產品的不斷增多,可能會出現共產類根據不同條件建立不同例項的需求,這種對條件的判斷和對具體產品型別的判斷交錯在一起,很難避免功能模組的蔓延,對系統的維護和擴充套件不利

3、改進後的簡單工廠模式主要是使用反射效率會低一些

相關文章