1. 程式人生 > >設計模式學習之抽象工廠模式(Abstract Factory)

設計模式學習之抽象工廠模式(Abstract Factory)

轉自:https://blog.csdn.net/u012909091/article/details/38349211

要想正確理解設計模式,首先必須明確它是為了解決什麼問題而提出來的。

——Shulin

抽象工廠設計模式概念:

        針對抽象工廠這個設計模式,我查找了不少資料,感覺只有涉及產品級別和產品族的才是理解了抽象工廠設計模式的精髓,工廠方法模式針對的是一個產品等級結構;而抽象工廠模式針對的是多個產品等級結構。有些觀點認為抽象工廠模式是為了解決客戶端程式碼與工廠類的耦合問題,我認為這種觀點的解決方案只是簡單工廠模式的一個應用,而這種觀點認為的抽象工廠模式是:

工廠模式+簡單工廠模式=抽象工廠模式這是不正確


針對的問題:


       針對多個產品等級結構。相對工廠模式針對的是一個產品等級結構。


例子描述


        我們通過一個簡單的例子來解釋抽象設計模式,同時這個例子貫穿全文

—————————————————————————————————————————————————————————

        組裝電腦,我們在組裝電腦的時候,通常需要選擇一系列的配件,比如CPU

、硬碟、記憶體、主機板、電源、機箱等。為討論使用簡單點,只考慮選擇CPU和主機板的問題。

  事實上,在選擇CPU的時候,面臨一系列的問題,比如品牌、型號、針腳數目、主頻等問題,只有把這些問題都確定下來,才能確定具體的CPU

  同樣,在選擇主機板的時候,也有一系列問題,比如品牌、晶片組、整合晶片、匯流排頻率等問題,也只有這些都確定了,才能確定具體的主機板。

  選擇不同的CPU和主機板,是每個客戶在組裝電腦的時候,向裝機公司提出的要求,也就是我們每個人自己擬定的裝機方案。

  在最終確定這個裝機方案之前,還需要整體考慮各個配件之間的相容性。比如:CPU和主機板,如果使用Intel

CPUAMD的主機板是根本無法組裝的。因為IntelCPU針腳數與AMD主機板提供的CPU插口不相容,就是說如果使用IntelCPU根本就插不到AMD的主機板中,所以裝機方案是整體性的,裡面選擇的各個配件之間是有關聯的。

  對於裝機工程師而言,他只知道組裝一臺電腦,需要相應的配件,但是具體使用什麼樣的配件,還得由客戶說了算。也就是說裝機工程師只是負責組裝,而客戶負責選擇裝配所需要的具體的配件。因此,當裝機工程師為不同的客戶組裝電腦時,只需要根據客戶的裝機方案,去獲取相應的配件,然後組裝即可。

—————————————————————————————————————————————————————————


兩個基本概念:


在學習抽象工廠具體例項之前需要了解2個基本概念:產品族產品等級

        所謂產品族,是指位於不同產品等級結構中,功能相關聯的產品組成的家族。比如AMD的主機板、晶片組、CPU組成一個家族,Intel的主機板、晶片組、CPU組成一個家族。

       而這兩個家族都來自於三個產品等級:主機板、晶片組、CPU。一個等級結構是由相同的結構的產品組成,示意圖如下:


針對上述的例子進行程式設計,採用工廠模式和抽象工廠模式有什麼區別?


工廠模式:

上面所給出的三個不同的等級結構具有平行的結構。因此,如果採用工廠方法模式,就勢必要使用三個獨立的工廠等級結構來對付這三個產品等級結構。由於這三個產品等級結構的相似性,會導致三個平行的工廠等級結構。隨著產品等級結構的數目的增加,工廠方法模式所給出的工廠等級結構的數目也會隨之增加。如下圖:



抽象工廠模式:




        通過上面兩幅圖,可以看出,一個工廠等級結構可以創建出分屬於不同產品等級結構的一個產品族中的所有物件。顯然,這時候抽象工廠模式比簡單工廠模式、工廠方法模式更有效率。對應於每一個產品族都有一個具體工廠。而每一個具體工廠負責建立屬於同一個產品族,但是分屬於不同等級結構的產品。


用抽象工廠模式設計類圖如下:




原始碼如下:


抽象元件Cpu

public interface Cpu {
	public void calculate();
}


具體元件IntelCpu和AmdCpu

public class IntelCpu implements Cpu {
	private int pins;	//表示CPU的腳針數
	public IntelCpu(int pins){
		this.pins = pins;
	}
	@Override
	public void calculate() {
		System.out.println("Intel CPU的腳針數目:"+pins);
	}
 
}

public class AmdCpu implements Cpu {
	private int pins;
	public AmdCpu(int pins){
		this.pins = pins;
	}
	@Override
	public void calculate() {
		System.out.println("CMD CPU的腳針數目:"+pins);
	}
}


抽象元件MainBoard

public interface MainBoard {
	public void installCpu();
}


具體元件IntelMainBoard和AmdMainBoard

public class IntelMainBoard implements MainBoard {
	private int cpuHoles;	//表示主機板上的CPU插槽孔數,對應腳針數
	public IntelMainBoard(int cpuHoles){
		this.cpuHoles = cpuHoles;
	}
	@Override
	public void installCpu() {
		System.out.println("Intel主機板上的CPU插槽孔數:"+cpuHoles);
	}
 
}
public class AmdMainBoard implements MainBoard {
	private int cpuHoles;
	public AmdMainBoard(int cpuHoles){
		this.cpuHoles = cpuHoles;
	}
	@Override
	public void installCpu() {
		System.out.println("AMD主機板上的CPU插槽孔數:"+cpuHoles);
	}
 
}


設定抽象工廠AbstractFactory,不負責製造具體的產品:

public interface AbstractFactory {
	/**
	 * 建立Cpu物件
	 * @return Cpu物件
	 */
	public Cpu createCpu();
	/**
	 * 建立MainBoard物件
	 * @return MainBoard物件
	 */
	public MainBoard creatMainBoard();
}


設計具體工廠,對應生成Intel和Amd品牌的CPU和主機板

/**
 * 用來建立與Intel相關的所有產品
 */
public class IntelFactory implements AbstractFactory {
	@Override
	public Cpu createCpu() {
		// TODO Auto-generated method stub
		return new IntelCpu(555);
	}
	@Override
	public MainBoard creatMainBoard() {
		// TODO Auto-generated method stub
		return new IntelMainBoard(555);
	}
 
}

/**
 * 用來建立AMD下系列產品
 */
public class AmdFactory implements AbstractFactory {
 
	@Override
	public Cpu createCpu() {
		return new AmdCpu(8888);
	}
	@Override
	public MainBoard creatMainBoard() {
		return new AmdMainBoard(8888);
	}
 
}

我們的裝機工程師只需要進行裝機操作,但是具體裝哪一個系列的產品,由使用者來決定:

ComputerEngineer

public class ComputerEngineer {
	private Cpu cpu = null;
	private MainBoard mainBoard = null;
	
	public void makeComputer(AbstractFactory af){
		//1.準備裝機的硬體
		this.prepareHardWares(af);
		//2.組裝機器
		//3.測試機器
		//4.交付客戶
	}
	public void prepareHardWares(AbstractFactory af){
		this.cpu = af.createCpu();				//準備CPU,但是不關心是什麼CPU
		this.mainBoard = af.creatMainBoard();	//準備主機板,也不關心是什麼主機板
		//檢視槽孔是否和腳針數目匹配
		this.cpu.calculate();
		this.mainBoard.installCpu();
	}
}


客戶端Client

public class Client {
 
	public static void main(String[] args) {
		ComputerEngineer cEngineer = new ComputerEngineer();
		AbstractFactory intelFactory = new IntelFactory();	//客戶選定某類具體工廠,工廠負責成產匹配的元件
		cEngineer.makeComputer(intelFactory);				//工程師開始組裝
	}
 
}


        抽象工廠的功能是為一系列相關物件或相互依賴的物件建立一個介面。一定要注意,這個介面內的方法不是任意堆砌的,而是一系列相關或相互依賴的方法。比如上面例子中的主機板和CPU,都是為了組裝一臺電腦的相關物件。不同的裝機方案,代表一種具體的電腦系列。





          由於抽象工廠定義的一系列物件通常是相關或相互依賴的,這些產品物件就構成了一個產品族,也就是抽象工廠定義了一個產品族。

  這就帶來非常大的靈活性,切換產品族的時候,只要提供不同的抽象工廠實現就可以了,也就是說現在是以一個產品族作為一個整體被切換。


使用情形:

1.一個系統不應當依賴於產品類例項如何被建立、組合和表達的細節,這對於所有形態的工廠模式都是重要的。

2.這個系統的產品有多於一個的產品族,而系統只消費其中某一族的產品。

3.同屬於同一個產品族的產品是在一起使用的,這一約束必須在系統的設計中體現出來。(比如:Intel主機板必須使用Intel CPUIntel      晶片組)

4.系統提供一個產品類的庫,所有的產品以同樣的接口出現,從而使客戶端不依賴於實現。


優點:

1、 分離介面和實現

  客戶端使用抽象工廠來建立需要的物件,而客戶端根本就不知道具體的實現是誰,客戶端只是面向產品的介面程式設計而已。也就是說,客戶端從具體的產品實現中解耦。

 

2、 使切換和新增產品族變得容易

  因為一個具體的工廠實現代表的是一個產品族,比如上面例子的從Intel系列到AMD系列只需要切換一下具體工廠。我也可以再新增一個新的裝機方案,一個新的產品族,都是很方便的。


不足:

不容易擴充套件新的產品等級,比如我要加一個硬碟、記憶體什麼的。那麼就需要修改抽象工廠,這樣就會導致修改所有的工廠實現類。