1. 程式人生 > >工廠模式 五種寫法總結

工廠模式 五種寫法總結

系列開篇瞎BB

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

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

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

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

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

一概述:

屬於建立型設計模式,需要生成的物件叫做產品 ,生成物件的地方叫做工廠 。

使用場景:

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

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

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

二 簡單(靜態)工廠:

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

public abstract class INoodles {
    /**
     * 描述每種麵條啥樣的
     */
public abstract void desc(); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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

public class LzNoodles extends INoodles {
    @Override
    public void desc() {
        System.out.println("蘭州拉麵 上海的好貴 家裡才5 6塊錢一碗");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

程式設計師加班必備也要吃泡麵(具體的產品類):

public class PaoNoodles extends INoodles {
    @Override
    public void
desc() { System.out.println("泡麵好吃 可不要貪杯"); } }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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

public class GankouNoodles extends INoodles {
    @Override
    public void desc() {
        System.out.println("還是家裡的幹扣面好吃 6塊一碗");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 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();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

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

/**
 * 簡單工廠模式
 */
 INoodles noodles = SimpleNoodlesFactory.createNoodles(SimpleNoodlesFactory.TYPE_GK);
 noodles.desc();
  • 1
  • 2
  • 3
  • 4
  • 5

輸出:

還是家裡的幹扣面好吃 6塊一碗
  • 1

特點

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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

點菜時:

        /**
         * 另一種簡單工廠
         * 利用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();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

輸出:

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

特點

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();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32

使用時:

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

        INoodles gk2 = MulWayNoodlesFactory.createGk();
        gk2.desc();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

輸出:

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

原始碼撐腰環節

檢視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>());
    }
  • 1
  • 2
  • 3
  • 4
  • 5

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

    public static ExecutorService newWorkStealingPool(int parallelism) {
        return new ForkJoinPool
            (parallelism,
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

這個例子可以感受到工廠方法的魅力了吧:方便建立 同種型別的 複雜引數 物件

五 普通工廠

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

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

public abstract class NoodlesFactory {
    public abstract INoodles create();
}
  • 1
  • 2
  • 3

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

public class LzFactory extends NoodlesFactory {
    @Override
    public INoodles create() {
        return new LzNoodles();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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

public class PaoFactory extends NoodlesFactory {
    @Override
    public INoodles create() {
        return new PaoNoodles();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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

public class GankouFactory extends NoodlesFactory {
    @Override
    public INoodles create() {
        return new GankouNoodles();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

使用時:

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

輸出:

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

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

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

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

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

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

缺點:

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

六 抽象工廠:

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

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

public abstract class IDrinks {
    /**
     * 描述每種飲料多少錢
     */
    public abstract void prices();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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

public class ColaDrinks extends IDrinks {
    @Override
    public void prices() {
        System.out.println("可樂三塊五");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

屌絲還是多喝水吧:

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

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

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

    /**
     * 生產飲料
     */
    public abstract IDrinks createDrinks();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

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

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

    @Override
    public IDrinks createDrinks() {
        return new WaterDrinks();//賣水
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

KFC(具體工廠類):

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

    @Override
    public IDrinks createDrinks() {
        return new ColaDrinks();//賣可樂
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

使用:

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

        abstractFoodFactory1= new LzlmFoodFactory();
        abstractFoodFactory1.createDrinks().prices();
        abstractFoodFactory1.createNoodles().desc();
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

輸出:

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

小結:

將工廠也抽象了,在使用時,工廠和產品都是面向介面程式設計,OO(面向物件)的不得了。

缺點

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

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

七 個人總結和使用場景

一句話總結工廠模式:方便建立 同種產品型別的 複雜引數 物件

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

為了適應程式的擴充套件性,擁抱變化,便衍生出了 普通工廠、抽象工廠等模式。

學習參考:

相關推薦

設計模式(一) 工廠模式 寫法總結

系列開篇瞎BB 設計模式相關的文章學習與總結,一直有意為之,一直又覺得時機不到。 一 是怕自己程式碼經驗還不夠,學習了也不懂,強行理解沒有意義。 二 是怕自己學習了以後總結出來,萬一有不對的地方,誤人子弟。 而在現在的公司摸爬滾打半年後,感覺

工廠模式 寫法總結

系列開篇瞎BB設計模式相關的文章學習與總結,一直有意為之,一直又覺得時機不到。一 是怕自己程式碼經驗還不夠,學習了也不懂,強行理解沒有意義。二 是怕自己學習了以後總結出來,萬一有不對的地方,誤人子弟。而在現在的公司摸爬滾打半年後,感覺自己寫程式碼遇到了瓶頸,想寫好寫優雅,卻不

單例模式寫法

單例模式算是設計模式中最容易理解,也是最容易手寫程式碼的模式了吧。但是其中的坑卻不少,所以也常作為面試題來考。本文主要對幾種單例寫法的整理,並分析其優缺點。很多都是一些老生常談的問題,但如果你不知道如何建立一個執行緒安全的單例,不知道什麼是雙檢鎖,那這篇文章可能會幫助到你。 1、懶漢式(執

C# 單例模式寫法

單例模式 nim blog markdown strong rod ati -s 優劣 C# 單例模式的五種寫法及優劣分析,見下文: [單例模式及常見寫法](htt

單例模式寫法

遺憾 想象 develop 由於 tcl loader adr 希望 線程不安全 第一種(懶漢,線程不安全): Java代碼 public class Singleton { private static Singleton instance;

java 單例模式5寫法

浪費 get public color ring 缺點 threads 構造函數 java 單例模式 學習整理 飽漢模式(懶漢模式) 1 // 飽漢 2 // UnThreadSafe 3 public class Singleton1 { 4 private

模式--單例模式8寫法

   單例模式是最常用到的設計模式之一,熟悉設計模式的朋友對單例模式都不會陌生。一般介紹單例模式的書籍都會提到 餓漢式 和 懶漢式 這兩種實現方式。但是除了這兩種方式,本文還會介紹其他幾種實現單例的方式,讓我們來一起看看吧。   簡介 單例模

linux裝置驅動 按鍵幾寫法總結

對於基礎按鍵的驅動,有如下幾種寫法: (1)查詢 所謂查詢方法,主要描述應用程式會在while(1)裡面一直read,如果沒有資料會導致阻塞,佔用CPU;這種方法是最差的。   (2)中斷 中斷配合休眠會避免查詢法佔用CPU的缺點。 應用程式和查詢法沒有什麼區別, 但是驅動裡面的read

ajax的兩寫法總結

   ajax不是一種新的程式語言,而是使用JavaScript向伺服器提出請求並處理而不阻塞使用者,核心物件時XMLHTTPRequest,可以在不重新整理頁面的前提下進行區域性重新整理(使用非同步資料重新整理),使使用者的體驗更好,伺服器的壓力更小。 一.原生的a

1057. Stack (30)解法總結(大雜燴)

Stack is one of the most fundamental data structures, which is based on the principle of Last In First Out (LIFO). The basic operations include Push (ins

Vue2 模板template的四寫法總結

下面小編就為大家分享一篇Vue2 模板template的四種寫法總結,寫的十分的全面細緻,具有一定的參考價值,對此有需要的朋友可以參考學習下。如有不足之處,歡迎批評指正。 如下所示: <div id="app"> <h1>我是直接寫在構造器裡的模

Java設計模式寫法

懶漢模式-執行緒不安全 public class Singleton { private static Singleton instance; private Singleton (){ } public static Singleton getInstance()

單例模式8寫法

1、餓漢式(靜態常量)[可用] public class Singleton { ​ private final static Singleton INSTANCE = new Singleton(); ​ private Singleton(){} ​ public sta

單例 模式7寫法

第一種(懶漢,執行緒不安全):  1 publicclass Singleton {   2 privatestatic Singleton instance;   3     privateSingleton (){}    4 publicstatic Single

Android資料儲存方式總結

SharePreferences是用來儲存一些簡單配置資訊的一種機制,使用Map資料結構來儲存資料,以鍵值對的方式儲存,採用了XML格式將資料儲存到裝置中。例如儲存登入使用者的使用者名稱和密碼。只能在同一個包內使用,不能在不同的包之間使用,其實也就是說只能在創建它的應用

java工廠模式詳解

工廠方法模式(Factory Method)工廠方法模式分為三種:1、普通工廠模式,就是建立一個工廠類,對實現了同一介面的一些類進行例項的建立。首先看下關係圖:舉例如下:(我們舉一個傳送郵件和簡訊的例子)首先,建立二者的共同介面:publicinterface Sender 

JAVA單例模式6寫法(附反射破壞單例)

java中單例模式是一種常見的設計模式,單例模式的寫法有多種,這裡主要介紹6種寫法:餓漢式單例、懶漢式單例3個、靜態內部類,列舉。   單例模式有以下特點:   1、單例類只能有一個例項。   2、單例類必須自己建立自己的唯一例項。   3、單例類必須給

Button點選事件的寫法

操作環境 Project:ButtonTest IDE:Android Studio2.1 學習了幾個月的Android,覺得有必要複習一下前面學過的知識,哪怕再簡單的知識也是可以溫故而知新的。就從最簡單的按鈕點選事件開始吧。我總結了五種不同的寫法,如下:

面試題(單例模式寫法

第一種形式:餓漢式單例 public class Singleton { private Singleton(){} private static Singleton i

事件處理之二:點選事件監聽器的寫法

首選方法二! 方法一:寫一個內部類,在類中實現點選事件 1、在父類中呼叫點選事件 bt_dail.setOnClickListener(new MyButtonListener()); 2、建立內部類 private class MyButtonListener