软件设计模式修炼 -- 建造者模式


建造者模式是最复杂的创建型模式,它将客户端与包含多个组成部分的复杂对象的创建过程分离,客户端无须知道复杂对象的内部组成部分与装配部分,只需知道建造者的类型即可。


模式动机

建造者模式用于创建一个包含对个组成部分的复杂对象,可以返回一个完整的产品对象给用户。用户无须知道创建过程和内部细节,只需直接使用创建好的完整对象即可。比如汽车拥有车轮、方向盘、发送机等各种部件,用户几乎不会单独使用某个部件,而是使用一辆完整的汽车。软件开发中也存在类似汽车一样的复杂对象,它们拥有一系列成员属性,而且可能存在一些限制条件。


模式定义

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。建造者模式是一步一步创建一个复杂对象,允许用户通过指定复杂对象的类型和内容构建它们,用户不需要知道内部具体构建细节。


模式结构

在这里插入图片描述

  1. Builder(抽象建造者)

    为创建Product对象的各个部件指定抽象接口,方法buildPartX()用于创建复杂对象的各个部件;另一个方法getResult()用于返回复杂对象。

  2. ConcreteBuilder(具体建造者)

    具体建造者实现Builder接口,实现各部件的构造和装配方法,定义并明确它所创建的复杂对象,也可以提供一个方法返回创建好的复杂产品对象。

  3. Product(产品角色)

    产品角色是被构建的复杂对象,包含多个组成部件,具体建造者创建该产品的内部表示并定义它的装配过程。

  4. Director(指挥者)

    负责安排复杂对象的建造次序,指挥者与抽象建造者之间存在关联关系,可以在其construct()建造方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造。客户端只需与指挥者进行交互,在客户端确定建造者的类型,并实例化具体建造者对象(也可通过配置文件和反射机制),然后通过指挥者类的构造函数或者set方法将该对象传入指挥者类中。


建造者模式实例之KFC套餐

  1. 实例说明

    套餐是一个复杂对象,一般包含主食(如汉堡、鸡肉卷等)和饮料(如果汁、可乐)等组成部分。不同套餐有不同的组成部分,KFC服务员根据顾客需求,一步一步装填这些组成部分,构造一份完整的套餐,然后返回给顾客。

  2. 实例代码及解释

    1. 产品类Meal

      套餐Meal是复杂产品对象,它包括两个成员属性food和drink,其中food表示主食,drink表示饮料,Meal中还包括成员属性的Getter方法和Setter方法。

      public class Meal {
      
          //部件
          private String food;
          private String drink;
      
          public String getDrink() {
              return drink;
          }
      
          public void setDrink(String drink) {
              this.drink = drink;
          }
      
          public String getFood() {
              return food;
          }
      
          public void setFood(String food) {
              this.food = food;
          }
      }
      
    2. 抽象建造者类MealBuilder(套餐建造者类)

      MealBuilder是套餐建造者,它是一个抽象类,声明了抽象的部件组装方法buildFood()和buildDrink(),在MealBuilder中定义Meal类型的对象meal,提供工厂方法getMeal()用于返回meal对象。

      public abstract class MealBuilder {
      
          protected Meal meal = new Meal();
      
          public abstract void buildFood();
      
          public abstract void buildDrink();
      
          public Meal getMeal() {
      
              return meal;
          }
      }
      
    3. 具体建造者类SubMealBuilderA(A套餐建造者类)

      SubMealBuilderA是具体建造者类,用于创建A套餐,它是抽象建造者类的子类,实现了抽象建造者类中声明的部件的组装方法。

      public class SubMealBuilderA extends MealBuilder {
      
          @Override
          public void buildFood() {
      
              meal.setFood("一个鸡腿堡");
          }
      
          @Override
          public void buildDrink() {
              meal.setDrink("一杯可乐");
          }
      }
      
    4. 具体建造者类SubMealBuilderB(B套餐建造者类)

      public class SubMealBuilderB extends MealBuilder {
      
          @Override
          public void buildFood() {
      
              meal.setFood("一个鸡肉卷");
          }
      
          @Override
          public void buildDrink() {
      
              meal.setDrink("一杯果汁");
          }
      }
      
    5. 指挥者类KFCWaiter(服务员类)

      KFCWaiter类是指挥者类,在KFC套餐制作过程中相当于KFC服务员,客户端指定具体建造者类型,在其construct()方法中调用指定建造者对象的部件组装方法和工厂方法。

      public class KFCWaiter {
      
          private MealBuilder mealBuilder;
      
          public void setMealBuilder(MealBuilder mealBuilder) {
              this.mealBuilder = mealBuilder;
          }
      
          public Meal construct() {
              mealBuilder.buildFood();
              mealBuilder.buildDrink();
              return mealBuilder.getMeal();
          }
      }
      
    6. XML操作工具类

      public class XMLUtil {
      
          public static Object getBean() throws Exception {
      
              //创建解析器工厂
              DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance();
              //创建解析器
              DocumentBuilder builder = builderFactory.newDocumentBuilder();
              //得到document
              Document document = builder.parse("config.xml");
              //获取包含品牌名称的文本节点
              NodeList brandNameList = document.getElementsByTagName("className");
              Node classNode = brandNameList.item(0).getFirstChild();
              String className = classNode.getNodeValue().trim();
      
              Class c = Class.forName("com.builderPattern." + className);
              Object o = c.newInstance();
              return o;
          }
      }
      
    7. 配置文件

      <?xml version="1.0" encoding="UTF-8" ?>
      <configuration>
         <className>SubMealBuilderA</className>
      </configuration>
      
    8. 测试类

      public class Test {
      
          public static void main(String[] args) throws Exception {
      
              //动态确定套餐种类
              MealBuilder mealBuilder = (MealBuilder) XMLUtil.getBean();
      
              //服务员是指挥者
              KFCWaiter waiter = new KFCWaiter();
      
              //服务员准备套餐
              waiter.setMealBuilder(mealBuilder);
      
              //客户获得套餐
              Meal meal = waiter.construct();
      
              System.out.println("套餐组成:");
              System.out.println(meal.getDrink());
              System.out.println(meal.getFood());
          }
      }
      
    9. 结果分析

      如果在配置文件将节点中内容设置为 SubMealBuilderA,则输出结果如下:
      在这里插入图片描述
      如果在配置文件将节点中内容设置为 SubMealBuilderB,则输出结果如下:
      在这里插入图片描述
      更换具体建造者无须修改源代码,只修改配置文件即可。如果需要增加新的具体建造者,只需增加一个新的具体建造者类继承抽象建造者类,再实现其中声明的抽象部件组装方法,修改配置文件,即可使用新的具体建造者构造新的类型的套餐,系统具有良好的灵活性和可扩展性,符合开闭原则的要求。


posted @ 2020-03-08 19:53  低吟不作语  阅读(614)  评论(1编辑  收藏  举报