由於女媧之前准備工作花費了非常大的精力,比如准備黃土,八卦爐等,從頭開始建立所有的事務也是不可能的,那就盡可能的舊物重新利用。人種(Product產品類)改造一下使得人類有愛恨情仇,於是定義互斥的性別,然后在每個個體中埋下一顆種子:異性相吸,成熟后就一定回去找個異性。從設計角度來看,一個具體的對象通過兩個坐標就可以確定:膚色和性別。如下圖:
產品分析完了,生產的工廠類(八卦爐)的改造,如果只有一個八卦爐,要么生產出來的都是男性,要么都是女性。為了產生不同性別的人於是就把目前已經有的生產設備八卦爐拆開,把原先的八卦爐一個變成兩個,並略加修改,就成了女性八卦爐(只生產女性人種)和男性八卦爐(只生產男性人種),於是就准備開始生產,類圖如下:
類圖比較簡單,Java典型類圖,一個接口,多個抽象類,然后是N個實現類,每個人種都是一個抽象類,性別是在各個實現類中實現的。特別需要說明的是HumanFactory接口,在這個接口中定義了三個方法,分別用來生產三個不同膚色的人種,它的實現類分別是性別跟膚色,通過性別與膚色可以唯一確定一個生產出來的對象。Human代碼如下:
//Human接口 public interface Human{ //每個人種都有相應的顏色 public void getColor(); //人類會說話 public void talk(); //每個人都有性別 public void getSex(); }
人種有三個抽象類,負責人種的抽象屬性定義:膚色和語言。白色人種、黑色人種、黃色人種代碼分別如下:
//白色人種 public abstract class AbstractWhiteHuman implements Human{ //白色人種的皮膚顏色是白色的 public void getColor(){ System.out.println("白色人種的皮膚顏色是白色的!"); } //白色人種講話 public void talk(){ System.out.println("白色人種會說話,一般說的都是單字節。"); } } //黑色人種 public abstract class AbstractBlackHuman implements Human{ //黑色人種的皮膚顏色是黑色的 public void getColor(){ System.out.println("黑色人種的皮膚顏色是黑色的!"); } //黑色人種講話 public void talk(){ System.out.println("黑色人種會說話,一般人聽不懂。"); } } //黃色人種 public abstract class AbstractYellowHuman implements Human{ //黃色人種的皮膚顏色是黃色的 public void getColor(){ System.out.println("黃色人種的皮膚顏色是黃色的!"); } //黃色人種講話 public void talk(){ System.out.println("黃色人種會說話,一般說的都是雙字節。"); } }
每個抽象類中都有兩個實現類,分別實現公共的最細節、最具體的事物:膚色和語言。具體的實現類實現膚色、性別定義,以黃色女性人種為例。代碼如下:
//黃色女性人種 public class FemalYellowHuman extends AbstractYellowHuman{ //黃人女性 public void getSex(){ System.out.println("黃人女性"); } }
黃人男性人種代碼如下:
//黃人男性 public class MaleYellowHuman extends AbstractYellowHuman{ //黃人男性 public void getSex(){ System.put.println("黃人男性"); } }
其他的黑色人種、白色人種的男性和女性的代碼與此類似,不再重復編寫。剩下的工作就是創造人口。接口HumanFactory代碼如下:
//八卦爐定義 public interface HumanFactory{ //創造一個黃色人種 public Human createYellowHuman(); //創造一個白色人種 public Human createWhiteHuman(); //創造一個黑色人種 public Human createBlackHuman(); }
女性與男性的八卦爐代碼分別如下:
//生產女性的八卦爐 public class FemaleFactory implements HumanFactory{ //生產出黑人女性 public Human createBlackHuman(){ return new FemaleBlackHuman(); } //生產出白人女性 public Human createWhiteHuman(){ return new FemaleWhiteHuman(); } //生產出黃人女性 public Human createYellowHuman(){ return new FemaleYellowHuman(); } } //生產男性的八卦爐 public class MaleFactory implements HumanFactory{ //生產出黑人男性 public Human createBlackHuman(){ return new MaleBlackHuman(); } //生產出白人男性 public Human createWhiteHuman(){ return new MaleWhiteHuman(); } //生產出黃人男性 public Human createYellowHuman(){ return new MaleYellowHuman(); } }
人種有了,八卦爐也有了,重現造人光景,代碼如下:
//女媧重造人類 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("生產一個黃色男性"); maleYellowHuman.getColor(); maleYellowHuman.talk(); maleYellowHuman.getSex(); } }
結果:
運行結果如下所示:
生產一個黃色女性
黃色人種的皮膚顏色是黃色的!
黃色人種會說話, 一般說的都是雙字節。
黃人女性
生產一個黃色男性
黃色人種的皮膚顏色是黃色的!
黃色人種會說話, 一般說的都是雙字節。
黃人男性
這種模式就類似於現實世界中的工廠,每個工廠分很多車間, 每個車間又分多條生產線, 分別生產不同的產品, 我們可以把八卦爐比喻為車間, 把八卦爐生產的工藝(生產白人、 黑人還是黃人) 稱為生產線, 如此來看就是一個女性生產車間, 專門生產各種膚色的女性, 一個是男性生產車間, 專門生產各種膚色男性, 生產完畢就可以在系統外組裝。在這樣的設計下,各個車間和各條生產線的職責非常明確, 在車間內各個生產出來的產品可以有耦合關系, 你要知道世界上黑、 黃、 白人種的比例是: 1∶4∶6, 那這就需要女媧娘娘在燒制的時候就要做好比例分配, 在一個車間內協調好。 這就是抽象工廠模式 。
3.1抽象工廠模式的定義
抽象工廠模式(Abstract Factory Pattern)是一種比較常用的模式,其定義如下:
Provide an interface for creating families of related or dependent objects without specifying their concrete class.(為創建一組相關或相互依賴的對象提供一個接口,並且無需指定它們的具體類)
抽象工廠模式的通用類圖如下:
3.2抽象工廠模式的使用場景
抽象工廠的使用場景定義非常簡單:一個對象族(或是一組沒有任何關系的對象)都有相同的約束,則可以使用抽象工廠模式。例如一個文本編輯器和一個圖片處理器,都是軟件實體,但是*nix下的文本編輯器和Windows下的文本編輯器雖然功能和界面都相同,但是代碼實現是不同的,圖片處理器也有類似的情況。也就是具有了共同的約束條件:操作系統類型。於是我們可以使用抽象工廠模式,產生不同操作系統下的編輯器和圖片處理器。
3.3抽象工廠的優點
封裝性:每個產品的實現類不是高層模塊要關心的,它關心的是接口,是抽象,它不關心對象是任何創建出來的,對象的創建是由工廠類來負責的,只要知道工廠類是誰,就能創建出一個需要uti的的對象,省時省力,優秀設計也應該如此。
產品族內的約束為非公開狀態:例如生產男女比例的問題上,猜想女媧娘娘肯定有自己的打算,不能讓女盛男衰,否則女性的優點就體現不出來了。那在抽象工廠模式,就應該有這樣的一個約束:沒生產1個女性,就同時生產出1.2個男性,這樣的生產過程對調用工廠類的高層模塊來說是透明的,它不需要知道這個約束,我就是要要一個黃色女性產品就可以了,具體的產品族內的約束是在工廠內實現的。
3.4抽象工廠的缺點
抽象工廠模式的最大缺點就是產品族的擴展非常困難,我們以通用代碼為例。如果要增加一個產品C,也就是說產品家族由原來的2個增加到3個,程序的改造如下:
抽象類AbstractCreator要增加一個方法createProduct(),然后兩個實現類都要修改,想想看,這嚴重違反了開閉原則,而且我們一直說明抽象類和接口是一個契約。改變契約,所有契約有關系的代碼都要修改,那么這段代碼叫什么?叫“有毒代碼”,——只要與這段代碼有關系,就可能產生侵害的危險。
3.5抽象工廠代碼實現
抽象工廠是工廠方法模式的升級版本,在有多個業務品種、業務分類時,通過抽象工廠模式產生需要的對象是一種非常好的解決方式。通過抽象工廠的通用源代碼,可以看出首先要有兩個相互影響的產品線(也叫產品族),例如制造汽車的左側門和右側門,這兩個應該是數量相等的——兩個對象之間的約束,每個型號的車門都是不一樣的,這是產品等級結構約束的,如下為兩個產品族的類圖:
注意類圖上的圈圈、框框相對應,兩個抽象產品類可以有關系,例如共同繼承或者實現一個抽象類或接口,其代碼如下:
//抽象產品類 public abstract class AbstractProductA{ //每個產品共有的方法 public void shareMethod(){ } //每個產品相同方法,不同實現 public abstract void doSomething(); }
兩個具體的產品實現類代碼如下:
//產品A1的實現類 public class ProductA1 extends AbstractProductA{ public void doSomething(){ System.out.println("產品A1的實現方法"); } } //產品A2的實現類 public class ProductA2 extends AbstractProductA{ public void doSomething(){ System.out.println("產品A2的實現方法"); } }
產品B與此類似,抽象工廠類AbstractCreator的職責是定義每個工廠要實現的功能,在通用代碼中,抽象工廠類定義了兩個產品族的產品創建。代碼如下:
//抽象工廠類 public abstract class AbstractCreator{ //創建A產品家族 public abstract AbstractProductA createProductA(); //創建產品B家族 public abstract AbstractProductB createProductB(); }
創建一個產品,則有具體的實現類來完成,Creator1和Creator2代碼如下:
//產品等級1的實現 public class Creator1 extends AbstractCreator{ //只生產產品等級為1的A產品 public AbstractProductA createProductA(){ return new ProductA1(); } //只生產產品等級為1的B產品 public AbstractProductB createProductB(){ return new ProductB1(); } } //產品等級2的實現 public class Creator2 extends AbstractCreator{ //只生產產品等級為2的A產品 public AbstractProductA createProductA(){ return new ProductA2(); } //只生產產品等級為2的B產品 public AbstractProductB createProductB(){ return new ProductB2(); } }
場景類的代碼如下:
//場景類 public class Client{ public static void main(String[] args){ //定義兩個工廠 AbstractCreator creator1 = new Creator(); AbstractCreator creator2 = new Creator(); //產生A1對象 AbstractProductA a1 = creator1.createProductA(); //產生A2對象 AbstractProductA a2 = creator2.createProductA(); //產生B1對象 AbstractProductB b1 = creator1.createProductB(); //產生B2對象 AbstractProductA b2 = creator2.createProductB(); //具體操作。。。 } }
在場景類中,沒有任何一個方法與實現類有關系,對於一個產品來說,我們只要知道它的工廠方法就可以直接產生一個產品對象,無需關心它的實現類。
3.6抽象工廠模式的注意事項
在抽象工廠模式的缺點中,我們提到抽象工廠模式的產品族擴展比較困難,但是一定要清楚,是產品族擴展困難,而不是產品等級。在該模式下,產品等級是非常容易擴展的,增加一個產品等級,只要增加一個工廠類負責新增加出來的產品生產任務即可。也就是說橫向擴展容易,縱向擴展困難。以人類為例子,產品等級中只有男、女兩個性別,現實世界還有一種性別:雙性人,既是男人也是女人(俗語就是陰陽人),那我們要擴展這個產品等級也是非常容易的,增加三個產品類,分別對應不同的膚色,然后在創建一個工廠類。專門負責不同膚色人的雙性人的創建任務,完全通過擴展來實現需求的變更,從這一點上看,抽象工廠模式是符合開閉原則的。