模板方法模式定义了一个算法的步骤,并允许子类别为一个或多个步骤提供其实践方式。让子类别在不改变算法架构的情况下,重新定义算法中的某些步骤。它适应的场景有:一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现;各子类中公共的行为被提取出来并集中到一个公共的父类中,从而避免代码重复。(百度百科),
看到定义或许会很迷糊,我们可以通过例子说明,其实模板模式的一个典型的应用就是Spring的JdbcTemplate,这是一个经典的模板模式,我们就以Spring的JdbcTemplate为例子介绍模板模式,在介绍JdbcTemplate之前我们可以先想一下Java对数据库的操作包括哪几部分?数据库的操作可以简单的分为以下几部分:数据源的配置,数据库连接的获取,SQL的执行,结果集的处理,这四步只有结果集会有不同的处理,前三个执行逻辑基本上都是一样的。因此我们可以将前三步封装成一个模板即JdbcTemplate,结果集的实现则交给调用者自己实现。我们可以查看JdbcTemplate的源码:
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations { ...... }上面的源码中JdbcAccessor是对数据源的管理,JdbcOperations则是对数据库操作接口定义,JdbcTemplate实现了JdbcOperations将调用逻辑封装成一个模板,如下为JdbcAccessor和JdbcOperations部分源码。更多源码可以参考Spring源码。
public abstract class JdbcAccessor implements InitializingBean { protected final Log logger = LogFactory.getLog(getClass()); private DataSource dataSource; private SQLExceptionTranslator exceptionTranslator; private boolean lazyInit = true; public void setDataSource(DataSource dataSource) { this.dataSource = dataSource; } public DataSource getDataSource() { return this.dataSource; } //更多方法参考Spring源码 ...... } public interface JdbcOperations { ..... <T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException; <T> T queryForObject(String sql, RowMapper<T> rowMapper) throws DataAccessException; //更多定义查看Spring源码 ...... }接下来就是模板方法的封装,我们以上面的List<T> query(String sql, RowMapper<T> rowMapper)方法为例,查看是怎么封装模板方法的。
public <T> List<T> query(String sql, RowMapper<T> rowMapper) throws DataAccessException { //调用下面的query方法, return query(sql, new RowMapperResultSetExtractor<T>(rowMapper)); } //该方法也没有执行数据库操作,而是交给execute(StatementCallback<T> action)方法 public <T> T query(final String sql, final ResultSetExtractor<T> rse) throws DataAccessException { Assert.notNull(sql, "SQL must not be null"); Assert.notNull(rse, "ResultSetExtractor must not be null"); if (logger.isDebugEnabled()) { logger.debug("Executing SQL query [" + sql + "]"); } class QueryStatementCallback implements StatementCallback<T>, SqlProvider { @Override public T doInStatement(Statement stmt) throws SQLException { ResultSet rs = null; try { //执行数据库操作 rs = stmt.executeQuery(sql); ResultSet rsToUse = rs; if (nativeJdbcExtractor != null) { rsToUse = nativeJdbcExtractor.getNativeResultSet(rs); } //将查询的结果集交给RowMapperResultSetExtractor实例处理 return rse.extractData(rsToUse); }finally { JdbcUtils.closeResultSet(rs); } } @Override public String getSql() { return sql; } } return execute(new QueryStatementCallback()); } //真正查询数据库的方法 public <T> T execute(StatementCallback<T> action) throws DataAccessException { Assert.notNull(action, "Callback object must not be null"); //获取数据库连接 Connection con = DataSourceUtils.getConnection(getDataSource()); Statement stmt = null; try { Connection conToUse = con; if (this.nativeJdbcExtractor != null && this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativeStatements()) { conToUse = this.nativeJdbcExtractor.getNativeConnection(con); } //创建Statement stmt = conToUse.createStatement(); applyStatementSettings(stmt); Statement stmtToUse = stmt; if (this.nativeJdbcExtractor != null) { stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt); } //执行SQL语句调用的是上面方法的内部类,QueryStatementCallback T result = action.doInStatement(stmtToUse); handleWarnings(stmt); return result; }catch (SQLException ex) { JdbcUtils.closeStatement(stmt); stmt = null; DataSourceUtils.releaseConnection(con, getDataSource()); con = null; throw getExceptionTranslator().translate("StatementCallback", getSql(action), ex); }finally { JdbcUtils.closeStatement(stmt); DataSourceUtils.releaseConnection(con, getDataSource()); } }阅读上面的源码,最终结果集的处理交给了ResultSetExtractor的extractData方法,我们再看该方法的实现:
@Override public List<T> extractData(ResultSet rs) throws SQLException { List<T> results = (this.rowsExpected > 0 ? new ArrayList<T>(this.rowsExpected) : new ArrayList<T>()); int rowNum = 0; while (rs.next()) { //最终调用RowMapper接口的mapRow方法实现数据的转换,RowMapper需要我们自己实现 results.add(this.rowMapper.mapRow(rs, rowNum++)); } return results; }上面方法使用RowMapper的mapRow实现结果集与对象之间的转换,如下我们查询一个一个消息记录的例子:
jdbcTemplate.query("select * from sms", new RowMapper<Sms>(){ @Override public Sms mapRow(ResultSet rs, int rowNum) throws SQLException { //自己实现之后返回要查询的对象 return null; } });利用模板方法将相同处理逻辑的代码放到抽象父类中,以提高代码的复用性。将不同的代码使用不同的子类实现,通过对子类的扩展增加新的行为,提高代码的扩展性。很好的实现了开闭原则。但是随着不同代码的增多,需要每一个抽象类都需要一个子类来实现,这样导致类的个数增加。增加了系统的复杂性。