設(shè)計模式之工廠模式

“工廠”這種設(shè)計模式可能是最廣為人知的模式之一了手蝎。其看起來最為簡單,讀起來也是郎朗上口俐芯。每次面試柑船,讓應聘者講幾種設(shè)計模式,往往都是“工廠模式泼各,單例模式鞍时,代理模式...”,可真正能講清楚“工廠”模式的人卻少之又少扣蜻。本篇文章逆巍,同樣結(jié)合網(wǎng)絡(luò)和自己理解,試著闡述我眼中的“工廠”莽使。

通常所說的“工廠模式”大部分人最直接的理解可能是有個工廠類負責創(chuàng)建各個不同的實例锐极。而實際上,這可能是最簡單的工廠了芳肌,學名其實就叫“簡單工廠”灵再,除此以外,還有看起來更為牛x的“工廠方法”以及“抽象工廠”亿笤。下面一一講開翎迁。

  1. 簡單工廠

假設(shè)我們需要做一個簡單的計算器,我們定義計算抽象類Operation净薛,它有兩個參數(shù)汪榔,numA和numB,有一個抽象方法getResult肃拜,返回計算的結(jié)果痴腌。另外有它的具體實現(xiàn)類,加法OperationAdd和減法OperationSub燃领。工廠類OperationFactory根據(jù)入?yún)⒎祷鼐唧w的Operation士聪。客戶端類Client調(diào)用工廠類獲取具體的運算方法進行計算猛蔽,最終得到計算結(jié)果剥悟。

簡單工廠.png

下面上代碼:

/**
 * 功能簡述: 運算符抽象類,輸入兩個整數(shù),返回結(jié)果
 *
 * @author 
 */
public abstract class Operation {

    protected int numA;

    protected int numB;

    public abstract int getResult();
}
/**
 * 功能簡述: 加法實現(xiàn)類
 *
 * @author
 */
public class OperationAdd extends Operation {

    @Override
    public int getResult() {
        return numA + numB;
    }
}
/**
 * 功能簡述: 減法實現(xiàn)類
 *
 * @author
 */
public class OperationSub extends Operation {

    @Override
    public int getResult() {
        return numA - numB;
    }
}
/**
 * 功能簡述: 簡單工廠類
 *
 * @author
 */
public class OperationFactory {
    public static Operation createOperate(String operate) {
        Operation oper = null;
        switch (operate) {
            case "+":
                oper = new OperationAdd();
                break;
            case "-":
                oper = new OperationSub();
                break;
        }
        return oper;
    }
}
/**
 * 功能簡述: 客戶端調(diào)用
 *
 * @author 
 */
public class Client {

    public static void main(String[] args) {
        //輸出3
        Operation oper = OperationFactory.createOperate("+");
        if (null != oper) {
            oper.numA = 1;
            oper.numB = 2;
            System.out.println(oper.getResult());
        }
        //-----------------------------------------------------------------------
        //輸出-1
        oper = OperationFactory.createOperate("-");
        if (null != oper) {
            oper.numA = 1;
            oper.numB = 2;
            System.out.println(oper.getResult());
        }
    }
}

上面的代碼運行無問題懦胞。也正是簡單工廠的實現(xiàn)方法。我在第一篇提到簡單工廠的問題凉泄,關(guān)于“開閉原則”(移步設(shè)計模式之開篇)躏尉。這里把相同的問題同樣記下來,省掉你移步的麻煩后众。

現(xiàn)在我們需要增加乘法的實現(xiàn)類胀糜,我們需要怎么做?首先蒂誉,需要寫一個OperationMul繼承Operation教藻,實現(xiàn)父類中的getResult方法,返回乘積右锨;其次括堤,需要在工廠類OperationFactory中新增一段case語句,當case"*"號時返回OperationMul實現(xiàn)绍移;最后悄窃,在client方法中測試實現(xiàn)。

/**
 * 功能簡述: 乘法實現(xiàn)類
 *
 * @author 
 */
