五種簡單工廠模式

轉載請標明出處:
http://blog.csdn.net/zxt0601/article/details/52798423

本文出自:【張旭童的博客】

系列開篇瞎BB

設計模式相關的文章學習與總結,一直有意爲之,一直又覺得時機不到。

一 是怕自己代碼經驗還不夠,學習了也不懂,強行理解沒有意義。

二 是怕自己學習了以後總結出來,萬一有不對的地方,誤人子弟。

而在現在的公司摸爬滾打半年後,感覺自己寫代碼遇到了瓶頸,想寫好寫優雅,卻不清楚這麼寫究竟是自以爲優雅 還是真的優雅。或對着看一些系統源碼、框架源碼時,不太理解他們這麼寫是爲什麼。

於是我開始了學習之路,從比較簡單的工廠模式開刀,看了大概10+篇資料,發現各位大大對工廠模式的各種寫法叫法不一,理解也不一,而且沒有一篇是比較全的收錄各種寫法的。so,這也堅定了我將它總結寫出來的決心,既然每個人的理解都有或多或少的缺失或衝突,那我也總結一份我的理解,呈現出來,供各位看官參考 點評。

一概述:

屬於創建型設計模式,需要生成的對象叫做產品 ,生成對象的地方叫做工廠

使用場景:

在任何需要生成複雜對象的地方,都可以使用工廠方法模式。
直接用new可以完成的不需要用工廠模式

個人理解,重點就是這個複雜 (構造函數有很多參數)和 是否可以 直接用new。(不理解這句話的話,看完一圈例子就理解了)

下面逐個介紹我所知道的各種工廠模式以及它們的特點,使用場景,並儘可能的找出JDK SDK裏它們的身影。


二 簡單(靜態)工廠:

一個栗子:
我喜歡吃麪條,抽象一個麪條基類,(接口也可以),這是產品的抽象類

public abstract class INoodles {
    /**
     * 描述每種麪條啥樣的
     */
    public abstract void desc();
}

先來一份蘭州拉麪(具體的產品類):

public class LzNoodles extends INoodles {
    @Override
    public void desc() {
        System.out.println("蘭州拉麪 上海的好貴 家裏才5 6塊錢一碗");
    }
}

程序員加班必備也要吃泡麪(具體的產品類):

public class PaoNoodles extends INoodles {
    @Override
    public void desc() {
        System.out.println("泡麪好吃 可不要貪杯");
    }
}

還有我最愛吃的家鄉的幹扣面(具體的產品類):

public class GankouNoodles extends INoodles {
    @Override
    public void desc() {
        System.out.println("還是家裏的幹扣面好吃 6塊一碗");
    }
}

準備工作做完了,我們來到一家“簡單面館”(簡單工廠類),菜單如下:

public class SimpleNoodlesFactory {
    public static final int TYPE_LZ = 1;//蘭州拉麪
    public static final int TYPE_PM = 2;//泡麪
    public static final int TYPE_GK = 3;//幹扣面

    public static INoodles createNoodles(int type) {
        switch (type) {
            case TYPE_LZ:
                return new LzNoodles();
            case TYPE_PM:
                return new PaoNoodles();
            case TYPE_GK:
            default:
                return new GankouNoodles();
        }
    }

簡單面館就提供三種麪條(產品),你說你要啥,他就給你啥。這裏我點了一份幹扣面:

/**
 * 簡單工廠模式
 */
 INoodles noodles = SimpleNoodlesFactory.createNoodles(SimpleNoodlesFactory.TYPE_GK);
 noodles.desc();

輸出:

還是家裏的幹扣面好吃 6塊一碗

特點

1 它是一個具體的類,非接口 抽象類。有一個重要的create()方法,利用if或者 switch創建產品並返回。

2 create()方法通常是靜態的,所以也稱之爲靜態工廠

缺點

1 擴展性差(我想增加一種麪條,除了新增一個麪條產品類,還需要修改工廠類方法)

2 不同的產品需要不同額外參數的時候 不支持。

三 另一種簡單工廠(反射):

利用反射Class.forName(clz.getName()).newInstance()實現的簡單工廠:

public class StaticNoodlesFactory {
    /**
     * 傳入Class實例化麪條產品類
     *
     * @param clz
     * @param <T>
     * @return
     */
    public static <T extends INoodles> T createNoodles(Class<T> clz) {
        T result = null;
        try {
            result = (T) Class.forName(clz.getName()).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }
}

點菜時:

