Java設計模式之抽象工廠模式


工廠方法模式中講了女媧造人的故事。人是造出來了,可是低頭一看,都是清一色的類型,缺少關愛、仇恨、喜怒哀樂等情緒,人類的生命太平淡了,女媧一想,猛然一拍腦袋,忘記給人類定義性別了,怎么辦?抹掉重來,於是人類經過一次大洗禮,所有的人種都消滅掉了,世界又是空無一物,寂靜又寂寞。

由於女媧之前准備工作花費了非常大的精力,比如准備黃土,八卦爐等,從頭開始建立所有的事務也是不可能的,那就盡可能的舊物重新利用。人種(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抽象工廠模式的注意事項

在抽象工廠模式的缺點中,我們提到抽象工廠模式的產品族擴展比較困難,但是一定要清楚,是產品族擴展困難,而不是產品等級。在該模式下,產品等級是非常容易擴展的,增加一個產品等級,只要增加一個工廠類負責新增加出來的產品生產任務即可。也就是說橫向擴展容易,縱向擴展困難。以人類為例子,產品等級中只有男、女兩個性別,現實世界還有一種性別:雙性人,既是男人也是女人(俗語就是陰陽人),那我們要擴展這個產品等級也是非常容易的,增加三個產品類,分別對應不同的膚色,然后在創建一個工廠類。專門負責不同膚色人的雙性人的創建任務,完全通過擴展來實現需求的變更,從這一點上看,抽象工廠模式是符合開閉原則的。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM