設計模式讀書筆記-----抽象工廠模式


        在工廠方法模式中,我們使用一個工廠創建一個產品,也就是說一個具體的工廠對應一個具體的產品。但是有時候我們需要一個工廠能夠提供多個產品對象,而不是單一的對象,這個時候我們就需要使用抽象工廠模式。

        在講解抽象工廠模式之前,我們需要厘清兩個概念:

        產品等級結構。產品的等級結構也就是產品的繼承結構。例如一個為空調的抽象類,它有海爾空調、格力空調、美的空調等一系列的子類,那么這個抽象類空調和他的子類就構成了一個產品等級結構。

        產品族。產品族是在抽象工廠模式中的。在抽象工廠模式中,產品族是指由同一個工廠生產的,位於不同產品等級結構中的一組產品。比如,海爾工廠生產海爾空調。海爾冰箱,那么海爾空調則位於空調產品族中。

        產品等級結構和產品族結構示意圖如下:

        一、基本定義                                                                                                                                                                                                          

        抽象工廠模式提供一個接口,用於創建相關或者依賴對象的家族,而不需要明確指定具體類。

        抽象工廠允許客戶端使用抽象的接口來創建一組相關的產品,而不需要關系實際產出的具體產品是什么。這樣一來,客戶就可以從具體的產品中被解耦。

 

        二、模式結構                                                                                                                                                                                                              

        抽象工廠模式的UML結構圖如下:

        模式結構說明。

        AbstractFactory:抽象工廠。抽象工廠定義了一個接口,所有的具體工廠都必須實現此接口,這個接口包含了一組方法用來生產產品。

        ConcreteFactory:具體工廠。具體工廠是用於生產不同產品族。要創建一個產品,客戶只需要使用其中一個工廠完全不需要實例化任何產品對象。

        AbstractProduct:抽象產品。這是一個產品家族,每一個具體工廠都能夠生產一整組產品。

        Product:具體產品。

 

        三、模式實現                                                                                                                                                                                                               

        依然是披薩店。為了要保證每家加盟店都能夠生產高質量的披薩,防止使用劣質的原料,我們打算建造一家生產原料的工廠,並將原料運送到各家加盟店。但是加盟店都位於不同的區域,比如紐約、芝加哥。紐約使用一組原料,芝加哥使用另一種原料。在這里我們可以這樣理解,這些不同的區域組成了原料家族,每個區域實現了一個完整的原料家族。

        首先創建一個原料工廠。該工廠為抽象工廠,負責創建所有的原料。

        PizzaIngredientFactory.java

 1 public interface PizzaIngredientFactory {
 2     /*
 3      * 在接口中,每個原料都有一個對應的方法創建該原料
 4      */
 5     public Dough createDough();    
 6     
 7     public Sauce createSauce();
 8     
 9     public Cheese createCheese();
10     
11     public Veggies[] createVeggies();
12     
13     public Pepperoni createPepperoni();
14     
15     public Clams createClams();
16 }

        原料工廠創建完成之后,需要創建具體的原料工廠。該具體工廠只需要繼承PizzaIngredientFactory,然后實現里面的方法即可。

        紐約原料工廠:NYPizzaIngredientFactory.java。

 1 public class NYPizzaIngredientFactory implements PizzaIngredientFactory{
 2 
 3     @Override
 4     public Cheese createCheese() {
 5         return new ReggianoCheese();
 6     }
 7 
 8     @Override
 9     public Clams createClams() {
10         return new FreshClams();
11     }
12 
13     @Override
14     public Dough createDough() {
15         return new ThinCrustDough();
16     }
17 
18     @Override
19     public Pepperoni createPepperoni() {
20         return new SlicedPepperoni();
21     }
22 
23     @Override
24     public Sauce createSauce() {
25         return new MarinaraSauce();
26     }
27 
28     @Override
29     public Veggies[] createVeggies() {
30         Veggies veggies[] =  {new Garlic(),new Onion(),new Mushroom(),new RefPepper()};
31         return veggies;
32     }
33 
34 }
    重新返回到披薩。在這個披薩類里面,我們需要使用原料,其他方法保持不變,將prepare()方法聲明為抽象,在這個方法中,我們需要收集披薩所需要的原料。

        Pizza.java

 

 1 public abstract class Pizza {
 2     /*
 3      * 每個披薩都持有一組在准備時會用到的原料
 4      */
 5     String name;
 6     Dough dough;
 7     Sauce sauce;
 8     Veggies veggies[];
 9     Cheese cheese;
10     Pepperoni pepperoni;
11     Clams clams;
12     
13     /*
14      * prepare()方法聲明為抽象方法。在這個方法中,我們需要收集披薩所需要的原料,而這些原料都是來自原料工廠
15      */
16     abstract void prepare();
17     
18     void bake(){
19         System.out.println("Bake for 25 munites at 350");
20     }
21     
22     void cut(){
23         System.out.println("Cutting the pizza into diagonal slices");
24     }
25     
26     void box(){
27         System.out.println("Place pizza in official PizzaStore box");
28     }
29 
30     public String getName() {
31         return name;
32     }
33 
34     public void setName(String name) {
35         this.name = name;
36     }
37 
38 }
 
        

        CheesePizza.java

 1 public class CheesePizza extends Pizza{
 2     PizzaIngredientFactory ingredientFactory;
 3     
 4     /*
 5      * 要制作披薩必須要有制作披薩的原料,而這些原料是從原料工廠運來的
 6      */
 7     public CheesePizza(PizzaIngredientFactory ingredientFactory){
 8         this.ingredientFactory = ingredientFactory;
 9         prepare();
10     }
11     
12     /**
13      * 實現prepare方法
14      * prepare 方法一步一步地創建芝士比薩,每當需要原料時,就跟工廠要
15      */
16     void prepare() {
17         System.out.println("Prepareing " + name);
18         dough = ingredientFactory.createDough();
19         sauce = ingredientFactory.createSauce();
20         cheese = ingredientFactory.createCheese();
21     }
22 
23 }
 

        Pizza的代碼利用相關的工廠生產原料。所生產的原料依賴所使用的工廠,Pizza類根本不關心這些原料,它只需要知道如何制作披薩即可。這里,Pizza和區域原料之間被解耦。

        ClamPizza.java

 1  public class ClamPizza extends Pizza{
 2 
 3     PizzaIngredientFactory ingredientFactory;
 4     
 5     public ClamPizza(PizzaIngredientFactory ingredientFactory){
 6         this.ingredientFactory = ingredientFactory;
 7     }
 8     
 9     @Override
10     void prepare() {
11         System.out.println("Prepare " + name);
12         dough = ingredientFactory.createDough();
13         sauce = ingredientFactory.createSauce();
14         cheese = ingredientFactory.createCheese();
15         clams = ingredientFactory.createClams();      
16     }
17 
18 }

        做完披薩后,需要關注披薩店了。

        在披薩店中,我們依然需要關注原料,當地的披薩店需要和本地的原料工廠關聯起來。

        PizzaStore.java

 

 1 public abstract class PizzaStore {
 2     public Pizza orderPizza(String type){
 3         Pizza pizza;
 4         pizza = createPizza(type);
 5             
 6         pizza.prepare();
 7         pizza.bake();
 8         pizza.cut();
 9         pizza.box();
10         
11         return pizza;
12     }
13         
14     /*
15     * 創建pizza的方法交給子類去實現
16      */
17     abstract Pizza createPizza(String type);
18 }
 
        

        紐約的披薩店:NYPizzaStore.java

 1 public class NYPizzaStore extends PizzaStore{
 2 
 3     @Override
 4     Pizza createPizza(String type) {
 5         Pizza pizza = null;
 6         //使用紐約的原料工廠
 7         PizzaIngredientFactory ingredientFactory = new NYPizzaIngredientFactory();   
 8         if("cheese".equals(type)){
 9             pizza = new CheesePizza(ingredientFactory);
10             pizza.setName("New York Style Cheese Pizza");
11         }
12         else if("veggie".equals(type)){
13             pizza = new VeggiePizza(ingredientFactory);
14             pizza.setName("New York Style Veggie Pizza");
15         }
16         else if("clam".equals(type)){
17             pizza = new ClamPizza(ingredientFactory);
18             pizza.setName("New York Style Clam Pizza");
19         }
20         else if("pepperoni".equals(type)){
21             pizza = new PepperoniPizza(ingredientFactory);
22             pizza.setName("New York Style Pepperoni Pizza");
23         }
24         return pizza;
25     }    
26 }
 
        

        下圖是上面的UML結構圖。

 

        其中PizzaIngredientFactory是抽象的披薩原料工廠接口,它定義了如何生產一個相關產品的家族。這個家族包含了所有制作披薩的原料。

        NYPizzaIngredientFactory和ChicagoPizzaIngredientFactory是兩個具體披薩工廠類,他們負責生產相應的披薩原料。

        NYPizzaStore是抽象工廠的客戶端。

 

        四、模式優缺點                                                                                                                                                                                                         

        優點

           1、  抽象工廠隔離了具體類的生成,是的客戶端不需要知道什么被創建。所有的具體工廠都實現了抽象工廠中定義的公共接口,因此只需要改變具體工廠的實例,就可以在某種程度上改變整個軟件系統的行為。

           2、  當一個產品族中的多個對象被設計成一起工作時,它能夠保證客戶端始終只使用同一個產品族中的對象。

        缺點

           添加新的行為時比較麻煩。如果需要添加一個新產品族對象時,需要更改接口及其下所有子類,這必然會帶來很大的麻煩。

 

        五、模式使用場景                                                                                                                                                                                                 

        1.  一個系統不應當依賴於產品類實例如何被創建、組合和表達的細節,這對於所有類型的工廠模式都是重要的。

        2.系統中有多於一個的產品族,而每次只使用其中某一產品族。

        3. 屬於同一個產品族的產品將在一起使用,這一約束必須在系統的設計中體現出來。

        4. 系統提供一個產品類的庫,所有的產品以同樣的接口出現,從而使客戶端不依賴於具體實現。

 

        六、總結                                                                                                                                                                                                                          

        1、  抽象工廠模式中主要的優點在於具體類的隔離,是的客戶端不需要知道什么被創建了。其缺點在於增加新的等級產品結構比較復雜,需要修改接口及其所有子類。


免責聲明!

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



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