Builder模式

有时候我们构造一个对象的时候,直接所需的参数未必都已经准备好,需要分步骤收集,或者需要在现有的参数上再做判断和进一步处理,又或者需要用户临时去决定。简而言之,我们只有间接数据,没有直接数据,因此不能简单地构造对象。如果将这些步骤逻辑或处理逻辑加入到构造函数,会使类构造函数变得复杂,类职责也不够单一。

 

Builder模式的意图是,将复杂的构建逻辑从目标类的构造函数中移除。

 

Builder模式最常用于解析配置文件以产生一个与配置文件对应的配置对象的情形。一开始,我们只有一个XML文件,参数都是未准备好的,需要在解析过程中逐步产生并且记录下来。引入Builder模式,解析过程中产生的数据可以先写到中间对象Builder中去,完全准备好后,再调用Builder对象的build方法,返回配置对象。build方法还可以有一些校验和处理的逻辑,确保返回的目标对象合法。这样,与具体业务相关的构建逻辑就完全集中在Builder和Parser中去,配置类的构造函数只有基本的赋值逻辑。

 

Builder模式基本工作过程如下

1 生成并保存直接数据到Builder对象中;

2 处理和校验直接数据;

3 将直接数据传入目标类简单的构造函数,生成目标对象。

 

1,2,3步骤都可以直接写到Builder.build方法里面,也可以分开,视乎实际情况,总之,我们的目标是将复杂的构建逻辑从目标类的构造函数中移除,具体实现方式可以很灵活。Builder模式的核心思想是单一职责,类构建逻辑与类核心功能应该分开。

  

例子1

 

例子2 

mybatis的session工厂类org.apache.ibatis.session.SqlSessionFactory与session工厂构造类org.apache.ibatis.session.SqlSessionFactoryBuilder

 

对于DefaultSqlSessionFactory,我们是无法直接准备好Configuration的,需要对配置文件进行解析才能得到Configuration。我们可以将解析逻辑直接写到DefaultSqlSessionFactory,那样的话,DefaultSqlSessionFactory会多出许多个构造函数,而且引入了与SqlSessionFactory本身毫无关系的XMLConfigBuilder。这样的方式,违反了单一职责,可读性也降低了。读者看一个类,通常只会关注这个类能干什么,而不是怎么创建出来的,类构建逻辑与核心功能逻辑写在一起,集中了太多的代码,会分散读者的注意力。复杂而且与类作用无重大关系的构建逻辑应该单独分开,让类的职责更加简明。

 

为了将构建逻辑从目标类中移除出去,mybatis使用了Builder模式。SqlSessionFactoryBuilder类的build方法接收Reader,Envrioment,Properties等参数,解析出Configuration对象,并最终调用DefaultSqlSessionFactory的构造函数返回一个SqlSessionFactory对象。我们可以看到,DefaultSqlSessionFactory类只有自身核心功能的代码,干净,易读。 

 

 

 

package org.apache.ibatis.session.defaults;

import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.logging.jdbc.ConnectionLogger;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.session.*;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.transaction.managed.ManagedTransactionFactory;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;

public class DefaultSqlSessionFactory implements SqlSessionFactory {

  private static final Log log = LogFactory.getLog(Connection.class);

  private final Configuration configuration;
  private final TransactionFactory managedTransactionFactory;

  public DefaultSqlSessionFactory(Configuration configuration) {
    this.configuration = configuration;
    this.managedTransactionFactory = new ManagedTransactionFactory();
  }

  public SqlSession openSession() {
  }

  public SqlSession openSession(boolean autoCommit) {
  }

  public SqlSession openSession(ExecutorType execType) {
  }

  public SqlSession openSession(TransactionIsolationLevel level) {
  }

  public SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level) {
  }

  public SqlSession openSession(ExecutorType execType, boolean autoCommit) {
  }

  public SqlSession openSession(Connection connection) {
  }

  public SqlSession openSession(ExecutorType execType, Connection connection) {
  }

  public Configuration getConfiguration() {
  }

  private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {

  }

  private SqlSession openSessionFromConnection(ExecutorType execType, Connection connection) {

  }

  private DataSource getDataSourceFromEnvironment(Environment environment) {

  }

  private TransactionFactory getTransactionFactoryFromEnvironment(Environment environment) {
   
  }

}

 

package org.apache.ibatis.session;

import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.exceptions.ExceptionFactory;
import org.apache.ibatis.session.defaults.DefaultSqlSessionFactory;
import org.apache.ibatis.executor.ErrorContext;

import java.io.IOException;
import java.io.Reader;
import java.util.Properties;

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 props) {
    try {
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, props);
      Configuration config = parser.parse();
      return build(config);
    } 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(Configuration config) {
    return new DefaultSqlSessionFactory(config);
  }

}

 

 

利与弊

 

 也许有人觉得这太折腾了,一个类变两个类。设计模式就是这样东西,你完全可以认为它没有必要,甚至有时候觉得它把问题复杂化。设计模式与其说是一种规范一种标准,不如说是一种思考和抽象的哲学。有人觉得类职责足够小是好的,但也有人觉得一个大而全的类是好的。不过对于使用类的人员,小而简单的类显然更容易接受。从目前软件的状况来看,更多人倾向于“小”。为了让东西变小,通常采取的方式是隐藏或者移除对外无太大用处的信息,这样至少表面上会让外界看起来很简单,虽然实质的内涵还是一样的复杂。

 

用不用设计模式,没有绝对的好与坏,更没有对与错,我们需要做的是取其精华,去其糟粕。builder模式带来了一些缺点

1 类从一个变成两个

2 维护人员需要学习设计模式

 

,但是也带来了足够大的优点:

1 职责分明,创建逻辑与核心功能分开,程序逻辑更清晰,层级更分明

2 对于类使用人员,注意力集中到类的核心功能上,感觉轻松了

3 对于类维护人员,可以用不同的builder类或者方法来实现不同的创建逻辑,灵活可扩展

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值