• 简说设计模式——建造者模式


    一、什么是建造者模式

      我们先说一个生活中的小例子,当我们在外面饭店吃饭时,比如点个水煮肉片,这家店可能会辣一点、那家店可能会咸一点、对面那家可能放青菜、隔壁那家可能放菠菜,每家店做出来的都不一样,明明都是水煮肉片却有不同的做法,如果都一样就不会说这家难吃那家好吃了。那再看快餐店,比如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

  • 相关阅读:
    [Memcached]操作
    [Linux-CentOS7]安装Telnet
    PAT Advanced 1093 Count PAT's (25分)
    PAT Advanced 1065 A+B and C (64bit) (20分)
    PAT Advanced 1009 Product of Polynomials (25分)
    PAT Advanced 1008 Elevator (20分)
    PAT Advanced 1006 Sign In and Sign Out (25分)
    PAT Advanced 1002 A+B for Polynomials (25分)
    半年分布式处理回顾&机器学习(一)——线性回归
    PAT Advanced 1147 Heaps (30分)
  • 原文地址:https://www.cnblogs.com/adamjwh/p/9033551.html
Copyright © 2020-2023  润新知