public class OperationMul extends Operation {

    @Override
    public int getResult() {
        return numA * numB;
    }
}
/**
 * 功能簡述: 簡單工廠類蹂窖,改造轧抗,新增了對*的處理
 *
 * @author
 */
public class OperationFactory {
    public static Operation createOperate(String operate) {
        Operation oper = null;
        switch (operate) {
            case "+":
                oper = new OperationAdd();
                break;
            case "-":
                oper = new OperationSub();
                break;
            case "*":
                oper = new OperationMul();
                break;
        }
        return oper;
    }
}
/**
 * 功能簡述: 客戶端調(diào)用
 *
 * @author 
 */
public class Client {

    public static void main(String[] args) {
        //輸出3
        Operation oper = OperationFactory.createOperate("+");
        if (null != oper) {
            oper.numA = 1;
            oper.numB = 2;
            System.out.println(oper.getResult());
        }
        //-----------------------------------------------------------------------
        //輸出-1
        oper = OperationFactory.createOperate("-");
        if (null != oper) {
            oper.numA = 1;
            oper.numB = 2;
            System.out.println(oper.getResult());
        }
        //-----------------------------------------------------------------------
        //輸出2
        oper = OperationFactory.createOperate("*");
        if (null != oper) {
            oper.numA = 1;
            oper.numB = 2;
            System.out.println(oper.getResult());
        }
    }
}

到目前為止,上面的代碼運行無問題瞬测,大部分人也是這么做的横媚。好,假設(shè)又來需求了月趟,我們需要增加除法的實現(xiàn)類灯蝴?需要怎么做?我們不僅需要增加除法的具體實現(xiàn)類孝宗,同樣需要修改工廠類绽乔。這里有什么問題?我們不斷的在新增類碳褒,同時又修改類折砸!要求做單元測試的同學們可能會有感觸了,測試同學同樣會有感觸吧沙峻?因為修改了一個類睦授,這個類的UT覆蓋率下降,同時有沒有引入其他問題摔寨?測試同學會想去枷,你修改了原來類的代碼,我是不是需要回歸全部場景?上面的實例代碼是非常簡單的,如在實際開發(fā)中可能業(yè)務會復雜的多鱼蝉,修改一個類你得小心再小心拨扶。在設(shè)計模式中,這叫違背了“開閉原則”特咆!那么我們要怎么辦,“工廠方法”可以解決我們的煩惱录粱。

  1. 工廠方法

工廠方法模式去掉了簡單工廠中工廠方法的靜態(tài)屬性腻格,并且抽象了工廠,也就是具體為加法還是減法工廠的壓力由工廠子類來分擔啥繁,沒有一個無敵的工廠類負責創(chuàng)建各類具體實現(xiàn)菜职,也就避免了業(yè)務調(diào)整時需要調(diào)整這個無敵類。工廠方法的類圖:

工廠方法.png

下面上具體代碼:

/**
 * 功能簡述: 運算符抽象類旗闽,輸入兩個整數(shù)酬核,返回結(jié)果
 *
 * @author 
 */
public abstract class Operation {

    protected int numA;

    protected int numB;

    public abstract int getResult();
}
/**
 * 功能簡述: 加法實現(xiàn)類
 *
 * @author
 */
public class OperationAdd extends Operation {

    @Override
    public int getResult() {
        return numA + numB;
    }
}

這一部分沒有變化,抽象計算方法适室,具體計算方法愁茁。變化在下面這個部分:

/**
 * 功能簡述: 抽象工廠類,此類只定義接口亭病,不定義實現(xiàn)鹅很,此為和簡單工廠的區(qū)別之一
 *
 * @author
 */
public interface OperationFactory {

    Operation createOperate();
}
/**
 * 功能簡述: 加法工廠類
 *
 * @author
 */
public class OperationAddFactory implements OperationFactory {

    @Override
    public Operation createOperate() {
        return new OperationAdd();
    }
}
/**
 * 功能簡述: 客戶端調(diào)用
 *
 * @author 
 */
