模板方法模式:定义一个算法骨架,并将其中一部分具体实现延迟到子类。他使得子类可以不必改变一个算法结构即可重定义该算法的某些特定步骤。
模板方法可以聚集方法实现里的公共部分,而把变化的部分延迟到了子类
package templatemethod; public abstract class Salary { /** 算法骨架 */ public float realIncome(float salary) { System.out.println("公共部分"); return salary - tax(salary) - ensurance(salary); } /** 延迟到子类的具体实现 * 实际上这两个抽象方法就是工厂方法(Factory Method) * GoF的设计模式里也提到了,模板方法实际上也依赖于工厂方法来实现 */ protected abstract float tax(float salary); protected abstract float ensurance(float salary); } package templatemethod; public class BeijingSalary extends Salary { @Override protected float tax(float salary) { return (float) (salary * 0.03); } @Override protected float ensurance(float salary) { return (float) (salary * 0.2); } } package templatemethod; public class ShanghaiSalary extends Salary { @Override protected float tax(float salary) { return (float) (salary * 0.032); } @Override protected float ensurance(float salary) { return (float) (salary * 0.22); } } package templatemethod; public class Test { public static void main(String[] args) { Salary bjSalary = new BeijingSalary(); Salary shSalary = new ShanghaiSalary(); float salary = 11000; System.out.println("Beijing real income of " + salary + " is " + bjSalary.realIncome(salary)); System.out.println("Shanghai real income of " + salary + " is " + shSalary.realIncome(salary)); } }
程序输出:
公共部分
Beijing real income of 11000.0 is 8470.0
公共部分
Shanghai real income of 11000.0 is 8228.0
Spring中也有许多Template的实现,下面对于JDBCTemplate为例做个简单介绍
JDBCTemplate一般的execute()方法实现逻辑是穿如一个SQL参数和一个Callback调用类,这个Callback调用就是模板方法的实现类
如下
@Override public <T> T execute(String sql, PreparedStatementCallback<T> action) throws DataAccessException { return execute(new SimplePreparedStatementCreator(sql), action); } @Override public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action) throws DataAccessException { Assert.notNull(psc, "PreparedStatementCreator must not be null"); Assert.notNull(action, "Callback object must not be null"); if (logger.isDebugEnabled()) { String sql = getSql(psc); logger.debug("Executing prepared SQL statement" + (sql != null ? " [" + sql + "]" : "")); } Connection con = DataSourceUtils.getConnection(getDataSource()); PreparedStatement ps = null; try { Connection conToUse = con; if (this.nativeJdbcExtractor != null && this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativePreparedStatements()) { conToUse = this.nativeJdbcExtractor.getNativeConnection(con); } ps = psc.createPreparedStatement(conToUse); applyStatementSettings(ps); PreparedStatement psToUse = ps; if (this.nativeJdbcExtractor != null) { psToUse = this.nativeJdbcExtractor.getNativePreparedStatement(ps); } T result = action.doInPreparedStatement(psToUse); handleWarnings(ps); 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. if (psc instanceof ParameterDisposer) { ((ParameterDisposer) psc).cleanupParameters(); } String sql = getSql(psc); psc = null; JdbcUtils.closeStatement(ps); ps = null; DataSourceUtils.releaseConnection(con, getDataSource()); con = null; throw getExceptionTranslator().translate("PreparedStatementCallback", sql, ex); } finally { if (psc instanceof ParameterDisposer) { ((ParameterDisposer) psc).cleanupParameters(); } JdbcUtils.closeStatement(ps); DataSourceUtils.releaseConnection(con, getDataSource()); } }
而如果我们没有传入Callback类,则使用其定义的内部类替代,如下
public void execute(final String sql) throws DataAccessException { if (logger.isDebugEnabled()) { logger.debug("Executing SQL statement [" + sql + "]"); } class ExecuteStatementCallback implements StatementCallback<Object>, SqlProvider { @Override public Object doInStatement(Statement stmt) throws SQLException { stmt.execute(sql); return null; } @Override public String getSql() { return sql; } } execute(new ExecuteStatementCallback()); }
对于JDBCTemplate来说,它的公共部分就是获取数据库连接,获取Statement,以及他们的关闭,还有SQLException的处理,所以在JDBCTemplate的使用中我们无需再编写这些冗长的连接及关闭还有异常代码.
而Spring的模板方法实现并不依赖于继承,而是使用了接口和类的传入,这样的好处是更灵活,不是继承的话,我们不需要去实现一些我们认为不需要的抽象方法,仅需根据自己的需要调用模板方法并传入参数即可.
另外值得一提的就是Spring的持久层异常处理机制,它将表意不明的SQLException转化成了各种粒度更细的非检查性Exception,好处就是我们不需要显示的去try catch,而且在出现异常后可以更明了的知道异常的信息.