工廠模式的三種實現,就這么簡單!

工廠模式

工廠模式是開發中常用的一種設計模式,每一種設計模式都會極大的解決程序設計方面的問題,工廠模式也是一樣,本文將會用通俗的語言來解釋什么是工廠模式?工廠模式的種類、代碼示例、每種工廠模式的優缺點和工廠模式適用的場景。

為什么要是使用工廠模式?

首先我們用一個生動故事來描述下什么是工廠模式,這會讓你更快的理解工廠模式,為后面理解的工廠模式的幾種實現方式打下基礎。

假如,你需要讓公司開一個收入證明為自己貸款買房提供收入證明,一般開收入證明的過程是:

  1. 打印收入證明;
  2. 在收入證明上蓋上公司的章;

貫穿整個過程,可以知道,你需要創建一個收入證明,并使用收入證明為貸款買房提供資料。這個過程中,有兩個關鍵的行為:創建收入證明,使用收入證明。創建收入證明的過程中又分為兩步:打印、蓋章。不熟悉這個流程的同事在創建收入證明的時候,往往會遇到很多麻煩導致開收入證明頻頻受阻,于是公司決定把員工開收入證明這件事做個優化,以前需要兩步做的事,現在只需要發一份郵件給財務部門,財務部門就會在下班之前就會把蓋好章的收入證明送到工位,這就是生活中遇到的工廠模式

說了這么多,工廠模式究竟解決了生活中的哪些問題呢?

在這個生活案例中,它讓員工創建收入證明這件事情變得更加簡單,員工不需要知道收入證明是怎么創建的,只需要發一份郵件給財務部門,財務部門就會幫助員工打印并蓋章。而且在后續的公司發展中,如果需要在收入證明中蓋上更多的章,員工也不需要自己取熟悉整個流程,拿著收入證明跑斷腿的到各個部門去蓋章。

那么,在程序的世界中,如何使用代碼演示員工為貸款買房,去公司開收入證明這個行為呢?又是如何用代碼展示工廠模式的呢?

類比上面的例子,假如有個收入證明類 創建只需要new一下就行,入參是打印證明給證明蓋章,如果后續收入證明的蓋章變了,需要修改入參。這就需要改大量調用創建收入證明類的new代碼。

于是這時工廠模式就可以使用了,工廠模式將類的創建和類的使用分離出來,當 Class A 想調用 Class B ,那么A只是調用B的方法,而至于B的實例化,就交給工廠類。

那又有人說,也可以把這些創建過程的代碼放到類的構造函數里,同樣可以降低重復率,而且構造函數本身的作用也是初始化對象。針對這個觀點,我們可以對比下工廠模式相較于構造函數的優點:

優點:

  1. 靜態工廠方法有名字而構造函數沒有,因為工廠方法可以有多個,通過名字來區別各個方法,但構造函數名字只能有一個,只能通過參數來區別,所以使用靜態工廠方法更明了。

  2. 靜態工廠方法支持條件性實例化,就是說你創建對象時,有時需要添加一些條件判斷是否應該創建,如果滿足條件則返回一個實例,不滿足則返回NULL,比如單例模式。構造函數時做不到這樣的,構造函數的功能需要保持單一,只為了構造而存在,而靜態工廠方法可以很簡單的做到。

  3. 方法能夠返回返回類型的子類型

這就是工廠模式在創建對象上的一些優點,而工廠模式最核心的知識點是:將對象的創建和使用做分離。請默念三遍。

抓住了核心點,再去了解工廠模式的各種實現就簡單的多了,工廠模式一般可以分為三種:

  • 簡單/靜態工廠模式
  • 工廠方法模式
  • 抽象工廠模式

我們學習步驟按照:工廠方法模式 到 簡單靜態工廠模式 到 抽象工廠模式,示例都非常簡單,一看就懂,文中的代碼我放在github上感興趣的同學可以下載:

github地址: java23種設計模式代碼示例--工廠模式

我知道大家都是愛心人士,白嫖當然不是你們的習慣,歡迎大家來一波素質三連:關注、點贊、點star

工廠方法模式

工廠方法相對比較和簡單\靜態工廠相對簡單比較容易理解。