        /**
         * 另一種簡單工廠
         * 利用Class.forName(clz.getName()).newInstance()
         */
        System.out.println("=====另一種簡單工廠利用Class.forName(clz.getName()).newInstance()======" +
                "\n個人覺得不好,因爲這樣和簡單的new一個對象一樣,工廠方法應該用於複雜對象的初始化" +
                "\n 這樣像爲了工廠而工廠");
        //蘭州拉麪
        INoodles lz = StaticNoodlesFactory.createNoodles(LzNoodles.class);
        lz.desc();
        //泡麪
        INoodles pm = StaticNoodlesFactory.createNoodles(PaoNoodles.class);
        pm.desc();

輸出:

=====另一種簡單工廠利用Class.forName(clz.getName()).newInstance()======
個人覺得不好,因爲這樣和簡單的new一個對象一樣,工廠方法應該用於複雜對象的初始化
 這樣像爲了工廠而工廠
蘭州拉麪 上海的好貴 家裏才5 6塊錢一碗
泡麪好吃 可不要貪杯

特點

1 它也是一個具體的類,非接口 抽象類。但它的create()方法,是利用反射機制生成對象返回,好處是增加一種產品時,不需要修改create()的代碼

缺點

這種寫法粗看牛逼,細想之下,不談reflection的效率還有以下問題:

1 個人覺得不好,因爲Class.forName(clz.getName()).newInstance()調用的是無參構造函數生成對象,它和new Object()是一樣的性質,而工廠方法應該用於複雜對象的初始化 ,當需要調用有參的構造函數時便無能爲力了,這樣像爲了工廠而工廠。

2 不同的產品需要不同額外參數的時候 不支持。

四 多方法工廠(常用)

使用方法二 三實現的工廠,都有一個缺點:不同的產品需要不同額外參數的時候 不支持。

而且如果使用時傳遞的type、Class出錯,將不能得到正確的對象,容錯率不高。

而多方法的工廠模式爲不同產品,提供不同的生產方法,使用時 需要哪種產品就調用該種產品的方法,使用方便、容錯率高

工廠如下:

public class MulWayNoodlesFactory {

    /**
     * 模仿Executors 類
     * 生產泡麪
     *
     * @return
     */
    public static INoodles createPm() {
        return new PaoNoodles();
    }

    /**
     * 模仿Executors 類
     * 生產蘭州拉麪
     *
     * @return
     */
    public static INoodles createLz() {
        return new LzNoodles();
    }

    /**
     * 模仿Executors 類
     * 生產幹扣面
     *
     * @return
     */
    public static INoodles createGk() {
        return new GankouNoodles();
    }
}

使用時:

        /**
         * 多方法靜態工廠(模仿Executor類)
         */
        System.out.println("==============================模仿Executor類==============================" +
                "\n 這種我比較青睞,增加一個新麪條,只要去增加一個static方法即可,也不修改原方法邏輯");
        INoodles lz2 = MulWayNoodlesFactory.createLz();
        lz2.desc();

        INoodles gk2 = MulWayNoodlesFactory.createGk();
        gk2.desc();

輸出:

==============================模仿Executor類==============================
 這種我比較青睞,增加一個新麪條,只要去增加一個static方法即可,也不修改原方法邏輯
蘭州拉麪 上海的好貴 家裏才5 6塊錢一碗
還是家裏的幹扣面好吃 6塊一碗

源碼撐腰環節

查看java源碼:java.util.concurrent.Executors類便是一個生成Executor 的工廠 ,其採用的便是 多方法靜態工廠模式

例如ThreadPoolExecutor類構造方法有5個參數,其中三個參數寫法固定,前兩個參數可配置,如下寫。

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());

}

又如JDK想增加創建ForkJoinPool類的方法了,只想配置parallelism參數,便在類裏增加一個如下的方法:

    public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

這個例子可以感受到工廠方法的魅力了吧:方便創建 同種類型的 複雜參數 對象

五 普通工廠

普通工廠就是把簡單工廠中具體的工廠類,劃分成兩層:抽象工廠層+具體的工廠子類層。(一般->特殊)

麪條工廠(抽象工廠類),作用就是生產麪條:

public abstract class NoodlesFactory {
    public abstract INoodles create();
}

蘭州拉麪工廠 (具體工廠子類):

public class LzFactory extends NoodlesFactory {
    @Override
    public INoodles create() {
        return new LzNoodles();
    }
}

泡麪工廠 (具體工廠子類):

public class PaoFactory extends NoodlesFactory {
    @Override
    public INoodles create() {
        return new PaoNoodles();
    }
}

最愛的幹扣面工廠 (具體工廠子類):

public class GankouFactory extends NoodlesFactory {
    @Override
    public INoodles create() {
        return new GankouNoodles();
    }
}

使用時:

        /**
         * 普通工廠方法:
         */
        System.out.println("===========================普通工廠方法==============================" +
                "\n 這種要多寫一個類,不過更面向對象吧 = = ,實際中我更傾向於使用【模仿Executor類】的方式");
        NoodlesFactory factory1 = new GankouFactory();
        INoodles gk3 = factory1.create();
        gk3.desc();

輸出:

