建造者模式

建造者模式

标签 : Java与设计模式


建造者模式: 又称生成器模式, 可以将一个产品的内部表象与产品的生成过程分割开来, 从而可以使一个建造过程生成具有不同的内部表象的产品(将一个复杂对象的构建与它的表示分离, 使得同样的构建过程可以创建不同的表示). 这样用户只需指定需要建造的类型就可以得到具体产品,而不需要了解具体的建造过程和细节.

  • 与抽象工厂的区别:
    在建造者模式中,角色分指导者(Director)建造者(Builder): 用户联系指导者, 指导者指挥建造者, 最后得到产品. 建造者模式可以强制实行一种分步骤进行的建造过程.

实现

需求: 模仿宇宙飞船的建造过程
    假设宇宙飞船有很多零部件: 引擎、轨道舱、逃逸塔、各种小零件... 因此宇宙飞船的建造/装配非常复杂(需要很好的生产/装配技术),而建造者模式可以将部件的建造与装配分开:


产品与部件

产品AirShip由多个零部件(Engine/EscapeTower/OrbitalModule)组成:

/**
 * 目标对象 - 宇宙飞船
 * (代表复杂对象, 拥有复杂的建造过程)
 * Created by jifang on 15/12/8.
 */
public class AirShip {

    private Engine engine;

    private EscapeTower escapeTower;

    private OrbitalModule orbitalModule;

    public Engine getEngine() {
        return engine;
    }

    public void setEngine(Engine engine) {
        this.engine = engine;
    }

    public EscapeTower getEscapeTower() {
        return escapeTower;
    }

    public void setEscapeTower(EscapeTower escapeTower) {
        this.escapeTower = escapeTower;
    }

    public OrbitalModule getOrbitalModule() {
        return orbitalModule;
    }

    public void setOrbitalModule(OrbitalModule orbitalModule) {
        this.orbitalModule = orbitalModule;
    }

    @Override
    public String toString() {
        return "AirShip{" +
                "engine=" + engine +
                ", escapeTower=" + escapeTower +
                ", orbitalModule=" + orbitalModule +
                '}';
    }
}

class Engine {

    private String description;

    public Engine(String description) {
        this.description = description;
    }

    @Override
    public String toString() {
        return "Engine{" +
                "description='" + description + '\'' +
                '}';
    }
}

class EscapeTower {

    private String description;

    public EscapeTower(String description) {
        this.description = description;
    }

    @Override
    public String toString() {
        return "EscapeTower{" +
                "description='" + description + '\'' +
                '}';
    }
}

class OrbitalModule {

    private String description;

    public OrbitalModule(String description) {
        this.description = description;
    }

    @Override
    public String toString() {
        return "OrbitalModule{" +
                "description='" + description + '\'' +
                '}';
    }
}

建造者(Builder)

Builder(AirShipBuilder)是为创建一个Product对象的各个部件指定的抽象接口, ConcreteBuilder(LowerAirShipBuilder/HigherAirShipBuilder)是具体的建造者, 实现Builder接口, 构造和装配各个部件.

/**
 * @author jifang
 * @since 16/8/17 下午2:13.
 */
public interface AirShipBuilder {

    void builtEngine();

    void builtEscapeTower();

    void builtOrbitalModule();

    AirShip getResult();
}

生产低端飞船, 需要LowerAirShipBuilder; 生产高端飞船, 就需要HigherAirShipBuilder:

class LowerAirShipBuilder implements AirShipBuilder {

    private AirShip airShip = new AirShip();

    @Override
    public void builtEngine() {
        System.out.println("\t\t构造低端引擎");
        airShip.setEngine(new Engine("低端 - 引擎"));
    }

    @Override
    public void builtEscapeTower() {
        System.out.println("\t\t构造低端逃逸塔");
        airShip.setEscapeTower(new EscapeTower("低端 - 逃逸塔"));
    }

    @Override
    public void builtOrbitalModule() {
        System.out.println("\t\t构造低端轨道舱");
        airShip.setOrbitalModule(new OrbitalModule("低端 - 轨道舱"));
    }

    @Override
    public AirShip getResult() {
        return airShip;
    }
}

class HigherAirShipBuilder implements AirShipBuilder {

