設(shè)計(jì)原則:
要依賴抽象,不要依賴具體類
目錄
本文的結(jié)構(gòu)如下:
- 什么是抽象工廠模式
- 為什么要用該模式
- 模式的結(jié)構(gòu)
- 代碼示例
- 優(yōu)點(diǎn)和缺點(diǎn)
- 適用環(huán)境
- 模式應(yīng)用
- 模式擴(kuò)展
- 總結(jié)
一徘意、前言
工廠方法模式通過(guò)引入工廠等級(jí)結(jié)構(gòu),解決了簡(jiǎn)單工廠模式中工廠類職責(zé)太重的問(wèn)題屯吊,但由于工廠方法模式中的每個(gè)工廠只生產(chǎn)一類產(chǎn)品,可能會(huì)導(dǎo)致系統(tǒng)中存在大量的工廠類铅祸,勢(shì)必會(huì)增加系統(tǒng)的開(kāi)銷昔案。此時(shí)乒验,我們可以考慮將一些相關(guān)的產(chǎn)品組成一個(gè)“產(chǎn)品族”愚隧,由同一個(gè)工廠來(lái)統(tǒng)一生產(chǎn),這就是我們本文將要學(xué)習(xí)的抽象工廠模式的基本思想锻全。
二狂塘、什么是抽象工廠模式
抽象工廠模式(Abstract Factory Pattern):提供一個(gè)接口,用于創(chuàng)建一系列相關(guān)或相互依賴對(duì)象的家族鳄厌,而無(wú)須指定它們具體的類荞胡。抽象工廠模式又稱為Kit模式,屬于對(duì)象創(chuàng)建型模式了嚎。
三泪漂、為什么要用該模式
3.1、官方解釋
在工廠方法模式中具體工廠負(fù)責(zé)生產(chǎn)具體的產(chǎn)品歪泳,每一個(gè)具體工廠對(duì)應(yīng)一種具體產(chǎn)品萝勤,工廠方法也具有唯一性,一般情況下呐伞,一個(gè)具體工廠中只有一個(gè)工廠方法或者一組重載的工廠方法敌卓。但是有時(shí)候我們需要一個(gè)工廠可以提供多個(gè)產(chǎn)品對(duì)象,而不是單一的產(chǎn)品對(duì)象伶氢。
為了更清晰地理解工廠方法模式趟径,需要先引入兩個(gè)概念:
- 產(chǎn)品等級(jí)結(jié)構(gòu):產(chǎn)品等級(jí)結(jié)構(gòu)即產(chǎn)品的繼承結(jié)構(gòu),如一個(gè)抽象類是電視機(jī)癣防,其子類有海爾電視機(jī)蜗巧、海信電視機(jī)、TCL電視機(jī)蕾盯,則抽象電視機(jī)與具體品牌的電視機(jī)之間構(gòu)成了一個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)幕屹,抽象電視機(jī)是父類,而具體品牌的電視機(jī)是其子類级遭。
- 產(chǎn)品族:在抽象工廠模式中香嗓,產(chǎn)品族是指由同一個(gè)工廠生產(chǎn)的,位于不同產(chǎn)品等級(jí)結(jié)構(gòu)中的一組產(chǎn)品装畅,如海爾電器工廠生產(chǎn)的海爾電視機(jī)、海爾電冰箱沧烈,海爾電視機(jī)位于電視機(jī)產(chǎn)品等級(jí)結(jié)構(gòu)中掠兄,海爾電冰箱位于電冰箱產(chǎn)品等級(jí)結(jié)構(gòu)中。
當(dāng)系統(tǒng)所提供的工廠所需生產(chǎn)的具體產(chǎn)品并不是一個(gè)簡(jiǎn)單的對(duì)象,而是多個(gè)位于不同產(chǎn)品等級(jí)結(jié)構(gòu)中屬于不同類型的具體產(chǎn)品時(shí)需要使用抽象工廠模式蚂夕。
抽象工廠模式是所有形式的工廠模式中最為抽象和最具一般性的一種形態(tài)迅诬。
抽象工廠模式與工廠方法模式最大的區(qū)別在于,工廠方法模式針對(duì)的是一個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)婿牍,而抽象工廠模式則需要面對(duì)多個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)侈贷,一個(gè)工廠等級(jí)結(jié)構(gòu)可以負(fù)責(zé)多個(gè)不同產(chǎn)品等級(jí)結(jié)構(gòu)中的產(chǎn)品對(duì)象的創(chuàng)建。當(dāng)一個(gè)工廠等級(jí)結(jié)構(gòu)可以創(chuàng)建出分屬于不同產(chǎn)品等級(jí)結(jié)構(gòu)的一個(gè)產(chǎn)品族中的所有對(duì)象時(shí)等脂,抽象工廠模式比工廠方法模式更為簡(jiǎn)單俏蛮、有效率。
3.2上遥、舉個(gè)例子
你的蛋糕店賣得非常好搏屑,為了更好的提供效率,你決定為每個(gè)蛋糕店開(kāi)設(shè)一個(gè)原料提供工廠粉楚,每個(gè)蛋糕店都有專門的原料工廠提供原料辣恋,比如水果蛋糕需要的雞蛋和水果,但CenterCakeStore更多賣的是草莓類水果蛋糕模软,用的雞蛋是一般的雞蛋伟骨,而CollegeCakeStore更多賣的是菠蘿類水果蛋糕,并且用的雞蛋都是土雞蛋燃异,所以原料工廠提供的具體水果和雞蛋是不一樣的携狭,代碼怎么設(shè)計(jì)呢?
這次你沒(méi)有請(qǐng)我特铝,因?yàn)槟阕銐蚵斆魇钪校阒牢铱隙〞?huì)這樣寫:
為每個(gè)原料建立一個(gè)抽象工廠類,提供抽象方法用于獲取產(chǎn)品:
/**
* Created by w1992wishes on 2017/11/1.
*/
public abstract class FruitFactory {
public abstract Fruit provideFruit();
}
/**
* Created by w1992wishes on 2017/11/1.
*/
public abstract class EggFactory {
abstract Egg provideEgg();
}
然后為抽象工廠提供多個(gè)實(shí)現(xiàn)鲫剿,每個(gè)子類工廠提供一種具體的原料:
/**
* Created by w1992wishes on 2017/11/1.
*/
public class CenterFruitFactory extends FruitFactory {
@Override
public Fruit provideFruit() {
return new StrawberryFruit();
}
}
/**
* Created by w1992wishes on 2017/11/1.
*/
public class CollegeFruitFactory extends FruitFactory{
@Override
public Fruit provideFruit() {
return new MangoFruit();
}
}
/**
* Created by w1992wishes on 2017/11/1.
*/
public class CenterEggFactory extends EggFactory {
@Override
Egg provideEgg() {
return new NormalEgg()();
}
}
/**
* Created by w1992wishes on 2017/11/1.
*/
public class CollegeEggFactory extends EggFactory{
@Override
Egg provideEgg() {
return new SpecialEgg();
}
}
接著還有一堆抽象原料鳄逾,Egg,F(xiàn)ruit...
一堆具體原料灵莲,SpecialEgg雕凹,NormalEgg,MangoFruit...
還要在具體的Cake中用具體的原料:
/**
* Created by w1992wishes on 2017/10/31.
*/
public class CenterFruitCake extends Cake {
public CenterFruitCake(){
name = "center fruit cake";
}
@Override
public void prepare(){
FruitFactory fruitFactory = new CenterFruitFactory();
EggFactory eggFactory = new CenterEggFactory();
fruit = fruitFactory.provideFruit();
egg = eggFactory.provideEgg();
}
}
/**
* Created by w1992wishes on 2017/11/1.
*/
public class CollegeFruitCake extends Cake {
public CollegeFruitCake(){
name = "college fruit cake";
}
@Override
public void prepare(){
FruitFactory fruitFactory = new CollegeFruitFactory();
EggFactory eggFactory = new CollegeEggFactory();
fruit = fruitFactory.provideFruit();
egg = eggFactory.provideEgg();
}
}
經(jīng)過(guò)一堆復(fù)雜的類構(gòu)建政冻,最后才能訂購(gòu)枚抵,而且這還只是一種蛋糕,如果需要增加新的蛋糕時(shí)明场,雖然不要修改現(xiàn)有代碼汽摹,但是需要增加大量類,針對(duì)每一個(gè)新增具體組件都需要增加一個(gè)具體工廠苦锨,類的個(gè)數(shù)成對(duì)增加逼泣,這無(wú)疑會(huì)導(dǎo)致系統(tǒng)越來(lái)越龐大趴泌,增加系統(tǒng)的維護(hù)成本和運(yùn)行開(kāi)銷;
正是因?yàn)槊恳粋€(gè)具體工廠對(duì)應(yīng)一種具體產(chǎn)品拉庶,如果產(chǎn)品族很多嗜憔,會(huì)引入很多類,為了解決這個(gè)問(wèn)題氏仗,所以有了抽象工廠模式吉捶。
四、模式的結(jié)構(gòu)
抽象工廠模式結(jié)構(gòu)和工廠方法模式結(jié)構(gòu)差不多皆尔,只是多了產(chǎn)品族中其他的抽象產(chǎn)品和具體產(chǎn)品呐舔。
● Factory(抽象工廠):它聲明了一組用于創(chuàng)建一族產(chǎn)品的方法,每一個(gè)方法對(duì)應(yīng)一種產(chǎn)品床佳。
● ConcreteFactory(具體工廠):它實(shí)現(xiàn)了在抽象工廠中聲明的創(chuàng)建產(chǎn)品的方法滋早,生成一組具體產(chǎn)品,這些產(chǎn)品構(gòu)成了一個(gè)產(chǎn)品族砌们,每一個(gè)產(chǎn)品都位于某個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)中杆麸。
● Product(抽象產(chǎn)品):它為每種產(chǎn)品聲明接口,在抽象產(chǎn)品中聲明了產(chǎn)品所具有的業(yè)務(wù)方法浪感。
● ConcreteProduct(具體產(chǎn)品):它定義具體工廠生產(chǎn)的具體產(chǎn)品對(duì)象昔头,實(shí)現(xiàn)抽象產(chǎn)品接口中聲明的業(yè)務(wù)方法。
五影兽、代碼示例
先定義一個(gè)抽象原料工廠:
/**
* Created by w1992wishes on 2017/11/1.
*/
public interface FoodFactory {
Egg provideEgg();
Fruit provideFruit();
}
每個(gè)蛋糕店對(duì)應(yīng)的具體工廠:
/**
* Created by w1992wishes on 2017/11/1.
*/
public class CenterFoodFactory implements FoodFactory {
@Override
public Egg provideEgg() {
return new NormalEgg();
}
@Override
public Fruit provideFruit() {
return new StrawberryFruit();
}
}
/**
* Created by w1992wishes on 2017/11/1.
*/
public class CollegeFoodFactory implements FoodFactory {
@Override
public Egg provideEgg() {
return new SpecialEgg();
}
@Override
public Fruit provideFruit() {
return new MongoFruit();
}
}
具體的水果蛋糕類:
/**
* Created by w1992wishes on 2017/11/01.
*/
public abstract class Cake {
String name;
Fruit fruit;
Egg egg;
abstract void prepare();
void bake(){
System.out.println("bake");
}
void box(){
System.out.println("box");
}
public String getName() {
return name;
}
public Fruit getFruit(){
return fruit;
}
public Egg getEgg(){
return egg;
}
}
/**
* Created by w1992wishes on 2017/10/31.
*/
public class CenterFruitCake extends Cake {
public CenterFruitCake(){
name = "center fruit cake";
}
@Override
public void prepare(){
FoodFactory foodFactory = new CenterFoodFactory();
fruit = foodFactory.provideFruit();
egg = foodFactory.provideEgg();
}
}
/**
* Created by w1992wishes on 2017/11/1.
*/
public class CollegeFruitCake extends Cake {
public CollegeFruitCake(){
name = "college fruit cake";
}
@Override
public void prepare(){
FoodFactory foodFactory = new CollegeFoodFactory();
fruit = foodFactory.provideFruit();
egg = foodFactory.provideEgg();
}
}
蛋糕店的代碼不變:
/**
* Created by w1992wishes on 2017/11/1.
*/
public class CenterCakeStore extends CakeStore {
@Override
protected Cake createCake(String type) {
Cake cake = null;
if ("cheese".equals(type)) {
cake = new CenterCheeseCake();
} else if ("fruit".equals(type)) {
cake = new CenterFruitCake();
} else if ("cream".equals(type)) {
cake = new CenterCreamCake();
}
return cake;
}
}
/**
* Created by w1992wishes on 2017/11/1.
*/
public class CollegeCakeStore extends CakeStore {
@Override
protected Cake createCake(String type) {
Cake cake = null;
if ("cheese".equals(type)) {
cake = new CollegeCheeseCake();
} else if ("fruit".equals(type)) {
cake = new CollegeFruitCake();
} else if ("cream".equals(type)) {
cake = new CollegeCreamCake();
}
return cake;
}
}
客戶端代碼不變:
/**
* Created by w1992wishes on 2017/11/1.
*/
public class Client {
public static void main(String[] args) {
CakeStore cakeStore = new CollegeCakeStore();//這里可通過(guò)引入配置文件更改
cakeStore.orderCake("fruit");
}
}
看這段代碼可能有點(diǎn)疑惑揭斧,建議不用管蛋糕店,直接看制作蛋糕的原料那塊峻堰,是抽象工廠模式的真正實(shí)現(xiàn)代碼讹开。
相對(duì)于工廠方法模式,抽象工廠方法捐名,即沒(méi)有破壞“開(kāi)閉原則”旦万,同時(shí)又解決了前者引入大量工廠類的弊端,是前者更高層次的抽象镶蹋。
六成艘、優(yōu)點(diǎn)和缺點(diǎn)
6.1、優(yōu)點(diǎn)
- 抽象工廠模式隔離了具體類的生成贺归,使得客戶并不需要知道什么被創(chuàng)建淆两。由于這種隔離,更換一個(gè)具體工廠就變得相對(duì)容易拂酣。所有的具體工廠都實(shí)現(xiàn)了抽象工廠中定義的那些公共接口秋冰,因此只需改變具體工廠的實(shí)例,就可以在某種程度上改變整個(gè)軟件系統(tǒng)的行為婶熬。另外丹莲,應(yīng)用抽象工廠模式可以實(shí)現(xiàn)高內(nèi)聚低耦合的設(shè)計(jì)目的光坝,因此抽象工廠模式得到了廣泛的應(yīng)用。
- 當(dāng)一個(gè)產(chǎn)品族中的多個(gè)對(duì)象被設(shè)計(jì)成一起工作時(shí)甥材,它能夠保證客戶端始終只使用同一個(gè)產(chǎn)品族中的對(duì)象。這對(duì)一些需要根據(jù)當(dāng)前環(huán)境來(lái)決定其行為的軟件系統(tǒng)來(lái)說(shuō)性含,是一種非常實(shí)用的設(shè)計(jì)模式洲赵。
- 增加新的具體工廠和產(chǎn)品族很方便,無(wú)須修改已有系統(tǒng)商蕴,符合“開(kāi)閉原則”叠萍。
6.2、缺點(diǎn)
- 在添加新的產(chǎn)品對(duì)象時(shí)绪商,難以擴(kuò)展抽象工廠來(lái)生產(chǎn)新種類的產(chǎn)品苛谷,這是因?yàn)樵诔橄蠊S角色中規(guī)定了所有可能被創(chuàng)建的產(chǎn)品集合,要支持新種類的產(chǎn)品就意味著要對(duì)該接口進(jìn)行擴(kuò)展格郁,而這將涉及到對(duì)抽象工廠角色及其所有子類的修改腹殿,顯然會(huì)帶來(lái)較大的不便。
- 開(kāi)閉原則的傾斜性例书。
(1) 增加產(chǎn)品族:對(duì)于增加新的產(chǎn)品族锣尉,抽象工廠模式很好地支持了“開(kāi)閉原則”,只需要增加具體產(chǎn)品并對(duì)應(yīng)增加一個(gè)新的具體工廠决采,對(duì)已有代碼無(wú)須做任何修改自沧。
(2) 增加新的產(chǎn)品等級(jí)結(jié)構(gòu):對(duì)于增加新的產(chǎn)品等級(jí)結(jié)構(gòu),需要修改所有的工廠角色树瞭,包括抽象工廠類拇厢,在所有的工廠類中都需要增加生產(chǎn)新產(chǎn)品的方法,違背了“開(kāi)閉原則”晒喷。
正因?yàn)槌橄蠊S模式存在“開(kāi)閉原則”的傾斜性孝偎,它以一種傾斜的方式來(lái)滿足“開(kāi)閉原則”,為增加新產(chǎn)品族提供方便厨埋,但不能為增加新產(chǎn)品結(jié)構(gòu)提供這樣的方便邪媳,因此要求設(shè)計(jì)人員在設(shè)計(jì)之初就能夠全面考慮,不會(huì)在設(shè)計(jì)完成之后向系統(tǒng)中增加新的產(chǎn)品等級(jí)結(jié)構(gòu)荡陷,也不會(huì)刪除已有的產(chǎn)品等級(jí)結(jié)構(gòu)雨效,否則將會(huì)導(dǎo)致系統(tǒng)出現(xiàn)較大的修改,為后續(xù)維護(hù)工作帶來(lái)諸多麻煩废赞。
七徽龟、適用環(huán)境
在以下情況下可以考慮使用抽象工廠模式:
- 一個(gè)系統(tǒng)不應(yīng)當(dāng)依賴于產(chǎn)品類實(shí)例如何被創(chuàng)建、組合和表達(dá)的細(xì)節(jié)唉地,這對(duì)于所有類型的工廠模式都是很重要的据悔,用戶無(wú)須關(guān)心對(duì)象的創(chuàng)建過(guò)程传透,將對(duì)象的創(chuàng)建和使用解耦。
- 系統(tǒng)中有多于一個(gè)的產(chǎn)品族极颓,而每次只使用其中某一產(chǎn)品族朱盐。可以通過(guò)配置文件等方式來(lái)使得用戶可以動(dòng)態(tài)改變產(chǎn)品族菠隆,也可以很方便地增加新的產(chǎn)品族兵琳。
- 屬于同一個(gè)產(chǎn)品族的產(chǎn)品將在一起使用,這一約束必須在系統(tǒng)的設(shè)計(jì)中體現(xiàn)出來(lái)骇径。同一個(gè)產(chǎn)品族中的產(chǎn)品可以是沒(méi)有任何關(guān)系的對(duì)象躯肌,但是它們都具有一些共同的約束,如同一制作水果蛋糕用的水果--草莓和芒果破衔,草莓和芒果之間沒(méi)有直接關(guān)系清女,但它們都是屬于水果。
- 產(chǎn)品等級(jí)結(jié)構(gòu)穩(wěn)定晰筛,設(shè)計(jì)完成之后嫡丙,不會(huì)向系統(tǒng)中增加新的產(chǎn)品等級(jí)結(jié)構(gòu)或者刪除已有的產(chǎn)品等級(jí)結(jié)構(gòu)。
八传惠、模式應(yīng)用
在很多軟件系統(tǒng)中需要更換界面主題迄沫,要求界面中的按鈕、文本框卦方、背景色等一起發(fā)生改變時(shí)羊瘩,可以使用抽象工廠模式進(jìn)行設(shè)計(jì)。
九盼砍、模式擴(kuò)展
“開(kāi)閉原則”的傾斜性
在介紹抽象工廠方法模式的缺點(diǎn)已經(jīng)提到尘吗,這里再重復(fù)啰嗦一下。
“開(kāi)閉原則”要求系統(tǒng)對(duì)擴(kuò)展開(kāi)放浇坐,對(duì)修改封閉睬捶,通過(guò)擴(kuò)展達(dá)到增強(qiáng)其功能的目的。對(duì)于涉及到多個(gè)產(chǎn)品族與多個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)的系統(tǒng)近刘,其功能增強(qiáng)包括兩方面:
- 增加產(chǎn)品族:對(duì)于增加新的產(chǎn)品族擒贸,工廠方法模式很好的支持了“開(kāi)閉原則”,對(duì)于新增加的產(chǎn)品族觉渴,只需要對(duì)應(yīng)增加一個(gè)新的具體工廠即可介劫,對(duì)已有代碼無(wú)須做任何修改。
- 增加新的產(chǎn)品等級(jí)結(jié)構(gòu):對(duì)于增加新的產(chǎn)品等級(jí)結(jié)構(gòu)案淋,需要修改所有的工廠角色座韵,包括抽象工廠類,在所有的工廠類中都需要增加生產(chǎn)新產(chǎn)品的方法,不能很好地支持“開(kāi)閉原則”誉碴。
工廠模式的退化
當(dāng)抽象工廠模式中每一個(gè)具體工廠類只創(chuàng)建一個(gè)產(chǎn)品對(duì)象宦棺,也就是只存在一個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)時(shí),抽象工廠模式退化成工廠方法模式黔帕;當(dāng)工廠方法模式中抽象工廠與具體工廠合并代咸,提供一個(gè)統(tǒng)一的工廠來(lái)創(chuàng)建產(chǎn)品對(duì)象,并將創(chuàng)建對(duì)象的工廠方法設(shè)計(jì)為靜態(tài)方法時(shí)成黄,工廠方法模式退化成簡(jiǎn)單工廠模式侣背。
十、總結(jié)
- 抽象工廠模式提供一個(gè)創(chuàng)建一系列相關(guān)或相互依賴對(duì)象的接口慨默,而無(wú)須指定它們具體的類。抽象工廠模式又稱為Kit模式弧腥,屬于對(duì)象創(chuàng)建型模式厦取。
- 抽象工廠模式包含四個(gè)角色:抽象工廠用于聲明生成抽象產(chǎn)品的方法;具體工廠實(shí)現(xiàn)了抽象工廠聲明的生成抽象產(chǎn)品的方法管搪,生成一組具體產(chǎn)品虾攻,這些產(chǎn)品構(gòu)成了一個(gè)產(chǎn)品族,每一個(gè)產(chǎn)品都位于某個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)中更鲁;抽象產(chǎn)品為每種產(chǎn)品聲明接口霎箍,在抽象產(chǎn)品中定義了產(chǎn)品的抽象業(yè)務(wù)方法;具體產(chǎn)品定義具體工廠生產(chǎn)的具體產(chǎn)品對(duì)象澡为,實(shí)現(xiàn)抽象產(chǎn)品接口中定義的業(yè)務(wù)方法漂坏。
- 抽象工廠模式是所有形式的工廠模式中最為抽象和最具一般性的一種形態(tài)。抽象工廠模式與工廠方法模式最大的區(qū)別在于媒至,工廠方法模式針對(duì)的是一個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)顶别,而抽象工廠模式則需要面對(duì)多個(gè)產(chǎn)品等級(jí)結(jié)構(gòu)。
- 抽象工廠模式的主要優(yōu)點(diǎn)是隔離了具體類的生成拒啰,使得客戶并不需要知道什么被創(chuàng)建驯绎,而且每次可以通過(guò)具體工廠類創(chuàng)建一個(gè)產(chǎn)品族中的多個(gè)對(duì)象,增加或者替換產(chǎn)品族比較方便谋旦,增加新的具體工廠和產(chǎn)品族很方便剩失;主要缺點(diǎn)在于增加新的產(chǎn)品等級(jí)結(jié)構(gòu)很復(fù)雜,需要修改抽象工廠和所有的具體工廠類册着,對(duì)“開(kāi)閉原則”的支持呈現(xiàn)傾斜性拴孤。
- 抽象工廠模式適用情況包括:一個(gè)系統(tǒng)不應(yīng)當(dāng)依賴于產(chǎn)品類實(shí)例如何被創(chuàng)建、組合和表達(dá)的細(xì)節(jié)指蚜;系統(tǒng)中有多于一個(gè)的產(chǎn)品族乞巧,而每次只使用其中某一產(chǎn)品族;屬于同一個(gè)產(chǎn)品族的產(chǎn)品將在一起使用摊鸡;系統(tǒng)提供一個(gè)產(chǎn)品類的庫(kù)绽媒,所有的產(chǎn)品以同樣的接口出現(xiàn)蚕冬,從而使客戶端不依賴于具體實(shí)現(xiàn)。