你还不懂设计模式? - 模版设计模式

模板模式(Template Pattern)顾名思义,就是定义一个操作流程的模版。然后不同子类去重写指定的模版方法。适用于能够确定操作流程。不能确定具体操作的场景。

在父类中定义处理流程的框架,在子类中实现具体的操作细节。这种代码编写方式就是模版设计模式。模版设计模式的还可以抽象出公共的逻辑。而把一些不同的操作留给子类实现。在Java的各个技术框架中有着广泛的应用。

还是举个简单栗子。比如我每天吃饭的过程。平时吃的简单点,过节的时候吃的好一点。我们可以这样来实现。

① 创建吃饭流程的模版, 先吃早饭,再吃午饭,再吃晚饭。如下:

public abstract class MyAbstractEat {
    /**
     * 吃早饭
     */
    public abstract void handleBreakfast();
    /**
     * 吃中饭
     */
    public abstract void handleLunch();
    /**
     * 吃晚餐
     */
    public abstract void handleDinner();

    public void eat(){
        //这里可以添加公共逻辑
        handleBreakfast();
        //这里可以添加公共逻辑
        handleLunch();
        //这里可以添加公共逻辑
        handleDinner();
        //这里可以添加公共逻辑
    }
}

② 平时吃饭流程,实现一个子类

public class UsuallyEat extends MyAbstractEat {
    @Override
    public void handleBreakfast() {
        System.out.println("早上吃白馒头");
    }

    @Override
    public void handleLunch() {
        System.out.println("中午吃外卖");
    }

    @Override
    public void handleDinner() {
        System.out.println("晚上喝粥");
    }
}

③ 节假日吃饭流程。新建一个子类

public class HolidayEat extends MyAbstractEat {
    @Override
    public void handleBreakfast() {
        System.out.println("早上喝牛奶吃面包");
    }

    @Override
    public void handleLunch() {
        System.out.println("中午去酒店吃满汉全席");
    }

    @Override
    public void handleDinner() {
        System.out.println("晚上去西餐厅吃牛排");
    }
}

测试一下:

	MyAbstractEat usuallyEat = new UsuallyEat();
	MyAbstractEat holidayEat = new HolidayEat();
	System.out.println("工作日吃饭流程:");
	usuallyEat.eat();
	System.out.println("..............................");
	System.out.println("节假日吃饭流程:");
	holidayEat.eat();
	
输出结果:

工作日吃饭流程:
早上吃白馒头
中午吃外卖
晚上喝粥
..............................
节假日吃饭流程:
早上喝牛奶吃面包
中午去酒店吃满汉全席
晚上去西餐厅吃牛排

可以看到,模版设计模式。可以很好的定义流程框架。模版设计模式很简单,但是实际使用还是要充分结合业务场景。毕竟编码都是为了业务来服务的!

模版设计模式在Spring ,mytais等等框架中有广泛使用,下面来看一下在mytais源码中的应用。

在mybatis中执行数据操作的核心接口是Executor,而其核心子类BaseExecutor中就完美的应用了模版设计模式。来看源码:

public abstract class BaseExecutor implements Executor {

  ...
  @Override
  public void close(boolean forceRollback) {
    try {
      try {
        rollback(forceRollback);
      } finally {
        if (transaction != null) {
          transaction.close();
        }
      }
    } catch (SQLException e) {
      // Ignore.  There's nothing that can be done at this point.
      log.warn("Unexpected exception on closing transaction.  Cause: " + e);
    } finally {
      transaction = null;
      deferredLoads = null;
      localCache = null;
      localOutputParameterCache = null;
      closed = true;
    }
  }

  @Override
  public boolean isClosed() {
    return closed;
  }