如果我們需要在工廠里造一臺手機,那么先定義一個phone抽象類,手機必須要有打電話功能,加一個call抽象方法

package com.shuai.design.factory.normals;

public abstract class Phone {

    // 所有的手機必須要有打電話的功能
    public abstract void call();

}

創建一個手機的工廠接口,里面有個createPhone方法,其他類型手機都要繼承這個接口,創建手機必須實現這個方法

package com.shuai.design.factory.normals;

import com.shuai.design.factory.simples.Phone;

public interface PhoneFactory {

    Phone createPhone();
}

創建小米手機類。繼承phone,實現call方法

package com.shuai.design.factory.normals;

import com.shuai.design.factory.simples.Phone;

public class MiPhone extends Phone {

    @Override
    public void call() {
        System.out.println("this is MI call");
    }
}

創建小米手機工廠

package com.shuai.design.factory.normals;

public class MiPhoneFactory implements PhoneFactory {

    @Override
    public MiPhone createPhone() {
        return new MiPhone();
    }
}

類似小米手機,實現華為手機類,繼承phoen抽象類,實現call方法

package com.shuai.design.factory.normals;

import com.shuai.design.factory.simples.Phone;

public class HuaWeiPhone extends Phone {

    @Override
    public void call() {
        System.out.println("this is HuaWei Phone");
    }
}

實現華為手機工廠

package com.shuai.design.factory.normals;

public class HuaWeiPhoneFactory implements PhoneFactory{

    @Override
    public HuaWeiPhone createPhone() {
        return new HuaWeiPhone();
    }
}

簡單靜態工廠模式

靜態工廠相對于工廠方法模式簡單的多,首先創建phone的抽象類

package com.shuai.design.factory.simples;

public abstract class Phone {

    // 所有的手機必須要有打電話的功能
    public abstract void call();

}

再創建phone的構建工廠類,根據傳入的類型創建不同類型的手機

package com.shuai.design.factory.simples;

public class PhoneFactory {

    public static MiPhone createMiPhone() {
        return new MiPhone();
    }

    public static HuaWeiPhone createHuaWeiPhone() {
        return new HuaWeiPhone();
    }

    public static Phone createPhone(String type) {
        if ("Mi".equals(type)) {
            return new MiPhone();
        } else {
            return new HuaWeiPhone();
        }
    }
}

實現小米手機類

package com.shuai.design.factory.simples;

public class MiPhone extends Phone {

    @Override
    public void call() {
        System.out.println("this is MI call");
    }
}

實現華為手機類

package com.shuai.design.factory.simples;

public class HuaWeiPhone extends Phone {

    @Override
    public void call() {
        System.out.println("this is HuaWei Phone");
    }
}

抽象工廠模式

定義:為創建一組相關或相互依賴的對象提供一個接口,而且無須指定它們的具體類

圖為:

抽象工廠

這里還是用創建手機的方式來實現抽象工廠,先創建一個phone的抽象類,里面不僅有call方法,還有手機的屏幕類型

package com.shuai.design.factory.abstracts;

public abstract class Phone {

    // 所有的手機必須要有打電話的功能
    public abstract void call();

    // 手機的屏幕類型
    public abstract void screenType();

}

再創建一個手機構建工廠的父接口

package com.shuai.design.factory.abstracts;

public interface PhoneFactory {

    Phone createMiPhone();

    Phone createHuaWeiPhone();

}

首先所有小米手機和華為手機都必須實現通話功能,重寫call方法

package com.shuai.design.factory.abstracts;


public abstract class MiPhone extends Phone {

    @Override
    public void call() {
        System.out.println("this is MI call");
    }

}

package com.shuai.design.factory.abstracts;


public abstract class HuaWeiPhone extends Phone {

    @Override
    public void call() {
        System.out.println("this is HuaWei Phone");
    }

}

在根據手機的屏幕類型分為小米折疊屏手機:

package com.shuai.design.factory.abstracts;

public class FoldingScreenMiPhone extends MiPhone {

    @Override
    public void screenType() {
        System.out.println("this is 小米折疊屏手機");
    }

}

在根據手機的屏幕類型分為華為折疊屏手機:

package com.shuai.design.factory.abstracts;

