[設計模式]-抽象工廠模式ing

定義:

提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類

結構:

抽象工廠模式中的角色和工廠方法模式的完全相同,由四個部分組成:

  • 抽象工廠:工廠方法模式的核心,與應用程序無關。是具體工廠角色必須實現的接口或必須繼承的父類
  • 具體工廠:由應用程序調用以創建對應具體的產品的對象
  • 抽象產品:產品接口
  • 具體產品:具體的產品實現對象,通常在具體工廠裏,會選擇具體的產品實現對象,來創建符合抽象工廠定義的方法返回的產品類型的對象

場景實例:組裝電腦,選擇組裝電腦的配件

客戶選擇不同的CPU和主板,在最終確定裝機方案之前,還需整體考慮各個配件之間的兼容性。比如CPU針腳數和主板提供的CPU插口不兼容,是無法組裝的。也就是說,裝機方案是有整體性的,裏面選擇的各個配件之間是有關聯的

簡單起見,使用簡單工廠實現:

  • 抽象接口
/**
 * CPU接口<產品簇1>
 */
public interface CPUApi {
	//實例方法,CPU運算的功能
	public void calculate();
}
/**
 * 主板接口<產品簇2>
 */
public interface MainboardApi {
	//實例方法,安裝CPU的功能
	public void installCPU();
}
  • 具體產品實現
class IntelCPU implements CPUApi{
	//CPU針腳數
	private int pins=0;
	public IntelCPU(int pins){
		this.pins=pins;
	}

	@Override
	public void calculate() {
		System.out.println("now in Intel CPU ,pins="+pins);
	}
}
class AMDCPU implements CPUApi{
	//CPU針腳數
	private int pins=0;
	public AMDCPU(int pins){
		this.pins=pins;
	}

	@Override
	public void calculate() {
		System.out.println("now in AMD CPU ,pins="+pins);
	}
}
/**
 * Intel主板
 */
class IntelMainboard implements MainboardApi{
	//CPU插槽孔數
	private int cpuHoles = 0;

	public IntelMainboard(int cpuHoles){
		this.cpuHoles=cpuHoles;
	}

	@Override
	public void installCPU() {
		System.out.println("Intel 主板的CPU插槽孔數是:"+cpuHoles);
	}
}
/**
 * AMD主板
 */
class AMDMainboard implements MainboardApi{
	//CPU插槽孔數
	private int cpuHoles = 0;

	public AMDMainboard(int cpuHoles){
		this.cpuHoles=cpuHoles;
	}

	@Override
	public void installCPU() {
		System.out.println("AMD 主板的CPU插槽孔數是:"+cpuHoles);
	}
}
  • 工廠方法
/**
 * CPU工廠
 */
public class CPUFactory {
	public static CPUApi createCPU(int type){
		CPUApi cpu=null;
		if(type==1){
			cpu=new IntelCPU(1156);
		}else{
			cpu=new AMDCPU(939);
		}
		return cpu;
	}
}
/**
 * 主板工廠
 */
class MainboardFactory {
	public static MainboardApi createMainboard(int type){
		MainboardApi mainboard=null;
		if(type==1){
			mainboard=new IntelMainboard(1156);
		}else{
			mainboard=new AMDMainboard(939);
		}
		return mainboard;
	}
}
  • 裝機工程師

/**
 * 裝機工程師,不知道具體實現,只根據客戶要求組裝電腦
 */
class ComputerEngineer{
	private CPUApi cpu=null;
	private MainboardApi mainboard=null;
	//根據方案組裝電腦
	public void makeComputer(int cpuType,int mainboardType){
		this.cpu=CPUFactory.createCPU(cpuType);
		this.mainboard=MainboardFactory.createMainboard(mainboardType);

		cpu.calculate();
		mainboard.installCPU();
	}
}

  • 客戶端

public class Client {
	public static void main(String[] args) {
		//創建工程師對象
		ComputerEngineer engineer=new ComputerEngineer();
		//告訴工程師自己的方案,由工程師組裝
		engineer.makeComputer(1,2);
		//output:	now in Intel CPU ,pins=1156
		//   		AMD 主板的CPU插槽孔數是:28
	}
}

有何問題?

上面的實現雖然通過簡單工廠解決了,但有一個問題沒解決,那就是這些CPU對象和主板對象是有關係的,是需要互相匹配的。而上面的實現並沒有維護這種關聯關係,如客戶端測試傳入1,2參數就會導致無法組裝。

抽象工廠模式來解決問題

  • 抽象工廠

