【设计模式】- 模版方法模式

179 阅读3分钟

定义:定义一个算法的框架,并允许子类提供框架中一个或多个步骤的具体实现。模版方法将算法的步骤实现交由子类决定,并且不会影响算法结构。

代码示例

/**
* 模版抽象类,定义算法结构
*/
public abstract class AbstractClass{
// 也可定义为抽象方法,每个抽象方法都需要实现
    protected void method1(){};
    protected void method2(){};
    public final void templateMethod(){
        this.method1();
        this.method2();
    }
}
/**
* 子类实现某个步骤
*/
    public class ConcreteClass extends AbstractClass{
        @Override
        protected void method1(){};
    }

    public static void main(String[] args) {
        AbstractClass abstractClass=new ConcreteClass();
        abstractClass.templateMethod();
    }

来看下spring中模版方法的使用 JdbcTemplate:

//留意到JdbcTemplate的继承关系
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
        // 主要看这个方法讲解,实现JdbcOperations的接口
	@Override
	@Nullable
	public <T> T execute(StatementCallback<T> action) throws DataAccessException {
		Assert.notNull(action, "Callback object must not be null");

		Connection con = DataSourceUtils.getConnection(obtainDataSource());
		Statement stmt = null;
		try {
			stmt = con.createStatement();
			applyStatementSettings(stmt);
                        // 模版的调用在这句
			T result = action.doInStatement(stmt);
			handleWarnings(stmt);
			return result;
		}
		catch (SQLException ex) {
			// Release Connection early, to avoid potential connection pool deadlock
			// in the case when the exception translator hasn't been initialized yet.
			String sql = getSql(action);
			JdbcUtils.closeStatement(stmt);
			stmt = null;
			DataSourceUtils.releaseConnection(con, getDataSource());
			con = null;
			throw translateException("StatementCallback", sql, ex);
		}
		finally {
			JdbcUtils.closeStatement(stmt);
			DataSourceUtils.releaseConnection(con, getDataSource());
		}
	}

}

在来看下StatementCallback接口,从注释就能明白这个接口的作用,以及JdbcTemplate所负责的功能。不得不强调良好的注释,能增加源码的可读性。

@FunctionalInterface
public interface StatementCallback<T> {

	/**
	 * Gets called by {@code JdbcTemplate.execute} with an active JDBC
	 * Statement. Does not need to care about closing the Statement or the
	 * Connection, or about handling transactions: this will all be handled
	 * by Spring's JdbcTemplate.
	 * <p><b>NOTE:</b> Any ResultSets opened should be closed in finally blocks
	 * within the callback implementation. Spring will close the Statement
	 * object after the callback returned, but this does not necessarily imply
	 * that the ResultSet resources will be closed: the Statement objects might
	 * get pooled by the connection pool, with {@code close} calls only
	 * returning the object to the pool but not physically closing the resources.
	 * <p>If called without a thread-bound JDBC transaction (initiated by
	 * DataSourceTransactionManager), the code will simply get executed on the
	 * JDBC connection with its transactional semantics. If JdbcTemplate is
	 * configured to use a JTA-aware DataSource, the JDBC connection and thus
	 * the callback code will be transactional if a JTA transaction is active.
	 * <p>Allows for returning a result object created within the callback, i.e.
	 * a domain object or a collection of domain objects. Note that there's
	 * special support for single step actions: see JdbcTemplate.queryForObject etc.
	 * A thrown RuntimeException is treated as application exception, it gets
	 * propagated to the caller of the template.
	 * @param stmt active JDBC Statement
	 * @return a result object, or {@code null} if none
	 * @throws SQLException if thrown by a JDBC method, to be auto-converted
	 * to a DataAccessException by an SQLExceptionTranslator
	 * @throws DataAccessException in case of custom exceptions
	 * @see JdbcTemplate#queryForObject(String, Class)
	 * @see JdbcTemplate#queryForRowSet(String)
	 */
	@Nullable
	T doInStatement(Statement stmt) throws SQLException, DataAccessException;

}

那么JdbcTemplate如何调用execute方法的呢,且看query方法

	@Override
	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);
					}
					return rse.extractData(rsToUse);
				}
				finally {
					JdbcUtils.closeResultSet(rs);
				}
			}
			@Override
			public String getSql() {
				return sql;
			}
		}
        // 提供具体实现类,实现execute某一步骤的自定义
		return execute(new QueryStatementCallback());
	}

可以看到StatementCallback的实现类:

image.png

Spring中1以Template结尾命名的类都是用的模板方法模式,同样的影子在RestTemplate、RedisTemplate等类中也能发现。


公众号.jpg