構建高效的企業級Java應用系列(一)架構篇——2
2:跨越組件邊界優先采用松耦合
首先需要明確三個概念:緊耦合、延遲綁定、松耦合。
緊耦合:如果一個給定的“事物”必須做出變化,以適應另一個“事物”的變化,我們稱前者緊耦合于后者。例如:ServletAPI緊耦合于HTTP協議規范:如果HTTP改變其規則,則ServletAPI(至少是javax.servlet.http包)也需要做出相應的修改。
延遲綁定:先看下面的一段代碼
Class c = Person.class;
Object o = c.newInstance();
Method m = c.getMethod("getAge", new Class[ ] { });
int age = ((Integer)(m.invoke(o, new Object[ ] { })).getIntValue();
Object o = c.newInstance();
Method m = c.getMethod("getAge", new Class[ ] { });
int age = ((Integer)(m.invoke(o, new Object[ ] { })).getIntValue();
我們常常認為上面的代碼是松耦合代碼,因為Person類及其getAge方法沒有直接出現在代碼中。這樣,如果Person類的getAge方法發生了變化,編譯器也不會報錯。然而,盡管我們在語法上沒有綁定于Person類,但實際存在語義上的綁定。如果這個無參數的getAge方法并不存在,或者通過c引用的Class對象不可訪問,這段代碼還是要失敗,而且是直到運行時才會被發現。所以,這并不是松耦合代碼,而是松綁定代碼。
Java動態方法調用機制有時也被稱為延遲綁定,直到運行時刻,也就是當確切知道具體對象的時刻,才能決定調用的是繼承體系中的哪個方法。
松耦合:當改變組件的實現(甚至于從Java移植至.NET)不會影響到組件使用者;或者組件使用者的改變的問題不需要組件做出相應的改變。
對于企業系統而言,由于其復雜的本質,確實是需要松耦合。企業系統需要訪問其他系統,也會被其他系統訪問,包括公司內部或外部的系統。這里需要針對不同環境下的組件加以區分:
對于遠程組件,要避免緊耦合的一種比較可行的方法是:優先采用數據驅動的接口,而不是行為驅動的接口(本質上就是傳遞消息和RPC的區別)。雖然,這么做可能會破壞組件的面向對象的性質,但是這未必是件壞事(在后續建議中討論)。對通訊保持上下文完整(content-complete)也對促進遠程組件的松耦合有幫助。
對于本地組件,本地方法調用的成本要遠遠低于遠程方法調用的成本,所以是否采用“數據驅動”或“上下文完整”就不再重要。例如,根據Servlet規范,在Web應用和容器之間能安全地創建Context對象,作為Web應用和容器之間保持一定距離的方式;例如,如果我們能向Context請求有關組件的文件資源(getResource和getResourceAsStream),就不需要知道容器的部署模型如何去執行的。