設(shè)計(jì)模式之工廠模式(Factory)

概述

根據(jù)依賴(lài)倒置原則,我們知道,我們應(yīng)優(yōu)先依賴(lài)抽象類(lèi)而不是具體類(lèi)画畅。在應(yīng)用開(kāi)發(fā)過(guò)程中砸琅,有很多實(shí)體類(lèi)都是非常易變的,依賴(lài)它們會(huì)帶來(lái)問(wèn)題轴踱,所以我們更應(yīng)該依賴(lài)于抽象接口症脂,已使我們免受大多數(shù)變化的影響。
工廠模式(Factory)允許我們只依賴(lài)于抽象接口就能創(chuàng)建出具體對(duì)象的實(shí)例寇僧,所以在開(kāi)發(fā)中摊腋,如果具體類(lèi)是高度易變的,那么該模式就非常有用嘁傀。

接下來(lái)我們就通過(guò)代碼舉例說(shuō)明什么是工廠模式

簡(jiǎn)單工廠模式

假設(shè)我們現(xiàn)在有個(gè)需求:把一段數(shù)據(jù)用Wi-Fi或者藍(lán)牙發(fā)送出去。
需求很簡(jiǎn)單是吧视粮?刷刷刷就寫(xiě)下了以下實(shí)現(xiàn):

    private String mode; //Wi-Fi|Bluetooth

    public void onClick() {
        byte[] data = {0x00, 0x01};

        if ("Wi-Fi".equals(mode)) {
            sendDataByWiFi(data);
        } else {
            sendDataByBluetooth(data);
        }
    }

    private void sendDataByWiFi(byte[] data) {
        // send data via Wi-Fi
    }

    private void sendDataByBluetooth(byte[] data) {
        // send data via Bluetooth
    }

但是上面的代碼擴(kuò)展性并不高细办,違反了開(kāi)放封閉原則。比如現(xiàn)在又有了個(gè)新的需求蕾殴,需要用zigbee把數(shù)據(jù)發(fā)送出去笑撞,就得再新增一個(gè)sendDataByZigbee方法了,而且還得修改onClick里面的邏輯钓觉。那么比較好的方法是怎么樣的呢茴肥?

定義一個(gè)數(shù)據(jù)發(fā)送器類(lèi):

/**
 * 數(shù)據(jù)發(fā)送器Sender
 *
 * @author HansChen
 */
public interface Sender {

    void sendData(byte[] data);
}

實(shí)現(xiàn)WiFi數(shù)據(jù)發(fā)送:

/**
 * Sender的實(shí)現(xiàn)類(lèi),通過(guò)Wi-Fi發(fā)送數(shù)據(jù)
 *
 * @author HansChen
 */
public class WiFiSender implements Sender {

    @Override
    public void sendData(byte[] data) {
        System.out.println("Send data by Wi-Fi");
    }
}

實(shí)現(xiàn)藍(lán)牙數(shù)據(jù)發(fā)送:

/**
 * Sender的實(shí)現(xiàn)類(lèi)荡灾,通過(guò)藍(lán)牙發(fā)送數(shù)據(jù)
 *
 * @author HansChen
 */
public class BluetoothSender implements Sender {

    @Override
    public void sendData(byte[] data) {
        System.out.println("Send data by Bluetooth");
    }
}

這樣瓤狐,原來(lái)發(fā)送數(shù)據(jù)的地方就改為了:

    private String mode; //Wi-Fi|Bluetooth

    public void onClick() {
        byte[] data = {0x00, 0x01};

        Sender sender;
        if ("Wi-Fi".equals(mode)) {
            sender = new WiFiSender();
        } else {
            sender = new BluetoothSender();
        }
        sender.sendData(data);
    }

