概述
每一個模式都是針對一定問題的解決方案囚灼。抽象工廠模式與工廠方法模式的最大區(qū)別就在于螃宙,工廠方法模式針對的是一個產(chǎn)品等級結(jié)構(gòu)雁社;而抽象工廠模式則需要面對多個產(chǎn)品等級結(jié)構(gòu)。
在學(xué)習(xí)抽象工廠具體實例之前久脯,應(yīng)該明白兩個重要的概念:產(chǎn)品族和產(chǎn)品等級纳胧。
所謂產(chǎn)品族,是指位于不同產(chǎn)品等級結(jié)構(gòu)中帘撰,功能相關(guān)聯(lián)的產(chǎn)品組成的家族跑慕。比如AMD的主板、芯片組摧找、CPU組成一個家族核行,Intel的主板、芯片組蹬耘、CPU組成一個家族芝雪。而這兩個家族都來自于三個產(chǎn)品等級:主板、芯片組综苔、CPU惩系。一個等級結(jié)構(gòu)是由相同的結(jié)構(gòu)的產(chǎn)品組成,示意圖如下:
顯然如筛,每一個產(chǎn)品族中含有產(chǎn)品的數(shù)目堡牡,與產(chǎn)品等級結(jié)構(gòu)的數(shù)目是相等的。產(chǎn)品的等級結(jié)構(gòu)與產(chǎn)品族將產(chǎn)品按照不同方向劃分杨刨,形成一個二維的坐標(biāo)系晤柄。橫軸表示產(chǎn)品的等級結(jié)構(gòu),縱軸表示產(chǎn)品族妖胀,上圖共有兩個產(chǎn)品族芥颈,分布于三個不同的產(chǎn)品等級結(jié)構(gòu)中惠勒。只要指明一個產(chǎn)品所處的產(chǎn)品族以及它所屬的等級結(jié)構(gòu),就可以唯一的確定這個產(chǎn)品浇借。
上面所給出的三個不同的等級結(jié)構(gòu)具有平行的結(jié)構(gòu)。因此怕品,如果采用工廠方法模式妇垢,就勢必要使用三個獨立的工廠等級結(jié)構(gòu)來對付這三個產(chǎn)品等級結(jié)構(gòu)。由于這三個產(chǎn)品等級結(jié)構(gòu)的相似性肉康,會導(dǎo)致三個平行的工廠等級結(jié)構(gòu)闯估。隨著產(chǎn)品等級結(jié)構(gòu)的數(shù)目的增加,工廠方法模式所給出的工廠等級結(jié)構(gòu)的數(shù)目也會隨之增加吼和。如下圖:
那么涨薪,是否可以使用同一個工廠等級結(jié)構(gòu)來對付這些相同或者極為相似的產(chǎn)品等級結(jié)構(gòu)呢?當(dāng)然可以的炫乓,而且這就是抽象工廠模式的好處刚夺。同一個工廠等級結(jié)構(gòu)負(fù)責(zé)三個不同產(chǎn)品等級結(jié)構(gòu)中的產(chǎn)品對象的創(chuàng)建。
可以看出末捣,一個工廠等級結(jié)構(gòu)可以創(chuàng)建出分屬于不同產(chǎn)品等級結(jié)構(gòu)的一個產(chǎn)品族中的所有對象侠姑。顯然,這時候抽象工廠模式比簡單工廠模式箩做、工廠方法模式更有效率莽红。對應(yīng)于每一個產(chǎn)品族都有一個具體工廠。而每一個具體工廠負(fù)責(zé)創(chuàng)建屬于同一個產(chǎn)品族邦邦,但是分屬于不同等級結(jié)構(gòu)的產(chǎn)品安吁。
抽象工廠模式結(jié)構(gòu)
抽象工廠模式是對象的創(chuàng)建模式,它是工廠方法模式的進(jìn)一步推廣燃辖。
假設(shè)一個子系統(tǒng)需要一些產(chǎn)品對象鬼店,而這些產(chǎn)品又屬于一個以上的產(chǎn)品等級結(jié)構(gòu)。那么為了將消費這些產(chǎn)品對象的責(zé)任和創(chuàng)建這些產(chǎn)品對象的責(zé)任分割開來黔龟,可以引進(jìn)抽象工廠模式薪韩。這樣的話,消費產(chǎn)品的一方不需要直接參與產(chǎn)品的創(chuàng)建工作捌锭,而只需要向一個公用的工廠接口請求所需要的產(chǎn)品俘陷。
通過使用抽象工廠模式,可以處理具有相同(或者相似)等級結(jié)構(gòu)中的多個產(chǎn)品族中的產(chǎn)品對象的創(chuàng)建問題观谦。如下圖所示:
由于這兩個產(chǎn)品族的等級結(jié)構(gòu)相同拉盾,因此使用同一個工廠族也可以處理這兩個產(chǎn)品族的創(chuàng)建問題豁状,這就是抽象工廠模式倒得。
根據(jù)產(chǎn)品角色的結(jié)構(gòu)圖,就不難給出工廠角色的結(jié)構(gòu)設(shè)計圖夭禽。
可以看出霞掺,每一個工廠角色都有兩個工廠方法,分別負(fù)責(zé)創(chuàng)建分屬不同產(chǎn)品等級結(jié)構(gòu)的產(chǎn)品對象讹躯。
源代碼
/**
* 處理器產(chǎn)品接口
*/
public interface Cpu {
public void showMsg();
}
/**
* 主板產(chǎn)品接口
*/
public interface Mainboard {
public void showMsg();
}
/**
* 具體的產(chǎn)品菩彬,intel處理器
*/
public class IntelCpu implements Cpu {
@Override
public void showMsg() {
System.out.println("處理器:intel");
}
}
/**
* 具體的產(chǎn)品,amd處理器
*/
public class AmdCpu implements Cpu {
@Override
public void showMsg() {
System.out.println("處理器:amd");
}
}
/**
* 具體的產(chǎn)品潮梯,intel主板
*/
public class IntelMainboard implements Mainboard {
@Override
public void showMsg() {
System.out.println("主板:intel");
}
}
/**
* 具體的產(chǎn)品骗灶,amd主板
*/
public class AmdMainboard implements Mainboard {
@Override
public void showMsg() {
System.out.println("主板:amd");
}
}
/**
* 抽象工廠
*/
public interface AbstractFactory {
/**
* 創(chuàng)建cpu
* @return
*/
public Cpu createCpu();
/**
* 創(chuàng)建主板
* @return
*/
public Mainboard createMainboard();
}
/**
* 具體的工廠,amd產(chǎn)品族工廠
*/
public class AmdFactory implements AbstractFactory {
@Override
public Cpu createCpu() {
return new AmdCpu();
}
@Override
public Mainboard createMainboard() {
return new AmdMainboard();
}
}
/**
* 具體的工廠秉馏,intel產(chǎn)品族工廠
*/
public class IntelFactory implements AbstractFactory {
@Override
public Cpu createCpu() {
return new IntelCpu();
}
@Override
public Mainboard createMainboard() {
return new IntelMainboard();
}
}
/**
* 客戶端
*/
public class Client {
public static void main(String[] args) {
//amd工廠耙旦,若要生產(chǎn)intel的產(chǎn)品,只需在new 工廠時改為 new intelFactory即可
AbstractFactory factory = new AmdFactory();
Cpu cpu = factory.createCpu();
Mainboard mainboard = factory.createMainboard();
cpu.showMsg();
mainboard.showMsg();
}
}
抽象工廠的功能是為一系列相關(guān)對象或相互依賴的對象創(chuàng)建一個接口萝究。一定要注意免都,這個接口內(nèi)的方法不是任意堆砌的,而是一系列相關(guān)或相互依賴的方法帆竹。比如上面例子中的主板和CPU琴昆,都是為了組裝一臺電腦的相關(guān)對象。不同的裝機方案馆揉,代表一種具體的電腦系列业舍。
由于抽象工廠定義的一系列對象通常是相關(guān)或相互依賴的,這些產(chǎn)品對象就構(gòu)成了一個產(chǎn)品族升酣,也就是抽象工廠定義了一個產(chǎn)品族舷暮。
這就帶來非常大的靈活性,切換產(chǎn)品族的時候噩茄,只要提供不同的抽象工廠實現(xiàn)就可以了下面,也就是說現(xiàn)在是以一個產(chǎn)品族作為一個整體被切換。
在什么情況下應(yīng)當(dāng)使用抽象工廠模式
1.一個系統(tǒng)不應(yīng)當(dāng)依賴于產(chǎn)品類實例如何被創(chuàng)建绩聘、組合和表達(dá)的細(xì)節(jié)沥割,這對于所有形態(tài)的工廠模式都是重要的。
2.這個系統(tǒng)的產(chǎn)品有多于一個的產(chǎn)品族凿菩,而系統(tǒng)只消費其中某一族的產(chǎn)品机杜。
3.同屬于同一個產(chǎn)品族的產(chǎn)品是在一起使用的,這一約束必須在系統(tǒng)的設(shè)計中體現(xiàn)出來衅谷。(比如:Intel主板必須使用Intel CPU、Intel芯片組)****
4.系統(tǒng)提供一個產(chǎn)品類的庫蚀苛,所有的產(chǎn)品以同樣的接口出現(xiàn)在验,從而使客戶端不依賴于實現(xiàn)腋舌。
抽象工廠模式的起源
抽象工廠模式的起源或者最早的應(yī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)篮灼,它們的細(xì)節(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)對產(chǎn)品對象的創(chuàng)建需求由一個工程的等級結(jié)構(gòu)滿足普泡,其中有兩個具體工程角色撼班,即UnixFactory和WindowsFactory砰嘁。UnixFactory對象負(fù)責(zé)創(chuàng)建Unix產(chǎn)品族中的產(chǎn)品矮湘,而WindowsFactory對象負(fù)責(zé)創(chuàng)建Windows產(chǎn)品族中的產(chǎn)品板祝。這就是抽象工廠模式的應(yīng)用走净,抽象工廠模式的解決方案如下圖:
顯然橘洞,一個系統(tǒng)只能夠在某一個操作系統(tǒng)的視窗環(huán)境下運行说搅,而不能同時在不同的操作系統(tǒng)上運行。所以适肠,系統(tǒng)實際上只能消費屬于同一個產(chǎn)品族的產(chǎn)品侯养。
在現(xiàn)代的應(yīng)用中,抽象工廠模式的使用范圍已經(jīng)大大擴大了柠傍,不再要求系統(tǒng)只能消費某一個產(chǎn)品族了惧笛。因此患整,可以不必理會前面所提到的原始用意并级。
抽象工廠模式的優(yōu)點
分離接口和實現(xiàn)
客戶端使用抽象工廠來創(chuàng)建需要的對象,而客戶端根本就不知道具體的實現(xiàn)是誰稻励,客戶端只是面向產(chǎn)品的接口編程而已望抽。也就是說煤篙,客戶端從具體的產(chǎn)品實現(xiàn)中解耦。
使切換產(chǎn)品族變得容易
因為一個具體的工廠實現(xiàn)代表的是一個產(chǎn)品族苛茂,比如上面例子的從Intel系列到AMD系列只需要切換一下具體工廠妓羊。
抽象工廠模式的缺點
不太容易擴展新的產(chǎn)品
如果需要給整個產(chǎn)品族添加一個新的產(chǎn)品,那么就需要修改抽象工廠,這樣就會導(dǎo)致修改所有的工廠實現(xiàn)類剥哑。
抽象工廠和工廠方法的區(qū)別
抽象工廠模式與工廠方法模式的最大區(qū)別就在于星持,工廠方法模式針對的是一個產(chǎn)品等級結(jié)構(gòu)督暂;而抽象工廠模式則需要面對多個產(chǎn)品等級結(jié)構(gòu)逻翁。
在什么情況下應(yīng)當(dāng)使用抽象工廠模式:
這個系統(tǒng)的產(chǎn)品有多于一個的產(chǎn)品族捡鱼,而系統(tǒng)只消費其中某一族的產(chǎn)品驾诈。比如XX應(yīng)用系統(tǒng)有快捷版跟標(biāo)準(zhǔn)版兩個版本管引,這就是兩個產(chǎn)品族褥伴,而具體使用哪一個產(chǎn)品族是實施人員在部署的時候設(shè)置后臺參數(shù)來決定的漾狼。
同屬于同一個產(chǎn)品族的產(chǎn)品是在一起使用的似踱,這一約束必須在系統(tǒng)的設(shè)計中體現(xiàn)出來稽煤。比如:快捷版數(shù)據(jù)保存在XML文件中狞洋,而標(biāo)準(zhǔn)版數(shù)據(jù)保存在數(shù)據(jù)庫中吉懊。將UserDAO和DeptDAO看作是這兩個版本(產(chǎn)品族)擁有的產(chǎn)品借嗽,而抽象工廠有兩個具體工廠實現(xiàn)類ShortcutFactory浆竭、StandardFactory負(fù)責(zé)分別創(chuàng)建快捷版ShortcutUserDAO惨寿、ShortcutDeptDAO和標(biāo)準(zhǔn)版StandardUserDAO顺囊、StandardDeptDAO。當(dāng)用戶操作刪除一個部門的時候蕉拢,先需要刪除這個部門下的所有用戶特碳。假如不使用抽象工廠可能會出現(xiàn)如下情況:后臺刪除用戶時調(diào)用的是快捷版的ShortcutUserDAO類,刪除部門時調(diào)用的是StandardDeptDAO類晕换。這樣就會出現(xiàn)問題午乓。
如果使用抽象工廠,那抽象工廠的具體實現(xiàn)類會幫你創(chuàng)建一個產(chǎn)品族中的一系列產(chǎn)品對象闸准,如標(biāo)準(zhǔn)版的StandardFactory工廠會創(chuàng)建StandardUserDAO益愈、StandardDeptDAO,快捷版的ShortcutFactory工廠會創(chuàng)建ShortcutUserDAO恕汇、ShortcutDeptDAO腕唧。
其實抽象工廠就是起到了一定的約束作用或辖,它所創(chuàng)建的都是同一個產(chǎn)品族中的一系列產(chǎn)品對象颂暇。防止出現(xiàn)上面例子中創(chuàng)建不同產(chǎn)品族中產(chǎn)品所帶來的問題。
總結(jié)
無論是簡單工廠模式,工廠方法模式察迟,還是抽象工廠模式,他們都屬于工廠模式,在形式和特點上也是極為相似的继薛,他們的最終目的都是為了解耦诈皿。在使用時缕题,我們不必去在意這個模式到底工廠方法模式還是抽象工廠模式,因為他們之間的演變常常是令人琢磨不透的。經(jīng)常你會發(fā)現(xiàn),明明使用的工廠方法模式,當(dāng)新需求來臨,稍加修改寡润,加入了一個新方法后,由于類中的產(chǎn)品構(gòu)成了不同等級結(jié)構(gòu)中的產(chǎn)品族,它就變成抽象工廠模式了;而對于抽象工廠模式僚焦,當(dāng)減少一個方法使的提供的產(chǎn)品不再構(gòu)成產(chǎn)品族之后名扛,它就演變成了工廠方法模式旺订。
所以,在使用工廠模式時本涕,只需要關(guān)心降低耦合度的目的是否達(dá)到了晦闰。