介紹
模板方式模式定義一個(gè)操作中的算法的骨架柄沮,而將一些步驟延遲到子類中祭务。模板方法使得子類可以不改變一個(gè)算法的結(jié)構(gòu)即可重定義該算法的某些特定步驟平绩。
所以父類模板方法中有兩類方法:
- 共同的方法:所有子類都會(huì)用到的代碼
- 不同的方法:子類要覆蓋的方法臼勉,分為兩種:
抽象方法:父類中的是抽象方法文兢,子類必須覆蓋
鉤子方法:父類中是一個(gè)空方法晤斩,子類繼承了默認(rèn)也是空的
生活中也有許多模板方法的例子,例如手機(jī)的操作(開機(jī)姆坚、開軟件等澳泵、關(guān)機(jī))、吃飯(點(diǎn)單兼呵、吃東西兔辅、買單)、煮菜(生火击喂,煮菜幢妄、關(guān)火)等等。
spring 中對 Hibernate 的支持茫负,將一些已經(jīng)定好的方法封裝起來蕉鸳,比如開啟事務(wù)、獲取 Session忍法、關(guān)閉 Session 等潮尝。
結(jié)構(gòu)圖
抽象類(AbstractClass): 定義抽象的原語操作(primitive operation) ,具體的子類將重定義它們以實(shí)現(xiàn)一個(gè)算法饿序, 實(shí)現(xiàn)一個(gè)模板方法,定義一個(gè)算法的骨架勉失。該模板方法不僅調(diào)用原語操作,也調(diào)用定義
具體子類 (ConcreteClass): 實(shí)現(xiàn)原語操作以完成算法中與特定子類相關(guān)的步驟原探。
案例
這篇就拿最原始的jdbc連接來說事乱凿。
最原始的jdbc連接代碼
public class BasicJdbc {
public static void main(String[] args) throws Exception{
//1、加載JDBC驅(qū)動(dòng)程序
Class.forName("com.mysql.jdbc.Driver").newInstance(); //MYSQL驅(qū)動(dòng)
//2咽弦、創(chuàng)建數(shù)據(jù)庫的連接
Connection con = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", "root", "root"); //鏈接本地MYSQL
//3徒蟆、創(chuàng)建一個(gè)Statement
Statement stmt = con.createStatement();
//4、執(zhí)行SQL語句
ResultSet res = stmt.executeQuery("select * from user ");
//5型型、處理結(jié)果
if (res.next()) {
}
//6段审、關(guān)閉連接
}
}
上面的代碼中,1闹蒜、2寺枉、3抑淫、6代碼都輸固定的,只有4姥闪、5中是不固定的始苇。所以可以用模板方法將1、2筐喳、3催式、6的代碼封裝在抽象類中,而4疏唾、5兩步交給子類蓄氧。
抽象類
public abstract class JdbcTemplate {
public Object execute(String sql) throws Exception{
Class.forName("com.mysql.jdbc.Driver").newInstance(); //MYSQL驅(qū)動(dòng)
Connection con = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", "root", "root"); //鏈接本地MYSQL
Statement stmt = con.createStatement();
//執(zhí)行SQL語句
sql += addPage();
ResultSet rs = stmt.executeQuery(sql);
//處理結(jié)果
Object object = handleResultSet(rs);
//關(guān)閉連接
return object;
}
public abstract Object handleResultSet(ResultSet rs);
//類似鉤子方法(可用可不用)
public String addPage() {
return "";
}
}
實(shí)現(xiàn)類
public class UserJdbc extends JdbcTemplate{
@Override
public Object handleResultSet(ResultSet rs) {
//處理結(jié)果集
return null;
}
@Override
public String addPage() {
int pageNumber = 1,pageSize = 5;
return "limit "+pageNumber+","+pageSize;
}
}
當(dāng)然函似,上面的代碼只是闡述大意而已槐脏,并不適用于項(xiàng)目中。接下來會(huì)根據(jù)spring源碼中的JdbcTemplate進(jìn)行分析撇寞。
spring模板方法(JdbcTemplate)
spring中的JdbcTemplate不僅僅使用了模板模式顿天,還使用了回調(diào)模式
JdbcTemplate中的execute方法
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);
}
stmt = conToUse.createStatement();
applyStatementSettings(stmt);
Statement stmtToUse = stmt;
if (this.nativeJdbcExtractor != null) {
stmtToUse = this.nativeJdbcExtractor.getNativeStatement(stmt);
}
T result = action.doInStatement(stmtToUse);
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.
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());
}
}
可以看到傳入的參數(shù)是StatementCallback類,然后調(diào)用action.doInStatement(stmtToUse)返回一個(gè)泛型結(jié)果蔑担,下面看看StatementCallback的源代碼
public interface StatementCallback<T> {
T doInStatement(Statement stmt) throws SQLException, DataAccessException;
}
可以看到就只有一個(gè)doInStatement方法牌废,現(xiàn)在再看看調(diào)用execute(StatementCallback<T> action)的方法
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());
}
這里定義了一個(gè)內(nèi)部類ExecuteStatementCallback 實(shí)現(xiàn)了StatementCallback接口,然后在調(diào)用execute(StatementCallback<T> action)方法啤握,最后再回調(diào)ExecuteStatementCallback 內(nèi)部類的doInStatement方法鸟缕。
這樣做的話可以有效減少子類的個(gè)數(shù),不同的處理結(jié)果只要傳入不同的實(shí)現(xiàn)了StatementCallback接口的類就可以了排抬, 這樣其實(shí)也是Spring的無侵入設(shè)計(jì)思想的體現(xiàn)懂从,同時(shí)也是“依賴優(yōu)于繼承”設(shè)計(jì)理念的體現(xiàn)。