    private AirShip airShip = new AirShip();

    @Override
    public void builtEngine() {
        System.out.println("\t\t构造高端引擎");
        airShip.setEngine(new Engine("高端 - 引擎"));
    }

    @Override
    public void builtEscapeTower() {
        System.out.println("\t\t构造高端逃逸塔");
        airShip.setEscapeTower(new EscapeTower("高端 - 逃逸塔"));
    }

    @Override
    public void builtOrbitalModule() {
        System.out.println("\t\t构造高端轨道舱");
        airShip.setOrbitalModule(new OrbitalModule("高端 - 轨道舱"));
    }

    @Override
    public AirShip getResult() {
        return airShip;
    }
}

指挥者(Director)

使用Director(AirShipDirector)控制建造过程, 也用它来隔离用户与建造过程的关联:

/**
 * @author jifang
 * @since 16/8/17 下午2:15.
 */
public class AirShipDirector {

    /**
     * 确定一种稳定的构造过程
     *
     * @param builder
     */
    public static void construct(AirShipBuilder builder) {

        builder.builtEngine();

        builder.builtEscapeTower();

        builder.builtOrbitalModule();
    }
}

Client

完全不需知道具体的创建/装配过程, 只需指定Builder:

public class Client {

    @Test
    public void client() {
        AirShipBuilder lowBuilder = new LowerAirShipBuilder();
        // 构造低端飞船
        AirShipDirector.construct(lowBuilder);
        AirShip lowShip = lowBuilder.getResult();
        System.out.println(lowShip);

        AirShipBuilder highBuilder = new HigherAirShipBuilder();
        // 相同的构造过程, 不同的Builder, 可以构造出不同的飞船
        AirShipDirector.construct(highBuilder);
        AirShip highShip = highBuilder.getResult();
        System.out.println(highShip);
    }
}

实例-MyBatis中的建造者模式

MyBatis的SqlSessionFactoryBuilder是对SqlSessionFactory建造过程的简单封装,他对建造者模式做了简化处理(只有Builder而无Director),以减小编程复杂度:

/**
 * @author Clinton Begin
 */
public class SqlSessionFactoryBuilder {

  public SqlSessionFactory build(Reader reader) {
    return build(reader, null, null);
  }

  public SqlSessionFactory build(Reader reader, String environment) {
    return build(reader, environment, null);
  }

  public SqlSessionFactory build(Reader reader, Properties properties) {
    return build(reader, null, properties);
  }

  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

  public SqlSessionFactory build(InputStream inputStream) {
    return build(inputStream, null, null);
  }

  public SqlSessionFactory build(InputStream inputStream, String environment) {
    return build(inputStream, environment, null);
  }

  public SqlSessionFactory build(InputStream inputStream, Properties properties) {
    return build(inputStream, null, properties);
  }

  public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        inputStream.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

  public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

}

由于SqlSessionFactory创建的SqlSession需要支持很多操作(如selectOne()selectList()update()等), 因此SqlSessionFactory的构造过程是非常复杂的(可参考SqlSessionManagerDefaultSqlSessionFactory实现),因此使用SqlSessionFactoryBuilder作为Builder简化其构造过程,并且为其设置配置文件(mybatis-configuration.xml)作为Director来指导Builder的构造过程:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="mapperLocations" value="classpath:mybatis/mapper/*DAO.xml"/>
    <property name="typeAliases" value="com.feiqing.domain.User"/>
    <property name="configLocation" value="classpath:mybatis/mybatis-configuration.xml"/>
</bean>

详细可参考博客: mybatis源码分析(1)——SqlSessionFactory实例的产生过程


小结

  • 由于构建装配的解耦, 不同的构建器, 相同的装配过程, 可以产生不同的产品,实现了更好的复用.因此常用于创建一些复杂的对象, 这些对象内部构建间的建造顺序通常是稳定的, 但内部的构建通常面临着复杂的变化. 如:
    • StringBuilderappend();
    • JDBC的PreparedStatement;
    • JDOM的DomBuilderSAXBuilder;
    • MyBatis的SqlSessionFactoryBuilder.

参考:

posted @ 2015-12-20 17:43  挨踢人啊  阅读(158)  评论(0编辑  收藏  举报