返回 导航

其他

hangge.com

Java - GoF设计模式详解3(建造者模式)

作者:hangge | 2023-02-28 08:50

三、建造者模式

1,基本介绍

(1)建造者模式(Builder)可以把复杂对象的创建与表示分离,使得同样的创建过程可以创建不同的表示。建造者模式与抽象工厂模式非常类似,但建造者模式是逐步地构造出一个复杂对象,并在最后返回对象的实例。
(2)该模式中包含的角色及其职责如下:
  • Product产品角色):表示被构造的复杂对象。
  • Builder抽象建造者):为创建一个产品对象的各个部件指定抽象接口。
  • ConcreteBuilder具体建造者):实现 Builder 的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,并提供一个检索产品的接口。
  • Director指挥者):构造一个使用 Builder 接口的对象。它主要有两个作用,一是:隔离了客户与对象的生产过程,二是:负责控制产品对象的生产过程

2,使用样例

(1)这里我们以肯德基套餐为例演示建造者模式的使用。首先我们创建一个 SetMeal 套餐类,也就是最终要创建的产品类,里面包含主食和饮料两个部件(属性)。
public class SetMeal {
  private String food;
  private String drink;

  public String getFood() {
    return food;
  }

  public void setFood(String food) {
    this.food = food;
  }

  public String getDrink() {
    return drink;
  }

  public void setDrink(String drink) {
    this.drink = drink;
  }
}

(2)接着创建一个 SetMealBuilder 套餐建造者类(即抽象建造者类),它声明了两个抽象的部件组装方法 buildFood() buildDrink(),同时定义了 SetMeal 产品对象,并提供了工厂方法 getSetMeal() 用于返回 SetMeal 对象。
public abstract class SetMealBuilder {
  protected SetMeal setMeal = new SetMeal();
  
  public abstract void buildFood();
  public abstract void buildDrink();
  
  public SetMeal getSetMeal() {
    return setMeal;
  }
}

(3)然后创建两个具体的建造者:香辣鸡腿堡套餐建造者、奥尔良烤翅套餐。它们是抽象建造者类的子类,实现了在抽象建造者中声明的部件组装方法,为产品设置不同的部件内容。
//香辣鸡腿堡套餐建造者
public class BurgerSetMealBuilder extends SetMealBuilder{
  @Override
  public void buildFood() {
    setMeal.setFood("1个香辣鸡腿堡");
  }
  @Override
  public void buildDrink() {
    setMeal.setDrink("1杯可乐");
  }
}

//奥尔良烤翅套餐
public class ChickenSetMealBuilder extends SetMealBuilder{
  @Override
  public void buildFood() {
    setMeal.setFood("1份奥尔良烤翅");
  }
  @Override
  public void buildDrink() {
    setMeal.setDrink("1杯九珍果汁");
  }
}

(4)接着我们创建一个服务员类(即指挥者),其中定义了一个抽象建造者对象,而具体建造者类型由客户端指定;同时在其 construct() 方法中调用建造者对象的部件组装方法和工厂方法,用于向客户端返回1份包含主食和饮料的完整套餐。
public class Waiter {
  private SetMealBuilder builder;

  public void setBuilder(SetMealBuilder builder) {
    this.builder = builder;
  }

  public SetMeal construct() {
    builder.buildFood();
    builder.buildDrink();
    return builder.getSetMeal();
  }
}

(5)最后我们测试一下,我们只需要的将对应的套餐建造者对象传入指挥者对象中,然后调用指挥者的 construct() 方法即可自动组装并返回相应的套餐。
public class Test {
  public static void main(String[] args) {
    Waiter waiter = new Waiter();

    //第一个套餐
    SetMealBuilder builder1 = new ChickenSetMealBuilder();
    waiter.setBuilder(builder1);
    SetMeal setMeal1 = waiter.construct();
    System.out.println("第一个套餐包含:" + setMeal1.getFood() + "," + setMeal1.getDrink());

    //第二个套餐
    SetMealBuilder builder2 = new BurgerSetMealBuilder();
    waiter.setBuilder(builder2);
    SetMeal setMeal2 = waiter.construct();
    System.out.println("第二个套餐包含:" + setMeal2.getFood() + "," + setMeal2.getDrink());
  }
}

附一:建造者模式在 JDK 中的应用(StringBuilder、StringBuffer)

