Java設(shè)計(jì)模式--回調(diào)模式

回調(diào)模式概念

上一節(jié)我們講了一下模板模式的作用,模板模式可以將實(shí)現(xiàn)步驟延遲到子類中進(jìn)行,其實(shí)在Java開發(fā)中,還有另外一個(gè)方法可以實(shí)現(xiàn)同樣的功能,那就是Java回調(diào)技術(shù),通過回調(diào)在接口中定義的方法,調(diào)用到具體的實(shí)現(xiàn)類中的 方法,其本質(zhì)是利用Java的動態(tài)綁定技術(shù),在這種實(shí)現(xiàn)中,可以不把實(shí)現(xiàn)類寫成單獨(dú)的類,而使用內(nèi)部類匿名內(nèi)部類來實(shí)現(xiàn)回調(diào)方法。
還是拿上一節(jié)的模板模式代碼舉例,如果我每次需要使用jdbcTemplate時(shí),都要繼承一下父類,是不是有些不方便呢? 畢竟一個(gè)類只能繼承一個(gè)父類,但是接口就不一樣了,我們可以實(shí)現(xiàn)很多接口。

那就讓我們甩掉abstract這頂帽子吧,這時(shí),就該callback(回調(diào))上場了
所謂回調(diào),就是在方法參數(shù)中傳遞一個(gè)接口,在調(diào)用此方法時(shí),必須調(diào)用方法中傳遞的接口的實(shí)現(xiàn)類。

回調(diào)模式一般滿足如下條件:

1.類A持有類B的一個(gè)引用,并且類A實(shí)現(xiàn)了一個(gè)接口CallBack;

2.類B有一個(gè)方法method(CallBack callBack),接收一個(gè)參數(shù)callBack,參數(shù)類型為CallBack,在方法method中()調(diào)用了callBack接口的實(shí)現(xiàn)類的方法。

一個(gè)示例代碼如下所示:

interface CallBack {
    public void doSomething();
}

class A implements CallBack {
    private B b;
    A(B b) {
      this.b = b;
}

public void test() {
    b.testB(this);
}

public void doSomething() {
    System.out.println("do something...");
  }
}

class B {
    public void testB(CallBack callBack) {
        System.out.println("========================");
        callBack.doSomething();
  }
}

public class CallBackDemo {
    public static void main(String[] args) {
        B b = new B();
        A a = new A(b);
        a.test();
    }
}

上述代碼分析:

這是一個(gè)最簡單的回調(diào)模式的實(shí)現(xiàn)代碼。A實(shí)現(xiàn)餓了CallBack接口,并且在A中有一個(gè)B的引用,B的testB(CallBack callBack)方法接收一個(gè)CallBack的實(shí)現(xiàn)類作為參數(shù),并且在testB()方法中代用callback的doSomething()方法,這個(gè)方法為CallBack接口的實(shí)現(xiàn)類doSomething()方法,在之前實(shí)現(xiàn)接口的時(shí)候根據(jù)自己的需要自定義功能代碼,而公共的代碼部分放在testB()方法中實(shí)現(xiàn)。

接下來我們結(jié)合前一節(jié)模板模式的代碼進(jìn)行修改,采用回調(diào)的方式來實(shí)現(xiàn):

首先,我們定義一個(gè)回調(diào)接口:

public interface StatementCallback {  
    Object doInStatement(Statement stmt) throws SQLException;  
} 

接下來我們重新定義JDBCtemplate類:

public class JdbcTemplate {  
    public final Object execute(StatementCallback action) throws SQLException{            
        Connection con = HsqldbUtil.getConnection();  
        Statement stmt = null;  
        try {   
            stmt = con.createStatement();  
            Object result = action.doInStatement(stmt);
            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();  
            }      
        }  
    }       
    //這是是為了看著更整齊添加的方法
    public Object query(StatementCallback stmt) throws SQLException{  
           return execute(stmt);  
    }  
}  

至此,我們已經(jīng)成功了將模板模式改為了采用回調(diào)模式。接下來我們創(chuàng)建一個(gè)test.java來測試一下,在前面我們也說過,回調(diào)模式有兩種調(diào)用方式,我們可以使用內(nèi)部類或匿名內(nèi)部類來實(shí)現(xiàn)回調(diào)方法:

內(nèi)部類:

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());  
    }  

在調(diào)用jdbcTemplate.query()方法時(shí),將內(nèi)部類StatementCallBack()的實(shí)例傳過去。

匿名內(nèi)部類:

//匿名類方式  
    public Object query2(final String sql) throws Exception{  
          
        JdbcTemplate jt = new JdbcTemplate();  
        return jt.query(new 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;  
            }  
        });           
    }  

在平時(shí)的代碼中,我們使用匿名內(nèi)部類的方式也更加多一些。

綜上兩種回調(diào)方式可以發(fā)現(xiàn),回調(diào)模式相比于模板模式更簡潔也更靈活一些。
通過對比模板模式與回調(diào)模式我們可以發(fā)現(xiàn)模板模式有一些弊端
當(dāng)流程中包含抽象函數(shù),子類繼承父類并實(shí)現(xiàn)父類的抽象函數(shù),這樣父類的流程這個(gè)流程是不變的,變的只是子類的抽象方法的實(shí)現(xiàn)。但是這個(gè)的基礎(chǔ)是繼承,如果你變化的部分太多,你要實(shí)現(xiàn)很多很多類,而且如果父類的流程有多個(gè),那子類要實(shí)現(xiàn)自己并不需要的抽象函數(shù),這是一個(gè)弊端。

這也是為什么spring不單一的使用傳統(tǒng)的模板方法,而加之以Callback進(jìn)行配合的原因。

試想,如果父類中有10個(gè)抽象方法,而繼承它的所有子類則要將這10個(gè)抽象方法全部實(shí)現(xiàn),子類顯得非常臃腫。而有時(shí)候某個(gè)子類只需要定制父類中的某一個(gè)方法該怎么辦呢?這個(gè)時(shí)候就要用到Callback回調(diào)了。

在文章編寫過程中,參照了一些前人的文章:

http://www.360doc.com/content/18/0714/22/56263195_770418578.shtml
https://blog.csdn.net/zhang23242/article/details/9305925

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容