1. 程式人生 > >Spring中模板模式和回撥模式(一)

Spring中模板模式和回撥模式(一)

模板模式

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
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;  
}  
在面向物件程式設計的年代裡,這樣的程式碼簡直不能上人容忍。試想,上面我們只是做了一張表的查詢,如果我們要做第2張表,第3張表呢,又是一堆重複的程式碼: 
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回調了。