簡說設計模式——建造者模式


一、什么是建造者模式

  我們先說一個生活中的小例子,當我們在外面飯店吃飯時,比如點個水煮肉片,這家店可能會辣一點、那家店可能會咸一點、對面那家可能放青菜、隔壁那家可能放菠菜,每家店做出來的都不一樣,明明都是水煮肉片卻有不同的做法,如果都一樣就不會說這家難吃那家好吃了。那再看快餐店,比如KFC,我們點個至尊蝦堡,所有人不管在哪個城市哪家店,做法、味道都是一樣的,為什么呢,因為它用料、時間、溫度等等都是嚴格規定的,我們只需要下訂單就行了,這就是一個建造者模式。

       建造者模式(Builder),將一個復雜的對象的構建與它的表示分離,使得同樣的構建過程可以創建不同的表示。UML結構圖如下:

       其中,Director為指揮者/導演類,負責安排已有模塊的順序,然后告訴Builder開始建造;Builder是抽象建造者,規范產品的組建,一般由子類實現;ConcreteBuilder是具體建造者,實現抽象類定義的所有方法,並且返回一個組建好的對象;Product是產品類,通常實現了模板方法模式。

    1. Director類

       導演類起到封裝的作用,避免高層模塊深入到建造者內部的實現類。在建造者模式比較龐大時,導演類可以有多個。

1 public class Director {
2     
3     public void Construct(Builder builder) {
4         builder.BuildPartA();
5         builder.BuildPartB();
6     }
7 
8 }

    2. Builder類

       抽象建造者類,確定產品由兩個部件PartA和PartB組成,並聲明一個得到產品建造后結果的方法getResult()。

public abstract class Builder {
    
    public abstract void BuildPartA();        //產品的A部件
    public abstract void BuildPartB();        //產品的B部件
    public abstract Product getResult();    //獲取產品建造后結果

}

    3. ConcreteBuilder類

       具體建造者類,有幾個產品類就有幾個具體的建造者,而且這多個產品類具有相同的接口或抽象類。這里給出一個產品類的樣例,多個產品類同理。

 1 public class ConcreteBuilder1 extends Builder {
 2 
 3     private Product product = new Product();
 4     
 5     //設置產品零件
 6     @Override
 7     public void BuildPartA() {
 8         product.add("部件A");
 9     }
10 
11     @Override
12     public void BuildPartB() {
13         product.add("部件B");
14     }
15 
16     //組建一個產品
17     @Override
18     public Product getResult() {
19         return product;
20     }
21 
22 }

    4. Client客戶端

 1 public class Client {
 2 
 3     public static void main(String[] args) {
 4         
 5         Director director = new Director();
 6         Builder builder1 = new ConcreteBuilder1();
 7         Builder builder2 = new ConcreteBuilder2();
 8         
 9         //指揮者用ConcreteBuilder1的方法來建造產品
10         director.Construct(builder1);
11         Product product1 = builder1.getResult();
12         product1.show();
13         
14         //指揮者用ConcreteBuilder2的方法來建造產品
15         director.Construct(builder2);
16         Product product2 = builder2.getResult();
17         product2.show();
18         
19     }
20     
21 }

       運行結果如下:

二、建造者模式的應用

    1. 何時使用

  • 一個基本部件不會變,而其組合經常變化的時候

    2. 優點

  • 封裝性。是客戶端不必知道產品內部組成的細節。
  • 建造者獨立,易擴展。
  • 便於控制細節風險。可以對建造過程逐步細化,而不對其他模塊產生任何影響。

    3. 缺點

  • 產品必須有共同點,范圍有限制。
  • 如果內部變化復雜,會有很多建造類。

    4. 使用場景

  • 相同的方法,不同的執行順序,產生不同的事件結果時。
  • 需要生成的對象具有復雜的內部結構時。
  • 多個部件或零件,都可以裝配到一個對象中,但產生的結果又不相同時。

    5. 與工廠模式的區別

  • 建造者模式更關注於零件裝配的順序

    6. 應用實例

  • KFC的食品制作流程,原料多少克、加熱幾分鍾等都有嚴格的規定,我們只需點餐即可,無論在哪里點的都是一樣的。
  • 去KFC吃漢堡、薯條、炸雞等,這些單品是不變的,其組合是經常改變的,也就是所謂的“套餐”。
  • Java中的StringBuilder/StringBuffer。