===========================普通工廠方法==============================
 這種要多寫一個類,不過更面向對象吧 = = ,實際中我更傾向於使用【模仿Executor類】的方式
還是家裏的幹扣面好吃 6塊一碗

普通工廠與簡單工廠模式的區別:

可以看出,普通工廠模式特點:不僅僅做出來的產品要抽象, 工廠也應該需要抽象

工廠方法使一個產品類的實例化延遲到其具體工廠子類.

工廠方法的好處就是更擁抱變化。當需求變化,只需要增刪相應的類,不需要修改已有的類

而簡單工廠需要修改工廠類的create()方法,多方法靜態工廠模式需要增加一個靜態方法。

缺點:

引入抽象工廠層後,每次新增一個具體產品類,也要同時新增一個具體工廠類,所以我更青睞 多方法靜態工廠。

六 抽象工廠:

以上介紹的工廠都是單產品系的。抽象工廠是多產品系 (貌似也有產品家族的說法)。

舉個例子來說,每個店(工廠)不僅僅賣麪條,還提供飲料賣。
提供飲料賣,飲料是產品,先抽象一個產品類,飲料:

public abstract class IDrinks {
    /**
     * 描述每種飲料多少錢
     */
    public abstract void prices();
}

然後實現兩個具體產品類:
可樂:

public class ColaDrinks extends IDrinks {
    @Override
    public void prices() {
        System.out.println("可樂三塊五");
    }
}

屌絲還是多喝水吧:

public class WaterDrinks extends IDrinks {
    @Override
    public void prices() {
        System.out.println("和我一樣的窮鬼都喝水,不要錢~!");
    }
}

抽象飯店,無外乎吃喝(抽象工廠類):

public abstract class AbstractFoodFactory {
    /**
     * 生產麪條
     *
     * @return
     */
    public abstract INoodles createNoodles();

    /**
     * 生產飲料
     */
    public abstract IDrinks createDrinks();
}

蘭州大酒店(具體工廠類):

public class LzlmFoodFactory extends AbstractFoodFactory {
    @Override
    public INoodles createNoodles() {
        return new LzNoodles();//賣蘭州拉麪
    }

    @Override
    public IDrinks createDrinks() {
        return new WaterDrinks();//賣水
    }
}

KFC(具體工廠類):

public class KFCFoodFactory extends AbstractFoodFactory {
    @Override
    public INoodles createNoodles() {
        return new PaoNoodles();//KFC居然賣泡麪
    }

    @Override
    public IDrinks createDrinks() {
        return new ColaDrinks();//賣可樂
    }
}

使用:

        /**
         * 抽象工廠方法:
         */
        System.out.println("==============================抽象方法==============================" +
                "\n 老實說,以我這一年的水平我體會不到抽象工廠有何巨大優勢,所以在我這裏我沒有想到很好的使用場景。希望以後在慢慢體會吧。");
        AbstractFoodFactory abstractFoodFactory1 = new KFCFoodFactory();
        abstractFoodFactory1.createDrinks().prices();
        abstractFoodFactory1.createNoodles().desc();

        abstractFoodFactory1= new LzlmFoodFactory();
        abstractFoodFactory1.createDrinks().prices();
        abstractFoodFactory1.createNoodles().desc();

輸出:

==============================抽象方法==============================
 老實說,以我這一年的水平我體會不到抽象工廠有何巨大優勢,所以在我這裏我沒有想到很好的使用場景。希望以後在慢慢體會吧。
可樂三塊五
泡麪好吃 可不要貪杯
和我一樣的窮鬼都喝水,不要錢~!
蘭州拉麪 上海的好貴 家裏才5 6塊錢一碗

小結:

將工廠也抽象了,在使用時,工廠和產品都是面向接口編程,OO(面向對象)的不得了。

缺點

但是將工廠也抽象後,有個顯著問題,就是類爆炸了。而且每次拓展新產品種類,例如不僅賣吃賣喝,我還想賣睡,提供牀位服務,這需要修改抽象工廠類,因此所有的具體工廠子類,都被牽連,需要同步被修改

老實說,以我這一年的水平我體會不到抽象工廠有何巨大優勢,所以在我這裏我沒有想到很好的使用場景。希望以後在慢慢體會吧。如有您知道,希望不吝賜教。


七 個人總結和使用場景

一句話總結工廠模式:方便創建 同種產品類型的 複雜參數 對象

工廠模式重點就是適用於 構建同產品類型(同一個接口 基類)的不同對象時,這些對象new很複雜,需要很多的參數,而這些參數中大部分都是固定的,so,懶惰的程序員便用工廠模式封裝之。
(如果構建某個對象很複雜,需要很多參數,但這些參數大部分都是“不固定”的,應該使用Builder模式)

爲了適應程序的擴展性,擁抱變化,便衍生出了 普通工廠、抽象工廠等模式。

發佈了24 篇原創文章 · 獲贊 35 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章