舉個生活中常見的例子——組裝電腦,咱們在組裝電腦的時候,一般須要選擇一系列的配件,好比CPU、硬盤、內存、主板、電源、機箱等。爲討論使用簡單點,只考慮選擇CPU和主板的問題。編程
事實上,在選擇CPU的時候,面臨一系列的問題,好比品牌、型號、針腳數目、主頻等問題,只有把這些問題都肯定下來,才能肯定具體的CPU。設計模式
一樣,在選擇主板的時候,也有一系列問題,好比品牌、芯片組、集成芯片、總線頻率等問題,也只有這些都肯定了,才能肯定具體的主板。ide
選擇不一樣的CPU和主板,是每一個客戶在組裝電腦的時候,向裝機公司提出的要求,也就是咱們每一個人本身擬定的裝機方案。學習
在最終肯定這個裝機方案以前,還須要總體考慮各個配件之間的兼容性。好比:CPU和主板,若是使用Intel的CPU和AMD的主板是根本沒法組裝的。由於Intel的CPU針腳數與AMD主板提供的CPU插口不兼容,就是說若是使用Intel的CPU根本就插不到AMD的主板中,因此裝機方案是總體性的,裏面選擇的各個配件之間是有關聯的。測試
對於裝機工程師而言,他只知道組裝一臺電腦,須要相應的配件,可是具體使用什麼樣的配件,還得由客戶說了算。也就是說裝機工程師只是負責組裝,而客戶負責選擇裝配所須要的具體的配件。所以,當裝機工程師爲不一樣的客戶組裝電腦時,只須要根據客戶的裝機方案,去獲取相應的配件,而後組裝便可。this
考慮客戶的功能,須要選擇本身須要的CPU和主板,而後告訴裝機工程師本身的選擇,接下來就等着裝機工程師組裝電腦了。spa
對裝機工程師而言,只是知道CPU和主板的接口,而不知道具體實現,很明顯能夠用上簡單工廠模式或工廠方法模式。爲了簡單,這裏選用簡單工廠。客戶告訴裝機工程師本身的選擇,而後裝機工程師會經過相應的工廠去獲取相應的實例對象。操作系統
CPU接口與具體實現設計
public interface Cpu {對象
public void calculate();
}
public class IntelCpu implements Cpu {
/**
* CPU的針腳數
*/
private int pins = 0;
publicIntelCpu(int pins){
this.pins = pins;
}
@Override
public void calculate() {
// TODO Auto-generated method stub
System.out.println("Intel CPU的針腳數:" + pins);
}
}
public class AmdCpu implements Cpu {
/**
* CPU的針腳數
*/
private int pins = 0;
publicAmdCpu(int pins){
this.pins = pins;
}
@Override
public void calculate() {
// TODO Auto-generated method stub
System.out.println("AMD CPU的針腳數:" + pins);
}
}
主板接口與具體實現
public interface Mainboard {
public void installCPU();
}
public class IntelMainboard implements Mainboard {
/**
* CPU插槽的孔數
*/
private int cpuHoles = 0;
/**
* 構造方法,傳入CPU插槽的孔數
* @param cpuHoles
*/
public IntelMainboard(int cpuHoles){
this.cpuHoles = cpuHoles;
}
@Override
public void installCPU() {
// TODO Auto-generated method stub
System.out.println("Intel主板的CPU插槽孔數是:" + cpuHoles);
}
}
public class AmdMainboard implements Mainboard {
/**
* CPU插槽的孔數
*/
private int cpuHoles = 0;
/**
* 構造方法,傳入CPU插槽的孔數
* @param cpuHoles
*/
public AmdMainboard(int cpuHoles){
this.cpuHoles = cpuHoles;
}
@Override
public void installCPU() {
// TODO Auto-generated method stub
System.out.println("AMD主板的CPU插槽孔數是:" + cpuHoles);
}
}
CPU與主板工廠類
public class CpuFactory {
public static Cpu createCpu(int type){
Cpu cpu = null;
if(type == 1){
cpu = new IntelCpu(755);
}else if(type == 2){
cpu = new AmdCpu(938);
}
return cpu;
}
}
public class MainboardFactory {
public static Mainboard createMainboard(int type){
Mainboard mainboard = null;
if(type == 1){
mainboard = new IntelMainboard(755);
}else if(type == 2){
mainboard = new AmdMainboard(938);
}
return mainboard;
}
}
裝機工程師類與客戶類運行結果以下:
public class ComputerEngineer {
/**
* 定義組裝機須要的CPU
*/
private Cpu cpu = null;
/**
* 定義組裝機須要的主板
*/
private Mainboard mainboard = null;
public void makeComputer(int cpuType , int mainboard){
/**
* 組裝機器的基本步驟
*/
//1:首先準備好裝機所須要的配件
prepareHardwares(cpuType, mainboard);
//2:組裝機器
//3:測試機器
//4:交付客戶
}
private void prepareHardwares(int cpuType , int mainboard){
//這裏要去準備CPU和主板的具體實現,爲了示例簡單,這裏只准備這兩個
//但是,裝機工程師並不知道如何去建立,怎麼辦呢?
//直接找相應的工廠獲取
this.cpu = CpuFactory.createCpu(cpuType);
this.mainboard = MainboardFactory.createMainboard(mainboard);
//測試配件是否好用
this.cpu.calculate();
this.mainboard.installCPU();
}
}
public class Client {
public static void main(String[]args){
ComputerEngineer cf = new ComputerEngineer();
cf.makeComputer(1,1);
}
}
運行結果以下:
上面的實現,雖然經過簡單工廠方法解決了:對於裝機工程師,只知CPU和主板的接口,而不知道具體實現的問題。但還有一個問題沒有解決,那就是這些CPU對象和主板對象實際上是有關係的,須要相互匹配的。而上面的實現中,並無維護這種關聯關係,CPU和主板是由客戶任意選擇,這是有問題的。好比在客戶端調用makeComputer時,傳入參數爲(1,2),運行結果以下:
觀察上面結果就會看出問題。客戶選擇的是Intel的CPU針腳數爲755,而選擇的主板是AMD,主板上的CPU插孔是938,根本沒法組裝,這就是沒有維護配件之間的關係形成的。該怎麼解決這個問題呢?
每個模式都是針對必定問題的解決方案。抽象工廠模式與工廠方法模式的最大區別就在於,工廠方法模式針對的是一個產品等級結構;而抽象工廠模式則須要面對多個產品等級結構。
在學習抽象工廠具體實例以前,應該明白兩個重要的概念:產品族和產品等級。
所謂產品族,是指位於不一樣產品等級結構中,功能相關聯的產品組成的家族。好比AMD的主板、芯片組、CPU組成一個家族,Intel的主板、芯片組、CPU組成一個家族。而這兩個家族都來自於三個產品等級:主板、芯片組、CPU。一個等級結構是由相同的結構的產品組成,示意圖以下:
顯然,每個產品族中含有產品的數目,與產品等級結構的數目是相等的。產品的等級結構與產品族將產品按照不一樣方向劃分,造成一個二維的座標系。橫軸表示產品的等級結構,縱軸表示產品族,上圖共有兩個產品族,分佈於三個不一樣的產品等級結構中。只要指明一個產品所處的產品族以及它所屬的等級結構,就能夠惟一的肯定這個產品。
上面所給出的三個不一樣的等級結構具備平行的結構。所以,若是採用工廠方法模式,就勢必要使用三個獨立的工廠等級結構來對付這三個產品等級結構。因爲這三個產品等級結構的類似性,會致使三個平行的工廠等級結構。隨着產品等級結構的數目的增長,工廠方法模式所給出的工廠等級結構的數目也會隨之增長。以下圖:
那麼,是否可使用同一個工廠等級結構來對付這些相同或者極爲類似的產品等級結構呢?固然能夠的,並且這就是抽象工廠模式的好處。同一個工廠等級結構負責三個不一樣產品等級結構中的產品對象的建立。
能夠看出,一個工廠等級結構能夠建立出分屬於不一樣產品等級結構的一個產品族中的全部對象。顯然,這時候抽象工廠模式比簡單工廠模式、工廠方法模式更有效率。對應於每個產品族都有一個具體工廠。而每個具體工廠負責建立屬於同一個產品族,可是分屬於不一樣等級結構的產品。
抽象工廠模式是對象的建立模式,它是工廠方法模式的進一步推廣。
假設一個子系統須要一些產品對象,而這些產品又屬於一個以上的產品等級結構。那麼爲了將消費這些產品對象的責任和建立這些產品對象的責任分割開來,能夠引進抽象工廠模式。這樣的話,消費產品的一方不須要直接參與產品的建立工做,而只須要向一個公用的工廠接口請求所須要的產品。
經過使用抽象工廠模式,能夠處理具備相同(或者類似)等級結構中的多個產品族中的產品對象的建立問題。以下圖所示:
因爲這兩個產品族的等級結構相同,所以使用同一個工廠族也能夠處理這兩個產品族的建立問題,這就是抽象工廠模式。
根據產品角色的結構圖,就不難給出工廠角色的結構設計圖。
能夠看出,每個工廠角色都有兩個工廠方法,分別負責建立分屬不一樣產品等級結構的產品對象。
前面示例實現的CPU接口和CPU實現對象,主板接口和主板實現對象,都不須要變化。
前面示例中建立CPU的簡單工廠和建立主板的簡單工廠,都再也不須要。
新加入的抽象工廠類和實現類:
public interface AbstractFactory {
/**
* 建立CPU對象
* @return CPU對象
*/
public Cpu createCpu();
/**
* 建立主板對象
* @return 主板對象
*/
public Mainboard createMainboard();
}
public class IntelFactory implements AbstractFactory {
@Override
public Cpu createCpu() {
// TODO Auto-generated method stub
return new IntelCpu(755);
}
@Override
public Mainboard createMainboard() {
// TODO Auto-generated method stub
return new IntelMainboard(755);
}
}
public class AmdFactory implements AbstractFactory {
@Override
public Cpu createCpu() {
// TODO Auto-generated method stub
return new IntelCpu(938);
}
@Override
public Mainboard createMainboard() {
// TODO Auto-generated method stub
return new IntelMainboard(938);
}
}
裝機工程師類跟前面的實現相比,主要的變化是:從客戶端再也不傳入選擇CPU和主板的參數,而是直接傳入客戶已經選擇好的產品對象。這樣就避免了單獨去選擇CPU和主板所帶來的兼容性問題,客戶要選就是一套,就是一個系列。
public class ComputerEngineer {
/**
* 定義組裝機須要的CPU
*/
private Cpu cpu = null;
/**
* 定義組裝機須要的主板
*/
private Mainboard mainboard = null;
public void makeComputer(AbstractFactory af){
/**
* 組裝機器的基本步驟
*/
//1:首先準備好裝機所須要的配件
prepareHardwares(af);
//2:組裝機器
//3:測試機器
//4:交付客戶
}
private void prepareHardwares(AbstractFactory af){
//這裏要去準備CPU和主板的具體實現,爲了示例簡單,這裏只准備這兩個
//但是,裝機工程師並不知道如何去建立,怎麼辦呢?
//直接找相應的工廠獲取
this.cpu = af.createCpu();
this.mainboard = af.createMainboard();
//測試配件是否好用
this.cpu.calculate();
this.mainboard.installCPU();
}
}
客戶端代碼:
public class Client {
public static void main(String[]args){
//建立裝機工程師對象
ComputerEngineer cf = new ComputerEngineer();
//客戶選擇並建立須要使用的產品對象
AbstractFactory af = new IntelFactory();
//告訴裝機工程師本身選擇的產品,讓裝機工程師組裝電腦
cf.makeComputer(af);
}
}
抽象工廠的功能是爲一系列相關對象或相互依賴的對象建立一個接口。必定要注意,這個接口內的方法不是任意堆砌的,而是一系列相關或相互依賴的方法。好比上面例子中的主板和CPU,都是爲了組裝一臺電腦的相關對象。不一樣的裝機方案,表明一種具體的電腦系列。
因爲抽象工廠定義的一系列對象一般是相關或相互依賴的,這些產品對象就構成了一個產品族,也就是抽象工廠定義了一個產品族。
這就帶來很是大的靈活性,切換產品族的時候,只要提供不一樣的抽象工廠實現就能夠了,也就是說如今是以一個產品族做爲一個總體被切換。
1.一個系統不該當依賴於產品類實例如何被建立、組合和表達的細節,這對於全部形態的工廠模式都是重要的。
2.這個系統的產品有多於一個的產品族,而系統只消費其中某一族的產品。
3.同屬於同一個產品族的產品是在一塊兒使用的,這一約束必須在系統的設計中體現出來。(好比:Intel主板必須使用Intel CPU、Intel芯片組)
4.系統提供一個產品類的庫,全部的產品以一樣的接口出現,從而使客戶端不依賴於實現。
抽象工廠模式的起源或者最先的應用,是用於建立分屬於不一樣操做系統的視窗構建。好比:命令按鍵(Button)與文字框(Text)都是視窗構建,在UNIX操做系統的視窗環境和Windows操做系統的視窗環境中,這兩個構建有不一樣的本地實現,它們的細節有所不一樣。
在每個操做系統中,都有一個視窗構建組成的構建家族。在這裏就是Button和Text組成的產品族。而每個視窗構件都構成本身的等級結構,由一個抽象角色給出抽象的功能描述,而由具體子類給出不一樣操做系統下的具體實現。
能夠發如今上面的產品類圖中,有兩個產品的等級結構,分別是Button等級結構和Text等級結構。同時有兩個產品族,也就是UNIX產品族和Windows產品族。UNIX產品族由UNIX Button和UNIX Text產品構成;而Windows產品族由Windows Button和Windows Text產品構成。
系統對產品對象的建立需求由一個工程的等級結構知足,其中有兩個具體工程角色,即UnixFactory和WindowsFactory。UnixFactory對象負責建立Unix產品族中的產品,而WindowsFactory對象負責建立Windows產品族中的產品。這就是抽象工廠模式的應用,抽象工廠模式的解決方案以下圖:
顯然,一個系統只可以在某一個操做系統的視窗環境下運行,而不能同時在不一樣的操做系統上運行。因此,系統實際上只能消費屬於同一個產品族的產品。
在現代的應用中,抽象工廠模式的使用範圍已經大大擴大了,再也不要求系統只能消費某一個產品族了。所以,能夠沒必要理會前面所提到的原始用意。
分離接口和實現
客戶端使用抽象工廠來建立須要的對象,而客戶端根本就不知道具體的實現是誰,客戶端只是面向產品的接口編程而已。也就是說,客戶端從具體的產品實現中解耦。
使切換產品族變得容易
由於一個具體的工廠實現表明的是一個產品族,好比上面例子的從Intel系列到AMD系列只須要切換一下具體工廠。
不太容易擴展新的產品
若是須要給整個產品族添加一個新的產品,那麼就須要修改抽象工廠,這樣就會致使修改全部的工廠實現類。