三、建造者模式的實現

       下面我們看一個KFC點套餐的例子,我們點餐可以點一個漢堡和一個冷飲,漢堡可以是雞肉漢堡、蝦堡等等,是裝在盒子中的,冷飲可以是可樂、雪碧等等,是裝在瓶子中的。下面我們來用建造者模式對其進行組合,用戶只需提交訂單即可,UML圖如下:

    1. Item接口

       創建一個表示食物條目和食物包裝的接口。

 1 public interface Item {
 2 
 3     //獲取食物名稱
 4     public String getName();
 5     //獲取包裝
 6     public Packing packing();
 7     //獲取價格
 8     public float getPrice();
 9     
10 }
1 public interface Packing {
2 
3     //獲取包裝類型
4     public String getPack();
5     
6 }

    2. 包裝類

       實現Packing接口的實現類。Wapper為紙盒包裝,Bottle為瓶裝。

1 public class Wrapper implements Packing {
2 
3     @Override
4     public String getPack() {
5         return "紙盒";
6     }
7 
8 }
1 public class Bottle implements Packing {
2 
3     @Override
4     public String getPack() {
5         return "紙杯";
6     }
7 
8 }

    3. 食品類

       創建實現Item接口的抽象類。Burger為漢堡,Drink為飲品。

 1 public abstract class Burger implements Item {
 2 
 3     @Override
 4     public Packing packing() {
 5         return new Wrapper();
 6     }
 7     
 8     @Override
 9     public abstract float getPrice();
10     
11 }
 1 public abstract class Drink implements Item {
 2 
 3     @Override
 4     public Packing packing() {
 5         return new Bottle();
 6     }
 7     
 8     @Override
 9     public abstract float getPrice();
10     
11 }

    4. 具體食品類

       創建擴展了Burger和Drink的具體實現類。這里簡單的就設為Burger1、Burger2、Drink1、Drink2。各寫一個,多余的就不贅述了。

 1 public class Burger1 extends Burger {
 2 
 3     @Override
 4     public String getName() {
 5         return "漢堡1";
 6     }
 7 
 8     @Override
 9     public float getPrice() {
10         return 25.0f;
11     }
12 
13 }
 1 public class Drink1 extends Drink {
 2 
 3     @Override
 4     public String getName() {
 5         return "飲品1";
 6     }
 7 
 8     @Override
 9     public float getPrice() {
10         return 15.0f;
11     }
12 
13 }

    5. Meal類

 1 public class Meal {
 2     
 3     private List<Item> items = new ArrayList<>();
 4     
 5     public void addItem(Item item) {
 6         items.add(item);
 7     }
 8     
 9     //獲取總消費
10     public float getCost() {
11         float cost = 0.0f;
12         
13         for (Item item : items) {
14             cost += item.getPrice();
15         }
16         
17         return cost;
18     }
19     
20     public void showItem() {
21         for (Item item : items) {
22             System.out.print("餐品:" + item.getName());
23             System.out.print(",包裝:" + item.packing().getPack());
24             System.out.println(",價格:¥" + item.getPrice());
25         }
26     }
27 
28 }

    6. 指揮者

 1 public class MealBuilder {
 2 
 3     //訂單1
 4     public Meal order1() {
 5         Meal meal = new Meal();
 6         meal.addItem(new Burger1());
 7         meal.addItem(new Drink1());
 8         
 9         return meal;
10     }
11     
12     //訂單2
13     public Meal order2() {
14         Meal meal = new Meal();
15         meal.addItem(new Burger2());
16         meal.addItem(new Drink2());
17         
18         return meal;
19     }
20     
21 }

    7. Client客戶端

 1 public class Client {
 2 
 3     public static void main(String[] args) {
 4         MealBuilder mealBuilder = new MealBuilder();
 5         
 6         //獲取第一個訂單
 7         Meal order1 = mealBuilder.order1();
 8         System.out.println("------order1------");
 9         order1.showItem();
10         System.out.println("總額:¥" + order1.getCost());
11         
12         //獲取第二個訂單
13         Meal order2 = mealBuilder.order2();
14         System.out.println("------order2------");
15         order2.showItem();
16         System.out.println("總額:¥" + order2.getCost());
17     }
18     
19 }

       運行結果如下:

 

       源碼地址:https://gitee.com/adamjiangwh/GoF


免責聲明!

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



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