public class Client {

    public static void main(String[] args) {
        OperationAddFactory addFactory = new OperationAddFactory();
        Operation oper = addFactory.createOperate();
        oper.numA = 1;
        oper.numB = 2;
        //輸出3
        System.out.println(oper.getResult());
    }
}

為了避免過多展示不需要的代碼,在這個例子中稍微簡化了下罪帖,移除了減法的實現(xiàn)促煮。下面我們需要新增減法的實現(xiàn),我們要怎么做整袁?

工廠方法.png

我們新增了運算具體實現(xiàn)類OperationSub菠齿,新增了減法工廠OperationSubFactory,然后就可以進行測試驗證了坐昙。在這種實現(xiàn)中绳匀,我們只是新增代碼,沒有修改代碼炸客!

/**
 * 功能簡述: 減法實現(xiàn)類
 *
 * @author 
 */
public class OperationSub extends Operation {

    @Override
    public int getResult() {
        return numA - numB;
    }
}
/**
 * 功能簡述:減法工廠類
 *
 * @author
 */
public class OperationSubFactory implements OperationFactory {

    @Override
    public Operation createOperate() {
        return new OperationSub();
    }
}
/**
 * 功能簡述: 測試實現(xiàn)
 *
 * @author
 */
public class Client {

    /**
     * 功能描述: <br>
     *
     * @param args
     */
    public static void main(String[] args) {
        OperationAddFactory addFactory = new OperationAddFactory();
        Operation oper = addFactory.createOperate();
        oper.numA = 1;
        oper.numB = 2;
        //輸出3
        System.out.println(oper.getResult());

        OperationSubFactory subFactory = new OperationSubFactory();
        oper = subFactory.createOperate();
        oper.numA = 1;
        oper.numB = 2;
        //輸出-1
        System.out.println(oper.getResult());
    }
}

看到這里疾棵,可能你已經(jīng)有一些感覺了。

  1. 抽象工廠

抽象工廠模式是工廠方法模式的升級版本痹仙,它用來創(chuàng)建一組相關(guān)或者相互依賴的對象是尔。

抽象工廠比較難以理解,主要是不知道解決哪一類問題开仰,而且看起來和工廠方法非常類似拟枚,工廠子類不僅創(chuàng)建一個具體產(chǎn)品實現(xiàn)薪铜,更可能會創(chuàng)建多個。想要清楚的理解抽象工廠就需要從它的起源說起恩溅。

抽象工廠模式的起源或者最早的應用隔箍,是用于創(chuàng)建分屬于不同操作系統(tǒng)的視窗構(gòu)建。比如:命令按鍵(Button)與文字框(Text)都是視窗構(gòu)建脚乡,在UNIX操作系統(tǒng)的視窗環(huán)境和Windows操作系統(tǒng)的視窗環(huán)境中蜒滩,這兩個構(gòu)建有不同的本地實現(xiàn),它們的細節(jié)有所不同每窖。

在每一個操作系統(tǒng)中帮掉,都有一個視窗構(gòu)建組成的構(gòu)建家族弦悉。在這里就是Button和Text組成的產(chǎn)品族窒典。而每一個視窗構(gòu)件都構(gòu)成自己的等級結(jié)構(gòu),由一個抽象角色給出抽象的功能描述稽莉,而由具體子類給出不同操作系統(tǒng)下的具體實現(xiàn)瀑志。

產(chǎn)品類.png

可以發(fā)現(xiàn)在上面的產(chǎn)品類圖中,有兩個產(chǎn)品的等級結(jié)構(gòu)污秆,分別是Button等級結(jié)構(gòu)和Text等級結(jié)構(gòu)劈猪。同時有兩個產(chǎn)品族,也就是UNIX產(chǎn)品族和Windows產(chǎn)品族良拼。UNIX產(chǎn)品族由UNIX Button和UNIX Text產(chǎn)品構(gòu)成战得;而Windows產(chǎn)品族由Windows Button和Windows Text產(chǎn)品構(gòu)成。