(1)JDK StringBuilder StringBuffer 类中提供了 append() 追加方法,这是一种链式创建对象的方法,开放构造步骤,最后调用了 toString() 方法就可获得完整的对象,例如:
提示:相对于 StringBuilderStringBufferappend 方法加上了 synchronized 关键字。在多线程场景下,使用 StringBuffer 保证线程安全,其他场景则使用 StringBuilder 获得更好的性能
StringBuilder sb = new StringBuilder()
        .append("1234")
        .append("abcd")
        .append("+-*/");
String s = sb.toString();

(2)以 StringBuilder 为例,它的继承结构如下图所示:
  • Appendable 接口定义了多个 append 抽象方法,即 Appendable 为抽象建造者。
  • AbstractStringBuilder 实现了 Appendable 接口方法,已经是一个具体建造者,但它是一个抽象类不能实例化。
  • StringBuilder 继承了 AbstractStringBuilder,它通过调用父类 AbstractStringBuilderappend 方法的形式重写了 append 方法。因而,StringBuilder 相当于 Appendable 抽象构建者的具体构建者,同时又充当了指挥者(Director)的角色。

附二:MyBatis 中的建造者模式

(1)MyBatisSqlSessionFactoryBuiler 类用到了建造者模式,SqlSessionFactory 是通过 SqlSessionFactoryBuilder 创建的,代码如下。
public class SqlSessionFactoryBuilder {
    ...
    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }
}

(2)但是,Configuration 类的属性非常多,设置起来非常繁杂。为了简便设置,在 SqlSessionFactoryBuilder 类中有个重载的 build 方法便通过 XMLConfigBuilderparse() 方法构建 Configuration 实例。
public class SqlSessionFactoryBuilder {
    
    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        try {
            // 创建建造者 XMLConfigBuilder的 实例
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            // XMLConfigBuilder 的 parse() 创建 Configuration 实例
            return build(parser.parse());
        } catch (Exception e) {
            // 捕获异常
            throw ExceptionFactory.wrapException("Error building SqlSession.", e);
        } finally {
            // 重置错误
            ErrorContext.instance().reset();
            try {
                // 释放资源
                inputStream.close();
            } catch (IOException e) {
                // IO 错误不进行处理
            }
        }
    }
}

(3)XMLConfigBuilder 负责 Configuration 各个组建的创建和装配,整个装配流程如下:
package org.apache.ibatis.builder.xml;
...

public class XMLConfigBuilder extends BaseBuilder {
    // 解析配置累
    private void parseConfiguration(XNode root) {
        try {
          // issue #117 read properties first
          // 1.读取XML的<properties>标签
          propertiesElement(root.evalNode("properties"));
          // 2. 读取XML的<settings>标签
          Properties settings = settingsAsProperties(root.evalNode("settings"));
          // 3. 加载自定义的配置
          loadCustomVfs(settings);
          loadCustomLogImpl(settings);
          // 4. 加载XML的<typeAliases> 标签
          typeAliasesElement(root.evalNode("typeAliases"));
          // 5. 加载XML的 <plugins> 标签
          pluginElement(root.evalNode("plugins"));
          // 6. 加载XML的<objectFactory> 标签
          objectFactoryElement(root.evalNode("objectFactory"));
          // 7. 加载XML的<objectWrapperFactory>标签
          objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
          // 8. 加载XML的<reflectorFactory> 标签
          reflectorFactoryElement(root.evalNode("reflectorFactory"));
          // 9. 更新设置
          settingsElement(settings);
          // read it after objectFactory and objectWrapperFactory issue #631
          // 10. 加载XML的<environments> 标签
          environmentsElement(root.evalNode("environments"));
          // 11. 加载XML的 <databaseIdProvider> 标签
          databaseIdProviderElement(root.evalNode("databaseIdProvider"));
          // 12. 加载XML的 <typeHandlers> 标签
          typeHandlerElement(root.evalNode("typeHandlers"));
          // 13. 加载XML的 <mappers> 标签
          mapperElement(root.evalNode("mappers"));
        } catch (Exception e) {
          throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
        }
     }
}

(4)我们可以看出:
  • XMLConfigBuilder 类负责创建复杂对象 ConfigurationXMLConfigBuilder 可以视为具体建造者角色,Configuration 相当于产品。XMLConfigBuilder 继承自 BaseBuilder,所以 BaseBuilder 相当于是抽象建造者的角色。
  • SqlSessionFactoryBuilder 则是以封装形式根据不同的输⼊参数构建 SqlSessionFactory 实例,又相当于一个简化的建造者模式。
评论

全部评论(0)

回到顶部