Posted on 2011-11-24 16:44
火炎炎 閱讀(886)
評論(0) 編輯 收藏
首先明確閉包的概念:一個代碼段被用來做為方法的參數.
java中沒有直接使用某個方法做為另一個方法的參數的,java使用匿名內部類來模擬這種情況。
匿名內部類往往是做為一個內部類(接口)的具體實現。在一些平臺類(platform class)中有一些模板方法。模板方法的包含了固定流程。其中某些步驟是調用了內部類(接口)中的某些方法。但是平臺類將這些方法的具體實現延遲到了業務類中。業務類調用平臺類的模板方法,但是傳入匿名內部類的實現做為模板方法的參數。
示例:
package callback;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
public class AnonymousBusinessTemplateExample2 {
// 內部接口也是回調接口,只定義抽象方法。
private interface Callback {
Object doIt(Connection conn) throws SQLException;
}
// 模板方法(抽象)
private Object execute(Callback callback) throws SQLException {
Connection conn = openConnection();
try {
return callback.doIt(conn);
} finally {
closeConnection(conn);
}
}
// 業務方法(具體)
public Object sqlQuery(final String sql) throws SQLException {
//匿名內部類做為模板方法的參數來模擬閉包
return execute(new Callback() {
public Object doIt(Connection conn) throws SQLException {
return conn.createStatement().executeQuery(sql);
}
});
}
public Connection openConnection() throws SQLException {
return DriverManager.getConnection("", null);
}
public void closeConnection(Connection conn) throws SQLException {
if (conn != null && !conn.isClosed()) {
conn.close();
}
}
}
一般內部接口比內部類用的更多。內部類中的方法可以有默認的實現。匿名內部類做為業務方法的參數傳入時,會override默認的方法。內部接口的話,沒有默認的實現。完全將具體的實現交給了匿名內部類。
匿名內部類的思想是回調,即好茉塢原則。回調的一個好處是decouple。 客戶端只需要關心業務(比如匿名內部類的具體實現)而不用再關心一些資源的連接釋放什么的,這些交給平臺類中的模板方法。ruby的閉包還支持對數組中的每個元素,文件中的每行,結果集中的每個記錄的操作。而用java實現這樣的迭代并操作其中元素非常麻煩。感覺java中用的多的偏模板方法,即邏輯中固定一些流程,初始化及釋放某些資源。
動態回調函數、匿名內部類和spring中的excute方法
公司目前采用了spring框架來構建和管理整個web項目。對于持久層的處理,使用了由spring框架提供的對hibernate3的封裝。這樣做無非是為了使用spring提供的對事務的統一管理。當我們用到由spring所封裝的hibernate的時候一定會用到一個類:HibernateTemplate.這是一個對持久層處理封裝的非常完整的類,包括對session的管理(事實上session的獲取于釋放是一個令人頭疼的問題)等等,我們通常會使用HibernateTemplate的excute方法來進行數據庫操作(即使我們調用的也許是別的類似于find、get之類的方法,但是實質上最終還是轉變為了調用excute方法)。對于第一次使用這個方法一定會存在困擾。因為excute方法所需要的參數一個HibernateCallback類型的參數。而在excute方法體內部回調了HibernateCallback類型的doInHibernate方法。這是一個典型的對象回調。就到目前為止也許一切都很清晰。但是實際上如果閱讀了HibernateTemplate的內部代碼就會發現,對于像get、find這樣的方法最終都回調用excute來完成數據庫操作但是調用形式看起來卻很奇怪:
public Object get(final Class entityClass, final Serializable id, final LockMode lockMode)
throws DataAccessException
{
return execute(new HibernateCallback() {
public Object doInHibernate(Session session)
throws HibernateException
{
if(lockMode != null)
return session.get(entityClass, id, lockMode);
else
return session.get(entityClass, id);
}
}, true);
}
Excute方法的參數是一種匿名類的方式。為什么要采用匿名類呢(不管怎么說匿名類看起來總是讓人覺得不舒服)?這個地方是否必須采用匿名類呢?
首先我們來想一想這段代碼涉及到幾個關鍵點:1、回調:excute方法會回調HibernateCallback類型的doInHibernate方法;2、匿名類參數:我們為excute方法提供的參數并不是一個真正生命出來的HibernateCallback實例。3、動態創建回調方法:如果我們打開HibernateCallback類就會發現,其實這是一個abstract類型的類,而他聲明了唯一的一個ie抽象方法就是doInHibernate。問題似乎已經明朗了,如果不采用匿名類,我們需要做的是為HibernateCallback創建一個實現類,并且實現doInHibernate方法。但是最要命的問題是doInHibernate方法的實現對于我們的實際需求來說每一次調用可能都是不一樣的(在doInHibernate方法中我們使用session進行數據庫操作,對于不同的業務邏輯,方法實現必定是不一樣的),采用了匿名類我們不用在代碼重創建新的類型,而且可以動態的創建我們所需要的回調函數。
總結一下,我們上面所講的并非是如何使用HibernateTemplate這個類。我們得到的結論是:當我們需要動態的創建回調函數的時候,匿名內部類是一個好的方式。注:這種需要動態創建的回調方法通常是一個interface中的接口或者abstract class中的抽象方法。