產(chǎn)品族.png

顯然庸推,一個系統(tǒng)只能夠在某一個操作系統(tǒng)的視窗環(huán)境下運行常侦,而不能同時在不同的操作系統(tǒng)上運行。所以贬媒,系統(tǒng)實際上只能消費屬于同一個產(chǎn)品族的產(chǎn)品聋亡。

用上面的例子實現(xiàn)的類圖及代碼如下:

抽象工廠.png
/**
 * 功能簡述: 按鈕接口<br>
 *
 */
public interface Button {

}
/**
 * 功能簡述: unix系統(tǒng)按鈕實現(xiàn)類<br>
 *
 */
public class UnixButton implements Button {

    public UnixButton() {
        System.out.println("I am unix button!");
    }
}
/**
 * 功能簡述: windows系統(tǒng)按鈕實現(xiàn)類<br>
 *
 */
public class WindowsButton implements Button {

    public WindowsButton() {
        System.out.println("I am windows button际乘!");
    }
}
/**
 * 功能簡述: 文本框接口<br>
 *
 */
public interface Text {

}
/**
 * 功能簡述: unix系統(tǒng)文本框?qū)崿F(xiàn)類<br>
 *
 */
public class UnixText implements Text {

    public UnixText() {
        System.out.println("I am unix text坡倔!");
    }
}
/**
 * 功能簡述: windows系統(tǒng)文本框?qū)崿F(xiàn)類<br>
 *
 */
public class WindowsText implements Text {

    public WindowsText() {
        System.out.println("I am windows text!");
    }
}

上面定義了兩個接口脖含,分別為Button和Text罪塔,表示兩類抽象產(chǎn)品,另外還有每個接口對應Unix和Windows的兩個實現(xiàn)养葵。

/**
 * 功能簡述: 抽象工廠接口<br>
 *
 */
public interface AbstractFactory {

    /**
     * 
     * 功能描述: 創(chuàng)建按鈕工廠<br>
     *
     * @return
     */
    public Button createButton();

    /**
     * 
     * 功能描述: 創(chuàng)建文本工廠<br>
     *
     * @return
     */
    public Text createText();
}

抽象工廠類和簡單工廠不一致的地方在于其定義了兩個方法垢袱,返回同一產(chǎn)品族的Button和Text。下面定義Unix和Windows不同的實現(xiàn):

/**
 * 功能簡述: unix系統(tǒng)實現(xiàn)<br>
 *
 */
public class UnixSystem implements AbstractFactory {

    @Override
    public Button createButton() {
        return new UnixButton();
    }

    @Override
    public Text createText() {
        return new UnixText();
    }
}
/**
 * 功能簡述: windows系統(tǒng)實現(xiàn)<br>
 *
 */
public class WindowsSystem implements AbstractFactory {

    @Override
    public Button createButton() {
        return new WindowsButton();
    }

    @Override
    public Text createText() {
        return new WindowsText();
    }
}

可以看到港柜,Unix實現(xiàn)類返回Unix的UnixButton和UnixText请契,Windows實現(xiàn)類返回Windows的WindowsButton和WindowsText咳榜。每類工廠都有兩個以上的具體產(chǎn)品,形成一個產(chǎn)品族爽锥。

/**
 * 功能簡述: 測試類<br>
 *
 */
public class Client {

    /**
     * 功能描述: 測試方法<br>
     *
     * @param args
     */
    public static void main(String[] args) {
        UnixSystem unixSys = new UnixSystem();
        unixSys.createButton();
        unixSys.createText();

        WindowsSystem winSys = new WindowsSystem();
        winSys.createButton();
        winSys.createText();
    }

    // 運行結(jié)果
    // I am unix button涌韩!
    // I am unix text!
    // I am windows button氯夷!
    // I am windows text臣樱!
}

通過以上的幾個例子,已經(jīng)講完了“工廠”設(shè)計模式腮考。