有沒(méi)有覺(jué)得代碼優(yōu)雅了一點(diǎn)?但是隨著發(fā)送器Sender的實(shí)現(xiàn)類(lèi)越來(lái)越多批幌,每增加一個(gè)實(shí)現(xiàn)類(lèi)础锐,就需要在onClick里面實(shí)例化相應(yīng)的實(shí)現(xiàn)類(lèi),能不能用一個(gè)單獨(dú)的類(lèi)來(lái)做這個(gè)創(chuàng)造實(shí)例的過(guò)程呢荧缘?這就是我們講到的工廠皆警。我們新增一個(gè)工廠類(lèi):

/**
 * 簡(jiǎn)單工廠類(lèi)
 *
 * @author HansChen
 */
public class SimpleFactory {

    public static Sender createSender(String mode) {
        switch (mode) {
            case "Wi-Fi":
                return new WiFiSender();
            case "Bluetooth":
                return new BluetoothSender();
            default:
                throw new IllegalArgumentException("illegal type: " + mode);
        }
    }
}

這樣一來(lái),怎么實(shí)例化數(shù)據(jù)發(fā)送器我們也不用管了截粗,最終代碼變?yōu)椋?/p>

    private String mode; //Wi-Fi|Bluetooth

    public void onClick() {
        byte[] data = {0x00, 0x01};

        Sender sender = SimpleFactory.createSender(mode);
        sender.sendData(data);
    }

好了信姓,到這里我們就完成了簡(jiǎn)單工廠模式的應(yīng)用了,下圖就是簡(jiǎn)單工廠模式的結(jié)構(gòu)圖:


2019-9-2-11-28-47.png

工廠方法模式

簡(jiǎn)單工廠模式的優(yōu)點(diǎn)在于工廠類(lèi)包含了必要的判斷邏輯绸罗,根據(jù)傳入的參數(shù)動(dòng)態(tài)實(shí)例化相關(guān)的類(lèi)意推,對(duì)于客戶(hù)端來(lái)說(shuō),去除了與具體產(chǎn)品的依賴(lài)从诲。但是這里還是會(huì)有個(gè)問(wèn)題左痢,假設(shè)上面例子中新增了一個(gè)zigbee發(fā)送器,那么一定是需要修改簡(jiǎn)單工廠類(lèi)的,也就是說(shuō)俊性,我們不但對(duì)擴(kuò)展開(kāi)放了略步,對(duì)修改也開(kāi)放了,這是不好的定页。解決的方法是使用工廠方法模式趟薄,工廠方法模式是指定義一個(gè)用于創(chuàng)建對(duì)象的接口,讓子類(lèi)決定實(shí)例化哪一個(gè)類(lèi)典徊。下面還是通過(guò)代碼來(lái)說(shuō)明:

在簡(jiǎn)單工廠模式的基礎(chǔ)上杭煎,讓我們對(duì)工廠類(lèi)也升級(jí)一下,首先定義一個(gè)工廠類(lèi)接口:

public interface SenderFactory {

    Sender createSender();
}

然后為每一個(gè)發(fā)送器的實(shí)現(xiàn)類(lèi)各創(chuàng)建一個(gè)具體的工廠方法去實(shí)現(xiàn)這個(gè)接口

定義WiFiSender的工廠類(lèi):

public class WiFiSenderFactory implements SenderFactory {

    @Override
    public Sender createSender() {
        return new WiFiSender();
    }
}

定義BluetoothSender的工廠類(lèi):

public class BluetoothSenderFactory implements SenderFactory {

    @Override
    public Sender createSender() {
        return new BluetoothSender();
    }
}

這樣卒落,即使有新的Sender實(shí)現(xiàn)類(lèi)加進(jìn)來(lái)羡铲,我們只需要新增相應(yīng)的工廠類(lèi)就行了,不需要修改原有的工廠儡毕,下圖就是工廠方法模式的結(jié)構(gòu)圖:


2019-9-2-11-28-17.png

客戶(hù)端調(diào)用代碼:

    private String mode; //Wi-Fi|Bluetooth

    public void onClick() {
        byte[] data = {0x00, 0x01};

        SenderFactory factory;
        if ("Wi-Fi".equals(mode)) {
            factory = new WiFiSenderFactory();
        } else {
            factory = new BluetoothSenderFactory();
        }
        Sender sender = factory.createSender();
        sender.sendData(data);
    }

