定義:
提供一個創建一系列相關或相互依賴對象的接口,而無需指定它們具體的類
結構:
抽象工廠模式中的角色和工廠方法模式的完全相同,由四個部分組成:
-
抽象工廠:工廠方法模式的核心,與應用程序無關。是具體工廠角色必須實現的接口或必須繼承的父類
-
具體工廠:由應用程序調用以創建對應具體的產品的對象
-
抽象產品:產品接口
-
具體產品:具體的產品實現對象,通常在具體工廠裏,會選擇具體的產品實現對象,來創建符合抽象工廠定義的方法返回的產品類型的對象
場景實例:組裝電腦,選擇組裝電腦的配件
客戶選擇不同的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芯片的主板,又組成另一個家族】
-
同屬於同一個產品族的產品一起使用。
-
-
優點:
-
分離接口和實現
-
使得切換產品簇變的容易
-
-
缺點:
-
不太容易擴展新產品。前面提到的擴展工廠方式可以但不夠安全,所以需根據實際情況權衡。
-
容易造成類層次複雜
-