设计模式之模板模式

模板方法模式定义了一个算法的步骤,并允许子类别为一个或多个步骤提供其实践方式。让子类别在不改变算法架构的情况下,重新定义算法中的某些步骤。它适应的场景有:一次性实现一个算法的不变的部分,并将可变的行为留给子类来实现;各子类中公共的行为被提取出来并集中到一个公共的父类中,从而避免代码重复。(百度百科),

看到定义或许会很迷糊,我们可以通过例子说明,其实模板模式的一个典型的应用就是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;
    }			
});

利用模板方法将相同处理逻辑的代码放到抽象父类中,以提高代码的复用性。将不同的代码使用不同的子类实现,通过对子类的扩展增加新的行为,提高代码的扩展性。很好的实现了开闭原则。但是随着不同代码的增多,需要每一个抽象类都需要一个子类来实现,这样导致类的个数增加。增加了系统的复杂性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值