當然實際開發(fā)中雇毫,總有不同的實現(xiàn),你能看出是“工廠”模式的影子踩蔚,但仔細對比發(fā)現(xiàn)卻又不是“工廠”的樣子棚放。我覺得,設(shè)計模式只是提供的一個標準的模板馅闽,是一個標桿飘蚯,每個不同的實現(xiàn)圍繞標桿即可。設(shè)計模式的最終目的是為了解耦福也,便于軟件的維護局骤。在實際開發(fā)中使用設(shè)計模式固然是好事,但也不能為了設(shè)計模式而設(shè)計暴凑,我們應當多嘗試峦甩,時間久了就會自然而然的在軟件設(shè)計中使用模式,使得軟件質(zhì)量提高现喳。希望大家不斷進步凯傲。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市拿穴,隨后出現(xiàn)的幾起案子泣洞,更是在濱河造成了極大的恐慌,老刑警劉巖默色,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件球凰,死亡現(xiàn)場離奇詭異,居然都是意外死亡腿宰,警方通過查閱死者的電腦和手機呕诉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吃度,“玉大人甩挫,你說我怎么就攤上這事〈幻浚” “怎么了伊者?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵英遭,是天一觀的道長。 經(jīng)常有香客問我亦渗,道長挖诸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任法精,我火速辦了婚禮多律,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘搂蜓。我一直安慰自己狼荞,他們只是感情好,可當我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布帮碰。 她就那樣靜靜地躺著相味,像睡著了一般。 火紅的嫁衣襯著肌膚如雪收毫。 梳的紋絲不亂的頭發(fā)上攻走,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天殷勘,我揣著相機與錄音此再,去河邊找鬼。 笑死玲销,一個胖子當著我的面吹牛输拇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播贤斜,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼策吠,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了瘩绒?” 一聲冷哼從身側(cè)響起猴抹,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎锁荔,沒想到半個月后蟀给,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡阳堕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年跋理,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片恬总。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡前普,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出壹堰,到底是詐尸還是另有隱情拭卿,我是刑警寧澤骡湖,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站峻厚,受9級特大地震影響勺鸦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜目木,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一换途、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧刽射,春花似錦军拟、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至摹恰,卻和暖如春辫继,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背俗慈。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工姑宽, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人闺阱。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓炮车,卻偏偏與公主長得像,于是被迫代替她去往敵國和親酣溃。 傳聞我的和親對象是個殘疾皇子瘦穆,可洞房花燭夜當晚...
    茶點故事閱讀 45,033評論 2 355

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

  • 工廠模式是最常用的一類創(chuàng)建型設(shè)計模式,之前一直以為工廠模式只是23中設(shè)計模式中的一種赊豌,重新了解才知道這個模式還要細...
    晨鳴code閱讀 1,276評論 0 6
  • 一扛或、簡單工廠模式 簡單工廠模式又稱靜態(tài)工廠方法模式。它存在的目的很簡單:將對象的創(chuàng)建過程封裝進一個類碘饼。 先來看看它...
    innovatorCL閱讀 439評論 0 1
  • 1. 工廠方法模式 使用場景 工廠方法模式是new一個對象的替代品熙兔,所以在所有需要生成對象的地方都可以使用,到那時...
    niaoge2016閱讀 344評論 0 1
  • 設(shè)計模式中主要分為三大類:創(chuàng)建型派昧、結(jié)構(gòu)型黔姜、行為型 工廠模式屬于創(chuàng)建型,顧名思義蒂萎,創(chuàng)建型模式關(guān)注對象的創(chuàng)建過程秆吵,它將...
    嘿嘿_小于同學閱讀 272評論 0 3
  • 使用優(yōu)盤為盒子安裝應用軟件,是盒子安裝第三方軟件的最簡單的方法五慈,但不是唯一的方法纳寂。不過在使用安裝方法之前主穗,需先去網(wǎng)...
    ckyyouknow閱讀 254評論 2 3