抽象工厂模式类图
???? 由于女媧之前的準備工作花費了非常大的精力,比如準備黃土,準備八卦爐等,從頭開始建立所有的事物也是不可能的,那就想在現有的條件下重新造人,盡可能舊物利用嘛。先說人種(Product產品類)應該怎么改造呢?怎么才能讓人類有愛有恨呢?是神仙當然辦法的了,定義互斥的性別,然后在每個個體中埋下一顆種子:異性相吸,成熟后就一定會去找個異性(這就是我們說的愛情原動力)。從設計角度來看,一個具體的對象通過兩個坐標就可以確定:膚色和性別,如圖9-1所示。
圖9-1 膚色性別坐標圖
???? 產品類分析完畢了,生產的工廠類該(八卦爐)怎么改造呢?只有一個生產設備,要么生產出全都是男性,要么都是女性,那不行呀,這么大的翻天覆地的改造就是為了產生不同性別的人類。有辦法了!把目前已經有的生產設備——八卦爐拆開,于是女媧就使用了“八卦拷貝術”,把原先的八卦爐一個變兩個,并且略加修改,就成了女性八卦爐(只生產女性人種)和男性八卦爐(只生產男性人種),于是乎女媧就開始準備生產了,其類圖如圖9-2所示。
圖9-2 女媧重新生產人類
???? 這個類圖雖然大,但是比較簡單,Java的典型類圖,一個接口,多個抽象類,然后是N個實現類,每個人種都是一個抽象類,性別是在各個實現類中實現的。特別需要說明的是HumanFactory接口,在這個接口中定義了三個方法,分別用來生產三個不同膚色的人種,也就是我們在圖9-1中的Y坐標,它的兩個實現類分別是性別,也就是圖9-1中的X坐標,通過X坐標(性別)和Y坐標(膚色)唯一確定了一個生產出來的對象:什么性別的人種。我們來看相關的實現,Human接口如代碼清單9-1所示。
代碼清單9-1 人種接口
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public interface Human { //每個人種都有相應的顏色 public void getColor(); //人類會說話 public void talk(); //每個人都有性別 public void getSex(); } |
???? 人種有三個抽象類,負責人種的抽象屬性定義:膚色和語言,白色人種、黑色人種、黃色人種分別如代碼清單9-2、9-3、9-4所示。
代碼清單9-2 白色人種
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public abstract class AbstractWhiteHuman implements Human { //白色人種的顏色是白色的 public void getColor(){ System.out.println("白色人種的皮膚顏色是白色的!"); } //白色人種講話 public void talk() { System.out.println("白色人種會說話,一般都是但是單字節。"); } } |
代碼清單9-3 黑色人種
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public abstract class AbstractBlackHuman implements Human { public void getColor(){ System.out.println("黑色人種的皮膚顏色是黑色的!"); } public void talk() { System.out.println("黑人會說話,一般人聽不懂。"); } } |
代碼清單9-4 黃色人種
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public abstract class AbstractYellowHuman implements Human { public void getColor(){ System.out.println("黃色人種的皮膚顏色是黃色的!"); } public void talk() { System.out.println("黃色人種會說話,一般說的都是雙字節。"); } } |
???? 每個抽象類都有兩個實現類,分別實現公共的最細節、最具體的事物:膚色和語言。具體的實現類膚色性別定義,以黃色女性人種為例,如代碼清單9-5所示。
代碼清單9-5 黃色女性人種
?| 1 2 3 4 5 6 7 8 9 10 11 | public class FemaleYellowHuman extends AbstractYellowHuman { //黃人女性 public void getSex() { System.out.println("黃人女性"); } } |
???? 黃色男性人種如代碼清單9-6所示。
代碼清單9-6 黃色男性人種
?| 1 2 3 4 5 6 7 8 9 10 11 | public class MaleYellowHuman extends AbstractYellowHuman { //黃人男性 public void getSex() { System.out.println("黃人男性"); } } |
???? 其他的黑色人種、白色人種的男性和女性的代碼與此類似,不再重復編寫。到此為止,我們已經把真實世界的人種都定義出來了,剩下的工作就是怎么制造人類。接口HumanFactory如代碼清單9-7所示。
代碼清單9-7 八卦爐定義
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | public interface HumanFactory { //制造一個黃色人種 public Human createYellowHuman(); //制造一個白色人種 public Human createWhiteHuman(); //制造一個黑色人種 public Human createBlackHuman(); } |
???? 在接口中,我們看到八卦爐是可以生產出不同膚色人種的(當然了,女媧的失誤嘛),那它有多少個八卦爐呢?兩個,分別生產女性和男性,女性和男性八卦爐分別如代碼清單9-8和9-9所示。
代碼清單9-8 生產女性八卦爐
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public class FemaleFactory implements HumanFactory { //生產出黑人女性 public Human createBlackHuman() { return new FemaleBlackHuman(); } //生產出白人女性 public Human createWhiteHuman() { return new FemaleWhiteHuman(); } //生產出黃人女性 public Human createYellowHuman() { return new FemaleYellowHuman(); } } |
代碼清單9-9 生產男性八卦爐
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public class MaleFactory implements HumanFactory { //生產出黑人男性 public Human createBlackHuman() { return new MaleBlackHuman(); } //生產出白人男性 public Human createWhiteHuman() { return new MaleWhiteHuman(); } //生產出黃人男性 public Human createYellowHuman() { return new MaleYellowHuman(); } } |
???? 人種有了,創建它的八卦爐也有了,我們就來重現一下當年女媧造人的光景,如代碼清單9-10所示。
代碼清單9-10 女媧重造人類
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | public class NvWa { public static void main(String[] args) { //第一條生產線,男性生產線 HumanFactory maleHumanFactory = new MaleFactory(); //第二條生產線,女性生產線 HumanFactory femaleHumanFactory = new FemaleFactory(); //生產線建立完畢,開始生產人了: Human maleYellowHuman = maleHumanFactory.createYellowHuman(); Human femaleYellowHuman = femaleHumanFactory.createYellowHuman(); System.out.println("---生產一個黃色女性---"); femaleYellowHuman.getColor(); femaleYellowHuman.talk(); femaleYellowHuman.getSex(); System.out.println("\n---生產一個黃色男性---"); maleYellowHuman.getColor(); maleYellowHuman.talk(); maleYellowHuman.getSex(); /* * ..... * 后面繼續創建 */ } } |
???? 運行結果如下所示:
---生產一個黃色女性---
黃色人種的皮膚顏色是黃色的!
黃色人種會說話,一般說的都是雙字節。
黃人女性
---生產一個黃色男性---
黃色人種的皮膚顏色是黃色的!
黃色人種會說話,一般說的都是雙字節。
黃人男性
???? 各種膚色的男性、女性都制造出來了,兩性之間產生了相互吸引力,于是情感產生,這個世界就多了一種小說的題材“愛情”。我們回頭來想想我們的設計,不知道大家有沒有去過工廠,每個工廠分很多車間,每個車間又分多個生產線,分別生產不同的產品,我們可以把八卦爐比喻為車間,把八卦爐生產的工藝(生產白人、黑人還是黃人)稱之為生產線,如此來看就是一個女性生產車間,專門生產各種膚色的女性,一個是男性生產車間,專門生產各種膚色男性,生產完畢就可以在系統外組裝,什么是組裝?嘿嘿,自己思考!在這樣的設計下,各個車間和各條生產線的職責非常明確,在車間內各個生產出來的產品可以有耦合關系,你要知道世界上黑、黃、白人種的比例是:1:4:6,那這就需要女媧娘娘在燒制的就要做好比例,在一個車間內協調好。這就是抽象工廠模式。
9.2 抽象工廠模式的定義
???? 抽象工廠模式(Abstract Factory Pattern)是一種比較常用的模式,其定義如下:
Provide an interface for creating families of related or dependent objects without specifying their concrete classes。 為創建一組相關或相互依賴的對象提供一個接口,而且無需指定它們的具體類。
???? 抽象工廠模式的通用類圖如圖9-3所示。
圖9-3 抽象工廠模式通用類圖
???? 抽象工廠模式是工廠方法模式的升級版本,在有多個業務品種、業務分類時,通過抽象工廠模式產生需要的對象是一種非常好的解決方式。我們來看抽象工廠的通用源代碼,首先有兩個互相影響的產品線(也叫做產品族),例如制造汽車的左側門和右側門,這兩個應該是數量相等的——兩個對象之間的約束,每個型號的車門都是不一樣的,這是產品等級結構約束的,我們先看兩個產品族的類圖,如圖9-4所示。
圖9-4 抽象工廠模式通用源碼類圖
???? 注意類圖上的圈圈、框框相對應,兩個抽象的產品類可以有關系,例如共同繼承或實現一個抽象類或接口,其源代碼如代碼清單9-11所示。
代碼清單9-11 抽象產品類
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 | public abstract class AbstractProductA { //每個產品共有的方法 public void shareMethod(){ } //每個產品相同方法,不同實現 public abstract void doSomething(); } |
???? 兩個具體的產品實現類如代碼清單9-12、9-13所示。
代碼清單9-12 產品A1的實現類
?| 1 2 3 4 5 6 7 8 9 | public class ProductA1 extends AbstractProductA { public void doSomething() { System.out.println("產品A1的實現方法"); } } |
代碼清單9-13 產品A2的實現類
?| 1 2 3 4 5 6 7 8 9 | public class ProductA2 extends AbstractProductA { public void doSomething() { System.out.println("產品A2的實現方法"); } } |
???? 產品B與此類似,不再贅述。抽象工廠類AbstractCreator的職責是定義每個工廠要實現的功能,在通用代碼中,抽象工廠類定義了兩個產品族的產品創建,如代碼清單9-14所示。
代碼清單9-14 抽象工廠類
?| 1 2 3 4 5 6 7 8 9 10 11 | public abstract class AbstractCreator { //創建A產品家族 public abstract AbstractProductA createProductA(); //創建B產品家族 public abstract AbstractProductB createProductB(); } |
???? 注意 有N個產品族,在抽象工廠類中就應該有N個創建方法。
???? 如何創建一個產品,則是有具體的實現類來完成的,Creator1和Creator2如代碼清單9-15、9-16所示。
代碼清單9-15 產品等級1的實現類
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public class Creator1 extends AbstractCreator { //只生產產品等級為1的A產品 public AbstractProductA createProductA() { return new ProductA1(); } //只生產產品等級為1的B產品 public AbstractProductB createProductB() { return new ProductB1(); } } |
代碼清單9-16 產品等級2的實現類
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | public class Creator2 extends AbstractCreator { //只生產產品等級為2的A產品 public AbstractProductA createProductA() { return new ProductA2(); } //只生產產品等級為2的B產品 public AbstractProductB createProductB() { return new ProductB2(); } } |
???? 注意 有M個產品等級就應該有M個實現工廠類,在每個實現工廠中,實現不同產品族的生產任務。
???? 具體在業務如何的產生一個與實現無關的對象呢?如代碼清單9-17所示。
代碼清單9-17 場景類
?| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | public class Client { public static void main(String[] args) { //定義出兩個工廠 AbstractCreator creator1 = new Creator1(); AbstractCreator creator2 = new Creator2(); //產生A1對象 AbstractProductA a1 = creator1.createProductA(); //產生A2對象 AbstractProductA a2 = creator2.createProductA(); //產生B1對象 AbstractProductB b1 = creator1.createProductB(); //產生B2對象 AbstractProductB b2 = creator2.createProductB(); /* * 然后在這里就可以為所欲為了... */ } } |
???? You See,在場景類中,沒有任何一個方法與實現類有關系,對于一個產品來說,我們只要知道它的工廠方法,就可以直接產生一個產品對象,無需關心它的實現類。
9.3抽象工廠模式的應用
9.3.1 抽象工廠模式的優點
- 封裝性,每個產品的實現類不是高層模塊要關系的,要關心的是什么?是接口,是抽象,它不關心對象是如何創建出來,這由誰負責呢?工廠類,只要知道工廠類是誰,我就能創建出一個需要的對象,省時省力,優秀設計就應該如此。
- 產品族內的約束為非公開狀態。例如生產男女比例的問題上,猜想女媧娘娘肯定有自己的打算,不能讓女盛男衰,否則女性的優點不就體現不出來了嗎?那在抽象工廠模式,就應該有這樣的一個約束:每生產1個女性,就同時生產出1.2個男性,這樣的生產過程對調用工廠類的高層模塊來說是透明的,它不需要知道這個約束,我就是要一個黃色女性產品就可以了,具體的產品族內的約束是在工廠內實現的。
9.3.2 抽象工廠模式的缺點
???? 抽象工廠模式的最大缺點就是產品族擴展非常困難,為什么這么說呢?我們以通用代碼為例,如果要增加一個產品C,也就是說有產品家族由原來的2個,增加到3個,看看我們的程序有多大改動吧!抽象類AbstractCreator要增加一個方法createProductC(),然后,兩個實現類都要修改,想想看,這在項目中的話,還這么讓人活!嚴重違反了開閉原則,而且我們一直說明抽象類和接口是一個契約,改變契約,所有與契約有關系的代碼都要修改,這段代碼叫什么?叫“有毒代碼”,——只要這段代碼有關系,就可能產生侵害的危險!
9.3.3 抽象工廠模式的使用場景
???? 抽象工廠模式的使用場景定義非常簡單:一個對象族(或是一組沒有任何關系的對象)都有相同的約束,則可以使用抽象工廠模式,什么意思呢?例如一個文本編輯器和一個圖片處理器,都是軟件實體,但是*nix下的文本編輯器和WINDOWS下的文本編輯器雖然功能和界面都相同,但是代碼實現是不同的,圖片處理器也是類似情況,也就是具有了共同的約束條件:操作系統類型,于是我們可以使用抽象工廠模式,產生不同操作系統下的編輯器和圖片處理器。
9.3.3.4 抽象工廠模式的注意實現
???? 在抽象工廠模式的缺點中,我們提到抽象工廠模式的產品族擴展比較困難,但是一定要清楚是產品族擴展困難,而不是產品等級,在該模式下,產品等級是非常容易擴展的,增加一個產品等級,只要增加一個工廠類負責新增加出來的產品生產任務即可,也就是說橫向擴展容易,縱向擴展困難。以人類為例子, 產品等級中只要男、女兩個性別,現實世界還有一種性別:雙性人,即使男人也是女人(俗語就是陰陽人),那我們要擴展這個產品等級也是非常容易的,增加三個產品類,分別對應不同的膚色,然后再創建一個工廠類,專門負責不同膚色人的雙性人的創建任務,完全通過擴展來實現的需求的變更,從這一點上看,抽象工廠模式是符合開閉原則的。
9.4最佳實踐
???? 一個模式在什么情況下才能夠使用,是很多讀者比較困惑的地方,抽象工廠模式是一個簡單的模式,使用的場景非常多,大家在軟件產品開發過程中,涉及到不同操作系統的時候,都可以考慮使用抽象工廠模式,例如一個應用,需要在三個不同平臺上運行:Windows、Linux、Android(Google發布的智能終端操作系統)上運行,你會怎么設計?分別設計三套不同的應用?非也非也,通過抽象工廠模式屏蔽掉操作系統對應用的影響。三個不同操作系統上的軟件功能、應用邏輯、UI都應該是非常類似,唯一不同的是調用不同的工廠方法,由不同的產品類去處理與操作系統交互的信息。
轉自:http://www.cnblogs.com/cbf4life/archive/2009/12/23/1630612.html
總結
- 上一篇: 浏览器禁止缓存讲解
- 下一篇: Win10 如何配置JDK环境变量