Spring中模板模式和回撥模式(一)
阿新 • • 發佈:2019-01-06
模板模式
public abstract class TemplatePattern { //模板方法 public final void templateMethod(){ method1(); method2();//勾子方法 method3();//抽象方法 } private void method1(){ System.out.println("父類實現業務邏輯"); } public void method2(){ System.out.println("父類預設實現,子類可覆蓋"); } protected abstract void method3();//子類負責實現業務邏輯 }
父類中有三個方法,分別是method1(),method2()和method3()。
method1()是私有方法,有且只能由父類實現邏輯,由於方法是private的,所以只能父類呼叫。
method2()是所謂的勾子方法。父類提供預設實現,如果子類覺得有必要定製,則可以覆蓋父類的預設實現。
method3()是子類必須實現的方法,即制定的步驟。
由此可看出,演算法的流程執行順序是由父類掌控的,子類只能配合。
這個子類只覆蓋了必須覆蓋的方法public class TemplatePatternImpl extends TemplatePattern { @Override protected void method3() { System.out.println("method3()在子類TemplatePatternImpl中實現了!!"); } }
TemplatePattern t1 = new TemplatePatternImpl();
t1.templateMethod();
輸出
父類實現業務邏輯
父類預設實現,子類可覆蓋
method3()在子類TemplatePatternImpl中實現了!!
接下來模仿spring動手寫一個基於模板模式和回撥的jdbcTemplate在面向物件程式設計的年代裡,這樣的程式碼簡直不能上人容忍。試想,上面我們只是做了一張表的查詢,如果我們要做第2張表,第3張表呢,又是一堆重複的程式碼:public List<User> query() { List<User> userList = new ArrayList<User>(); String sql = "select * from User"; Connection con = null; PreparedStatement pst = null; ResultSet rs = null; try { con = HsqldbUtil.getConnection(); pst = con.prepareStatement(sql); rs = pst.executeQuery(); User user = null; while (rs.next()) { user = new User(); user.setId(rs.getInt("id")); user.setUserName(rs.getString("user_name")); user.setBirth(rs.getDate("birth")); user.setCreateDate(rs.getDate("create_date")); userList.add(user); } } catch (SQLException e) { e.printStackTrace(); }finally{ if(rs != null){ try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } try { pst.close(); } catch (SQLException e) { e.printStackTrace(); } try { if(!con.isClosed()){ try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } } catch (SQLException e) { e.printStackTrace(); } } return userList; }
1、獲取connection
2、獲取statement
3、獲取resultset
4、遍歷resultset並封裝成集合
5、依次關閉connection,statement,resultset,而且還要考慮各種異常
6、.....
這時候,使用模板模式的時機到了!!!
通過觀察我們發現上面步驟中大多數都是重複的,可複用的,只有在遍歷ResultSet並封裝成集合的這一步驟是可定製的,因為每張表都對映不同的java bean。這部分程式碼是沒有辦法複用的,只能定製。那就讓我們用一個抽象的父類把它們封裝一下吧:
public abstract class JdbcTemplate {
//template method
public final Object execute(String sql) throws SQLException{
Connection con = HsqldbUtil.getConnection();
Statement stmt = null;
try {
stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(sql);
Object result = doInStatement(rs);//abstract method
return result;
}
catch (SQLException ex) {
ex.printStackTrace();
throw ex;
}
finally {
try {
stmt.close();
} catch (SQLException e) {
e.printStackTrace();
}
try {
if(!con.isClosed()){
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//implements in subclass
protected abstract Object doInStatement(ResultSet rs);
}
在上面這個抽象類中,封裝了SUN JDBC API的主要流程,而遍歷ResultSet這一步驟則放到抽象方法doInStatement()中,由子類負責實現。 好,我們來定義一個子類,並繼承上面的父類:
public class JdbcTemplateUserImpl extends JdbcTemplate {
@Override
protected Object doInStatement(ResultSet rs) {
List<User> userList = new ArrayList<User>();
try {
User user = null;
while (rs.next()) {
user = new User();
user.setId(rs.getInt("id"));
user.setUserName(rs.getString("user_name"));
user.setBirth(rs.getDate("birth"));
user.setCreateDate(rs.getDate("create_date"));
userList.add(user);
}
return userList;
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
}
試想,如果我每次用jdbcTemplate時,都要繼承一下上面的父類,是不是有些不方面呢?
那就讓我們甩掉abstract這頂帽子吧,這時,就該callback(回撥)上場了
所謂回撥,就是方法引數中傳遞一個介面,父類在呼叫此方法時,必須呼叫方法中傳遞的介面的實現類。
那我們就來把上面的程式碼改造一下,改用回撥實現吧:
首先,我們來定義一個回撥介面:
public interface StatementCallback {
Object doInStatement(Statement stmt) throws SQLException;
}
這時候,我們就要方法的簽名改一下了:
private final Object execute(StatementCallback action) throws SQLException
Object result = action.doInStatement(stmt);
public Object query(StatementCallback stmt) throws SQLException{
return execute(stmt);
}
我們來寫一個測試類Test.java測試一下吧:
這時候,訪問有兩種方式,一種是內部類的方式,一種是匿名方式。
//內部類方式
public Object query(final String sql) throws SQLException {
class QueryStatementCallback implements StatementCallback {
public Object doInStatement(Statement stmt) throws SQLException {
ResultSet rs = stmt.executeQuery(sql);
List<User> userList = new ArrayList<User>();
User user = null;
while (rs.next()) {
user = new User();
user.setId(rs.getInt("id"));
user.setUserName(rs.getString("user_name"));
user.setBirth(rs.getDate("birth"));
user.setCreateDate(rs.getDate("create_date"));
userList.add(user);
}
return userList;
}
}
JdbcTemplate jt = new JdbcTemplate();
return jt.query(new QueryStatementCallback());
}
為什麼spring不用傳統的模板方法,而加之以Callback進行配合呢?
試想,如果父類中有10個抽象方法,而繼承它的所有子類則要將這10個抽象方法全部實現,子類顯得非常臃腫。而有時候某個子類只需要定製父類中的某一個方法該怎麼辦呢?這個時候就要用到Callback回調了。