  @Override
  public int update(MappedStatement ms, Object parameter) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing an update").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    clearLocalCache();
    return doUpdate(ms, parameter);
  }

  @Override
  public List<BatchResult> flushStatements() throws SQLException {
    return flushStatements(false);
  }

  public List<BatchResult> flushStatements(boolean isRollBack) throws SQLException {
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    return doFlushStatements(isRollBack);
  }

  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
    return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
  }

  @SuppressWarnings("unchecked")
  @Override
  public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql) throws SQLException {
    ErrorContext.instance().resource(ms.getResource()).activity("executing a query").object(ms.getId());
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    if (queryStack == 0 && ms.isFlushCacheRequired()) {
      clearLocalCache();
    }
    List<E> list;
    try {
      queryStack++;
      list = resultHandler == null ? (List<E>) localCache.getObject(key) : null;
      if (list != null) {
        handleLocallyCachedOutputParameters(ms, key, parameter, boundSql);
      } else {
        list = queryFromDatabase(ms, parameter, rowBounds, resultHandler, key, boundSql);
      }
    } finally {
      queryStack--;
    }
    if (queryStack == 0) {
      for (DeferredLoad deferredLoad : deferredLoads) {
        deferredLoad.load();
      }
      // issue #601
      deferredLoads.clear();
      if (configuration.getLocalCacheScope() == LocalCacheScope.STATEMENT) {
        // issue #482
        clearLocalCache();
      }
    }
    return list;
  }

  @Override
  public <E> Cursor<E> queryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameter);
    return doQueryCursor(ms, parameter, rowBounds, boundSql);
  }

  @Override
  public void deferLoad(MappedStatement ms, MetaObject resultObject, String property, CacheKey key, Class<?> targetType) {
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    DeferredLoad deferredLoad = new DeferredLoad(resultObject, property, key, localCache, configuration, targetType);
    if (deferredLoad.canLoad()) {
      deferredLoad.load();
    } else {
      deferredLoads.add(new DeferredLoad(resultObject, property, key, localCache, configuration, targetType));
    }
  }

  @Override
  public CacheKey createCacheKey(MappedStatement ms, Object parameterObject, RowBounds rowBounds, BoundSql boundSql) {
    if (closed) {
      throw new ExecutorException("Executor was closed.");
    }
    CacheKey cacheKey = new CacheKey();
    cacheKey.update(ms.getId());
    cacheKey.update(rowBounds.getOffset());
    cacheKey.update(rowBounds.getLimit());
    cacheKey.update(boundSql.getSql());
    List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
    TypeHandlerRegistry typeHandlerRegistry = ms.getConfiguration().getTypeHandlerRegistry();
    // mimic DefaultParameterHandler logic
    for (ParameterMapping parameterMapping : parameterMappings) {
      if (parameterMapping.getMode() != ParameterMode.OUT) {
        Object value;
        String propertyName = parameterMapping.getProperty();
        if (boundSql.hasAdditionalParameter(propertyName)) {
          value = boundSql.getAdditionalParameter(propertyName);
        } else if (parameterObject == null) {
          value = null;
        } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
          value = parameterObject;
        } else {
          MetaObject metaObject = configuration.newMetaObject(parameterObject);
          value = metaObject.getValue(propertyName);
        }
        cacheKey.update(value);
      }
    }
    if (configuration.getEnvironment() != null) {
      // issue #176
      cacheKey.update(configuration.getEnvironment().getId());
    }
    return cacheKey;
  }

  @Override
  public boolean isCached(MappedStatement ms, CacheKey key) {
    return localCache.getObject(key) != null;
  }

  @Override
  public void commit(boolean required) throws SQLException {
    if (closed) {
      throw new ExecutorException("Cannot commit, transaction is already closed");
    }
    clearLocalCache();
    flushStatements();
    if (required) {
      transaction.commit();
    }
  }

  @Override
  public void rollback(boolean required) throws SQLException {
    if (!closed) {
      try {
        clearLocalCache();
        flushStatements(true);
      } finally {
        if (required) {
          transaction.rollback();
        }
      }
    }
  }
  
  protected abstract int doUpdate(MappedStatement ms, Object parameter) throws SQLException;

  protected abstract List<BatchResult> doFlushStatements(boolean isRollback) throws SQLException;

  protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql)throws SQLException;

  protected abstract <E> Cursor<E> doQueryCursor(MappedStatement ms, Object parameter, RowBounds rowBounds, BoundSql boundSql) throws SQLException;

}

可以看到BaseExecutor中有四个模版方法,把执行数据的核心操作作为抽象方法。留给子类实现。

在这里插入图片描述
主要实现子类是BatchExecutor,RueuseExecutor,SimpleExecutor。这代表了三种不同类型的数据库操作。默认是SimpleExecutor 。有兴趣的可以去好好阅读一下Mybatis的源码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值