/**
 * 抽象工廠:聲明創建抽象產品對象的操作
 *@Author: April
 *@Date: 2014-7-5
 */
public interface AbstractFactory {
	//創建CPU的對象
	public CPUApi createCPUApi();
	//創建主板的對象
	public MainboardApi createMainboardApi();
}

  • 抽象工廠的實現:即裝機方案

/**
 * 裝機方案一:Intel的CPU與Intel主板
 */
public class Schema1 implements AbstractFactory{

	@Override
	public CPUApi createCPUApi() {
		return new IntelCPU(1156);
	}

	@Override
	public MainboardApi createMainboardApi() {
		return new IntelMainboard(1156);
	}
}
/**
 * 裝機方案二:AMD的CPU與AMD的主板
 */
class Schema2 implements AbstractFactory{

	@Override
	public CPUApi createCPUApi() {
		return new AMDCPU(939);
	}

	@Override
	public MainboardApi createMainboardApi() {
		return new AMDMainboard(939);
	}
}

  • 裝機工程師

/**
 * 裝機工程師,不知道具體實現,只根據客戶要求組裝電腦
 */
class ComputerEngineer{
	private CPUApi cpu=null;
	private MainboardApi mainboard=null;
	//根據方案組裝電腦
	public void makeComputer(AbstractFactory schema){
		//使用抽象工廠獲取相應的接口對象
		this.cpu=schema.createCPUApi();
		this.mainboard=schema.createMainboardApi();
		//測試下..
		cpu.calculate();
		mainboard.installCPU();
	}
}

  • 客戶端
public class Client {
	public static void main(String[] args) {
		//創建工程師對象
		ComputerEngineer engineer=new ComputerEngineer();
		//客戶選擇並創建想要的裝機方案
		AbstractFactory schema=new Schema1();
		engineer.makeComputer(schema);
	}
}

定義可擴展的工廠

相對靈活但不太安全的改進方式:在抽象工廠中不需定義那麼多方法,只定義一個方法,給這個方法設置一個參數,通過這個參數判斷具體創建什麼產品簇對象;這個方法的返回值就不能是具體某個產品類型了,可以是所有產品簇都實現的接口,也可以直接用Object類型

//抽象工廠修改
public interface AbstractFactory {
	public Object createProduct(int type);
}
//裝機方案修改
public class Schema1 implements AbstractFactory{
	@Override
	public Object createProduct(int type){
		Object result=null;
		if(type==1){
			result=new IntelCPU(1156);
		}else if (type==2){
			result=new IntelMainboard(1156);
		}
		return result;
	}
}
//裝機工程師修改
public class ComputerEngineer{
	private CPUApi cpu=null;
	private MainboardApi mainboard=null;
	public void makeComputer(AbstractFactory schema){
		//強制轉換成接口對象,如果不匹配則會報錯,所以不安全
		this.cpu=(CPUApi)schema.createProduct(1);
		this.mainboard=(MainboardApi)schema.createProduct(2);
		cpu.calculate();
		mainboard.installCPU();
	}
}

此時,如果要加入一個新的產品-內存,當然可以提供一個新的裝機方案來使用它,這樣已有的代碼就不需要變化了

理解抽象工廠模式

  • 抽象工廠的本質:選擇產品簇的實現
  • 抽象工廠的功能

爲一系列相關對象或相互依賴的對象創建一個接口。注意:接口內的方法不是任意堆砌的,而是一系列相關或相互依賴的方法,比如上面的CPU與主板,就是爲組裝一臺電腦的相關對象

  • 使用工廠方法

AbstractFactory定義的創建產品的方法可以看成是工廠方法,而這些工廠方法的具體實現就延遲到了具體的工廠裏面。也就是說使用工廠方法來實現抽象工廠

  • 切換產品簇

抽象工廠定義的是一個產品簇,這帶來很大的靈活性,切換產品簇的時候,只需提供不同的抽象工廠實現就可以了,也就是說現在是以產品簇作爲一個整體被切換

  • 何時選用抽象工廠模式?
    • 系統中有多個產品族,而系統一次只可能消費其中一族產品。【產品族概念:位於不同產品等級結構中,功能相關聯的產品組成的家族。比如AMD的CPU和ADM芯片的主板,組成一個家族。Intel的CPU和Intel芯片的主板,又組成另一個家族】
    • 同屬於同一個產品族的產品一起使用。
  • 優點:
    • 分離接口和實現
    • 使得切換產品簇變的容易
  • 缺點:
    • 不太容易擴展新產品。前面提到的擴展工廠方式可以但不夠安全,所以需根據實際情況權衡。
    • 容易造成類層次複雜




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章