public class FoldingScreenHuaWeiPhone extends HuaWeiPhone {

    @Override
    public void screenType() {
        System.out.println("this is 華為折疊屏手機");
    }

}

在根據手機的屏幕類型分為小米曲面屏屏手機:

package com.shuai.design.factory.abstracts;

public class CurvedScreenMiPhone extends MiPhone {

    @Override
    public void screenType() {
        System.out.println("this is 小米曲面屏手機");
    }
}

在根據手機的屏幕類型分為華為曲面屏手機:

package com.shuai.design.factory.abstracts;

public class CurvedScreenHuaWeiPhone extends HuaWeiPhone{

    @Override
    public void screenType(){
        System.out.println("this is 華為曲面屏手機");

    }

}

針對不同的屏幕類型手機,抽象出根據屏幕類型的構建工廠,有曲面屏手機工廠和折疊屏手機工廠:

package com.shuai.design.factory.abstracts;
//曲面屏手機工廠
public class CurvedScreenPhoneFactory implements PhoneFactory {

    @Override
    public CurvedScreenMiPhone createMiPhone() {
        return new CurvedScreenMiPhone();
    }

    @Override
    public CurvedScreenHuaWeiPhone createHuaWeiPhone() {
        return new CurvedScreenHuaWeiPhone();
    }
}

折疊屏手機工廠

package com.shuai.design.factory.abstracts;
//折疊屏手機工廠
public class FoldingScreenPhoneFactory implements PhoneFactory{

    @Override
    public FoldingScreenMiPhone createMiPhone() {
        return new FoldingScreenMiPhone();
    }

    @Override
    public FoldingScreenHuaWeiPhone createHuaWeiPhone() {
        return new FoldingScreenHuaWeiPhone();
    }

}

再寫一個測試類測試一下結果:

package com.shuai.design.factory.abstracts;

public class Test {

    public static void main(String[] args) {

        // 創建一個華為曲面屏手機
        CurvedScreenHuaWeiPhone curvedScreenHuaWeiPhone = new CurvedScreenPhoneFactory().createHuaWeiPhone();
        System.out.println("curvedScreenHuaWeiPhone 的手機類型為:");
        curvedScreenHuaWeiPhone.screenType();

        // 創建一個小米曲面屏手機
        CurvedScreenMiPhone curvedScreenMiPhone = new CurvedScreenPhoneFactory().createMiPhone();
        System.out.println("curvedScreenMiPhone 的手機類型為:");
        curvedScreenMiPhone.screenType();

        // 創建一個華為折疊屏手機
        FoldingScreenHuaWeiPhone foldingScreenHuaWeiPhone = new FoldingScreenPhoneFactory().createHuaWeiPhone();
        System.out.println("foldingScreenHuaWeiPhone 的手機類型為:");
        foldingScreenHuaWeiPhone.screenType();

        // 創建一個小米折疊屏手機
        FoldingScreenMiPhone foldingScreenMiPhone = new FoldingScreenPhoneFactory().createMiPhone();
        System.out.println("foldingScreenMiPhone 的手機類型為:");
        foldingScreenMiPhone.screenType();

    }

}

抽象工廠模式的使用場景:一個對象族(或是一組沒有任何關系的對象)都有相同的約束,則可以使用抽象工廠模式。通過工廠類,只要知道工廠類是誰,我就能創建出一個需要的對象

1

缺點

擴展產品族困難。比如在phone中類型增加一個帶手寫筆類型的手機,那么每個已經實現的手機類就都需要實現這個方法。這嚴重違反了開閉原則。

優點:

增加等級簡單。如果在折疊屏手機下增加一個雙折疊屏和三折疊屏的手機這就比較簡單,只需要在折疊屏手機構建工廠下面修改就行。

總結

實際上,一般開發過程中,我們使用簡單工廠模式比較多,抽象工廠模式的話需要業務比較大的情況下才會用到。如果你有更好的觀點,歡迎在評論區提出,互相學習。

參考資料:

歡迎關注公眾號:java之旅

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,963評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,348評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,083評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,706評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,442評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,802評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,795評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,983評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,542評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,287評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,486評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,030評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,710評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,116評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,412評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,224評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,462評論 2 378

推薦閱讀更多精彩內容