細(xì)心的讀者可能已經(jīng)發(fā)現(xiàn)了也切,工廠方法模式實(shí)現(xiàn)時(shí),客戶(hù)端需要決定實(shí)例化哪一個(gè)工廠類(lèi)腰湾,相比于簡(jiǎn)單工廠模式雷恃,客戶(hù)端多了一個(gè)選擇判斷的問(wèn)題,也就是說(shuō)费坊,工廠方法模式把簡(jiǎn)單工廠模式的內(nèi)部邏輯判斷移到了客戶(hù)端倒槐!你想要加功能,本來(lái)是修改簡(jiǎn)單工廠類(lèi)的附井,現(xiàn)在改為修改客戶(hù)端讨越。但是這樣帶來(lái)的好處是整個(gè)工廠和產(chǎn)品體系都沒(méi)有“修改”的變化,只有“擴(kuò)展”的變化羡忘,完全符合了開(kāi)放封閉原則谎痢。

總結(jié)

簡(jiǎn)單工廠模式和工廠方法模式都封裝了對(duì)象的創(chuàng)建,它們使得高層策略模塊在創(chuàng)建類(lèi)的實(shí)例時(shí)無(wú)需依賴(lài)于這些類(lèi)的具體實(shí)現(xiàn)卷雕。但是兩種工廠模式之間又有差異:

  • 簡(jiǎn)單工廠模式:最大的優(yōu)點(diǎn)在于工廠類(lèi)包含了必要的判斷邏輯节猿,根據(jù)客戶(hù)端的條件動(dòng)態(tài)地實(shí)例化相關(guān)的類(lèi)。但這也是它的缺點(diǎn)漫雕,當(dāng)擴(kuò)展功能的時(shí)候滨嘱,需要修改工廠方法,違反了開(kāi)放封閉原則
  • 工廠方法模式:符合開(kāi)放封閉原則浸间,但這帶來(lái)的代價(jià)是擴(kuò)展的時(shí)候要增加相應(yīng)的工廠類(lèi)太雨,增加了開(kāi)發(fā)量,而且需要修改客戶(hù)端代碼
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末魁蒜,一起剝皮案震驚了整個(gè)濱河市囊扳,隨后出現(xiàn)的幾起案子吩翻,更是在濱河造成了極大的恐慌,老刑警劉巖锥咸,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狭瞎,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡搏予,警方通過(guò)查閱死者的電腦和手機(jī)熊锭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)雪侥,“玉大人碗殷,你說(shuō)我怎么就攤上這事∷儆В” “怎么了锌妻?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)旬牲。 經(jīng)常有香客問(wèn)我从祝,道長(zhǎng),這世上最難降的妖魔是什么引谜? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮擎浴,結(jié)果婚禮上员咽,老公的妹妹穿的比我還像新娘。我一直安慰自己贮预,他們只是感情好贝室,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著仿吞,像睡著了一般滑频。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上唤冈,一...
    開(kāi)封第一講書(shū)人閱讀 49,829評(píng)論 1 290
  • 那天峡迷,我揣著相機(jī)與錄音,去河邊找鬼你虹。 笑死绘搞,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的傅物。 我是一名探鬼主播夯辖,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼董饰!你這毒婦竟也來(lái)了蒿褂?” 一聲冷哼從身側(cè)響起圆米,我...
    開(kāi)封第一講書(shū)人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎啄栓,沒(méi)想到半個(gè)月后娄帖,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡谴供,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年块茁,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片桂肌。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡数焊,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出崎场,到底是詐尸還是另有隱情佩耳,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布谭跨,位于F島的核電站干厚,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏螃宙。R本人自食惡果不足惜蛮瞄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望谆扎。 院中可真熱鬧挂捅,春花似錦、人聲如沸堂湖。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)无蜂。三九已至伺糠,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間斥季,已是汗流浹背训桶。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留泻肯,地道東北人渊迁。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像灶挟,于是被迫代替她去往敵國(guó)和親琉朽。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容