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

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

代码示例

/**
* 模版抽象类,定义算法结构
*/
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等类中也能发现。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容