模板模式(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的源码。