“工廠”這種設(shè)計模式可能是最廣為人知的模式之一了手蝎。其看起來最為簡單,讀起來也是郎朗上口俐芯。每次面試柑船,讓應聘者講幾種設(shè)計模式,往往都是“工廠模式泼各,單例模式鞍时,代理模式...”,可真正能講清楚“工廠”模式的人卻少之又少扣蜻。本篇文章逆巍,同樣結(jié)合網(wǎng)絡(luò)和自己理解,試著闡述我眼中的“工廠”莽使。
通常所說的“工廠模式”大部分人最直接的理解可能是有個工廠類負責創(chuàng)建各個不同的實例锐极。而實際上,這可能是最簡單的工廠了芳肌,學名其實就叫“簡單工廠”灵再,除此以外,還有看起來更為牛x的“工廠方法”以及“抽象工廠”亿笤。下面一一講開翎迁。
- 簡單工廠
假設(shè)我們需要做一個簡單的計算器,我們定義計算抽象類Operation净薛,它有兩個參數(shù)汪榔,numA和numB,有一個抽象方法getResult肃拜,返回計算的結(jié)果痴腌。另外有它的具體實現(xiàn)類,加法OperationAdd和減法OperationSub燃领。工廠類OperationFactory根據(jù)入?yún)⒎祷鼐唧w的Operation士聪。客戶端類Client調(diào)用工廠類獲取具體的運算方法進行計算猛蔽,最終得到計算結(jié)果剥悟。
下面上代碼:
/**
* 功能簡述: 運算符抽象類,輸入兩個整數(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è)計模式中,這叫違背了“開閉原則”特咆!那么我們要怎么辦,“工廠方法”可以解決我們的煩惱录粱。
- 工廠方法
工廠方法模式去掉了簡單工廠中工廠方法的靜態(tài)屬性腻格,并且抽象了工廠,也就是具體為加法還是減法工廠的壓力由工廠子類來分擔啥繁,沒有一個無敵的工廠類負責創(chuàng)建各類具體實現(xiàn)菜职,也就避免了業(yè)務調(diào)整時需要調(diào)整這個無敵類。工廠方法的類圖:
下面上具體代碼:
/**
* 功能簡述: 運算符抽象類旗闽,輸入兩個整數(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),我們要怎么做整袁?
我們新增了運算具體實現(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)有一些感覺了。
- 抽象工廠
抽象工廠模式是工廠方法模式的升級版本痹仙,它用來創(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)瀑志。
可以發(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)成。
顯然庸推,一個系統(tǒng)只能夠在某一個操作系統(tǒng)的視窗環(huán)境下運行常侦,而不能同時在不同的操作系統(tǒng)上運行。所以贬媒,系統(tǒng)實際上只能消費屬于同一個產(chǎn)品族的產(chǎn)品聋亡。
用上面的例子實現(xiàn)的類圖及代碼如下:
/**
* 功能簡述: 按鈕接口<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ì)量提高现喳。希望大家不斷進步凯傲。