一.模板模式的适用场景
1.一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现
2.各子类中公共的行为被提取出来并集中到一个公共的父类中,从而避免代码重复
二.模板模式的优点
1.利用模板方法将相同处理逻辑的代码放到抽象父类中,提高代码的复用性
2.把不同的代码放到不同的子类中,通过对子类的扩展增加新的行为,提高代码的扩展性
3.把不变的行为写在父类上,去除子类的重复代码,提供了一个很好的代码复用平台,符合开闭原则
三.模板模式的缺点
1.类数目的增加,每一个抽象类都需要一个子类来实现,这样导致类的个数增加
2.类数量的增加,间接地增加了系统实现的复杂度
3.继承关系自身缺点,如果父类添加新的抽象方法,所有子类都要改一遍
四.模板模式在源码中的体现
1.JDK中的AbstractList,来看代码:
package java.util;
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
...
abstract public E get(int index);
...
}
我们看到 get()是一个抽象方法,那么它的逻辑就是交给子类来实现,我们大家所熟知的 ArrayList 就 是 AbstractList 的 子 类 。 同 理 , 有 AbstractList 就 有 AbstractSet 和 AbstractMap,有兴趣的小伙伴可以去看看这些的源码实现
2.HttpServlet中,有三个方法service()和doGet(),doPost()方法,都是模板方法的抽象实现
HttpServlet的父类GenericServlet中service方法
@Override
public abstract void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException;
HttpServlet中的service方法
public abstract class HttpServlet extends GenericServlet {
....
@Override
public void service(ServletRequest req, ServletResponse res)
throws ServletException, IOException {
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException(lStrings.getString("http.non_http"));
}
service(request, response);
}
....
}
service的重载方法中,根据请求方式的不同,调用了doPost,或者doGet等等
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
// servlet doesn't support if-modified-since, no reason
// to go through further expensive logic
doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
} catch (IllegalArgumentException iae) {
// Invalid date header - proceed as if none was set
ifModifiedSince = -1;
}
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
// If the servlet mod time is later, call doGet()
// Round down to the nearest second for a proper compare
// A ifModifiedSince of -1 will always be less
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
//
// Note that this means NO servlet supports whatever
// method was requested, anywhere on this server.
//
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}
3.在Mybaits的框架中也有一些经典的应用,例如BaseExecutor类
BaseExecutor是一个基础的SQL执行类,实现了大部分的SQL执行逻辑,然后把几个方法交给子类定制化完成,源码如下
public abstract class BaseExecutor implements Executor {
...
protected abstract int doUpdate(MappedStatement var1, Object var2) throws SQLException;
protected abstract List<BatchResult> doFlushStatements(boolean var1) throws SQLException;
protected abstract <E> List<E> doQuery(MappedStatement var1, Object var2, RowBounds var3, ResultHandler var4, BoundSql var5) throws SQLException;
protected abstract <E> Cursor<E> doQueryCursor(MappedStatement var1, Object var2, RowBounds var3, BoundSql var4) throws SQLException;
...
}
如 doUpdate、doFlushStatements、doQuery、doQueryCursor 这几个方法就是交 由子类来实现,,那么 BaseExecutor 有哪些子类呢?我们来看一下它的类图:
我们一起来看一下 SimpleExecutor 的 doUpdate 实现:
@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.update(stmt);
} finally {
closeStatement(stmt);
}
}
再对比一下BatchExecutor中的doUpdate()方法
@Override
public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
final Configuration configuration = ms.getConfiguration();
final StatementHandler handler = configuration.newStatementHandler(this, ms, parameterObject, RowBounds.DEFAULT, null, null);
final BoundSql boundSql = handler.getBoundSql();
final String sql = boundSql.getSql();
final Statement stmt;
if (sql.equals(currentSql) && ms.equals(currentStatement)) {
int last = statementList.size() - 1;
stmt = statementList.get(last);
applyTransactionTimeout(stmt);
handler.parameterize(stmt);// fix Issues 322
BatchResult batchResult = batchResultList.get(last);
batchResult.addParameterObject(parameterObject);
} else {
Connection connection = getConnection(ms.getStatementLog());
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt); // fix Issues 322
currentSql = sql;
currentStatement = ms;
statementList.add(stmt);
batchResultList.add(new BatchResult(ms, sql, parameterObject));
}
handler.batch(stmt);
return BATCH_UPDATE_RETURN_VALUE;
}
细心的小伙伴一定看出了差异