2018年8月24日17:20:30
抽象原廠模式定義
借我一隻晨光的鉛筆,明天就還給你。
現在我可以還你晨光的鉛筆了,你早已不在了,明天還是明天。
抽象原廠模式(Abstract Factory),提供一個建立一系列相關或相互依賴對象的介面,而無需指定它們具體的類。——《設計模式:可複用物件導向軟體的基礎》
使用情境
首先要來認識兩個新概念:產品族和產品等級結構 。
產品族,是指不同產品等級結構中,功能相關的產品組成的家族。
產品等級結構, 即產品的繼承結構,繼承(或實現)同一抽象產品的具體產品屬於同一產品等級結構。
在後面的程式碼範例中,晨光文具和真彩文具是兩個產品族,繼承(或實現)鉛筆抽象產品的晨光鉛筆和真彩鉛筆是一個產品等級結構,繼承(或實現)橡皮抽象產品的晨光鉛筆和真彩鉛筆是另一個產品等級結構。【這個我用了繼承(或實現)是因為Java中有介面是實現的,抽象類別是繼承的,抽象產品既可以是介面,也可以是抽象類別,為了不誤導我自己,特意加上。】例子還有:Intel的主板、晶片集、CPU是一個產品族,AMD的主板、晶片集、CPU也是一個產品族,而這裡有三個產品等級結構,分別是:主板、晶片集、CPU。
想程式碼範例的時候,想我們經常用的東西,就想到了文具,想到了晨光和真彩。查了一下,沒想到小小文具,營業額超十億。
現在我們的目標是先賺他一個億,開一家部落格園文具(產品族),產品等級結構呢,他們都有,我們直接繼承(或實現)他們的抽象產品類就好了,多方便。這就是抽象原廠模式的好處。增加一個產品族,不用破壞原來代碼結構,符合開閉原則。
但是只有老產品,沒有什麼競爭力,要不我們增加一個新產品即新的產品等級結構,暫時就是碼農作弊專用智能筆,幫你輕鬆應對筆試,這時我們就要在部落格園文具(產品族)添加新成員,需要在部落格園文具具體工廠類中添加相關建立新產品的代碼,不符合開閉原則。這就是抽象原廠模式的缺點了。
綜上所述,結合其他情況,以下情況可以使用抽象原廠模式:
1、一個系統不應當依賴於產品執行個體如何被建立、組合和表達的細節,這對於所有類型的原廠模式都是重要的。
2、系統有多於一個的產品,且每次只使用其中一個產品族。
3、屬於同一產品族的產品將在一起使用,這一約束應該在系統設計中體現出來。
4、所有產品以自己繼承(或實現)的介面出現,從而用戶端不依賴於具體實現。
第1點,毋庸置疑。第2點和第3點,說的是我選擇晨光文具,我使用的就是晨光的鉛筆就要使用晨光的橡皮,要配套使用,不能是我使用了晨光的鉛筆卻使用真彩的橡皮。這個約束在這個例子看起來有點牽強,但是想一下這樣的情境:期末考試要同一文具,有兩種選擇,一種是晨光,一種是真彩,他們是配套的,晨光的鉛筆配晨光的橡皮,真彩的鉛筆配真彩的橡皮,這樣就舒服了。第4點,面向介面編程,降低耦合。
第2點和第3點,用上面提到的Intel、AMD產品族更貼切,Intel的CPU是無法和AMD的主板匹配的,AMD的CPU也無法和Intel的主板匹配,所以組裝電腦的時候,就預設有這個約束:只能使用同一個產品族中的產品。
關於抽象原廠模式的起源,是用於建立分屬於不同作業系統的視窗構建。比如:命令按鍵(Button)與文字框(Text)都是視窗構建,在UNIX作業系統的視窗環境和Windows作業系統的視窗環境中,這兩個構建有不同的本地實現,它們的細節有所不同。視窗構建時,只能選擇一個作業系統的Button和Text,如果不是,兩個作業系統都用不了,我要這視窗有何用?
角色
抽象工廠角色(Abstract Factory):具體工廠(產品族)公用介面
具體工廠角色(Concrete Factory):產品族
抽象產品角色(Abstract Product):產品等級結構
具體產品角色(Concrete Product):產品
圖示
程式碼範例
程式碼範例類依賴關係圖:
抽象工廠角色(AbstractFactory.java)
public interface AbstractFactory { public Pencil createPencil(); public Eraser createEraser();}
具體工廠角色(ChenGuangFactory.java、TrueColorFactory.java)
// 晨光工廠public class ChenGuangFactory implements AbstractFactory { @Override public Pencil createPencil() { return new ChenGuangPencil(); } @Override public Eraser createEraser() { return new ChenGuangEraser(); }}// 真彩工廠public class TrueColorFactory implements AbstractFactory{ @Override public Pencil createPencil() { return new TrueColorPencil(); } @Override public Eraser createEraser() { return new TrueColorEraser(); }}
抽象產品角色(Pencil.java、Eraser.java)
// 鉛筆public interface Pencil { public void draw();}// 橡皮public interface Eraser { public void erase();}
具體產品角色(ChenGuangPencil.java、TrueColorPencil.java、ChenGuangEraser.java、TrueColorEraser.java)
// 晨光鉛筆public class ChenGuangPencil implements Pencil { @Override public void draw() { System.out.println("用晨光鉛筆畫圖。"); }}// 真彩鉛筆public class TrueColorPencil implements Pencil { @Override public void draw() { System.out.println("用真彩鉛筆畫圖。"); }}// 晨光橡皮public class ChenGuangEraser implements Eraser { @Override public void erase() { System.out.println("用晨光橡皮擦除晨光鉛筆畫的圖。"); }}// 真彩橡皮public class TrueColorEraser implements Eraser { @Override public void erase() { System.out.println("用真彩橡皮擦除真彩鉛筆畫的圖。"); }}
測試類別(AbstractFactoryTest.java)
public class AbstractFactoryTest { public static void main(String[] args) { // 1、晨光 AbstractFactory factory = new ChenGuangFactory(); Pencil pencil = factory.createPencil(); pencil.draw(); // 用晨光鉛筆畫圖。 Eraser eraser = factory.createEraser(); eraser.erase(); // 用晨光橡皮擦除晨光鉛筆畫的圖。 // 2、真彩 AbstractFactory factoryT = new TrueColorFactory(); Pencil pencilT = factoryT.createPencil(); pencilT.draw(); // 用真彩鉛筆畫圖。 Eraser eraserT = factoryT.createEraser(); eraserT.erase(); // 用真彩橡皮擦除真彩鉛筆畫的圖。 }}
測試結果:
優點
1、擁有簡單原廠模式、Factory 方法模式一樣的優點:用戶端不需要知道對象被建立的細節
2、當產品族中多個對象被設計成一起工作時,它能保證用戶端始終只使用同一個產品族中的產品。
3、增加產品族,符合開閉原則。
缺點
1、增加產品等級結構時,不符合開閉原則。
總結
抽象原廠模式在Factory 方法模式的基礎上,增添了產品族和產品等級結構的概念,以及只使用同一個產品族中的約束,使得抽象原廠模式符合現實生活中的某些情境,成為實用的設計模式,再加上抽象原廠模式實現高內聚低耦合,也為其廣泛應用提供基礎。
完
(給晨光和真彩做廣告,是不是應該收點廣告費???)
參考:
《JAVA與模式》之抽象原廠模式
2018年8月25日18:31:58