首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

JDK 內建 Observer 設計模式深度解析

     編輯:Java 相關知識

前言

設計模式介紹文章層出不窮,但深度研究型文章仍顯稀缺,實屬憾事。本文旨在拋磚引玉,具體分析 Java JDK 內建 Observer 設計模式(下文若無特別說明,Observer 設計模式即指 JDK 內建實作)的實作機制。

1. Observer 設計模式概要

在 GOF《設計模式》中,Observer 屬於行為型模式。JDK 提供的實作由 java.util.Observable 類別與 java.util.Observer 介面組成。從名稱即可看出兩者扮演的角色:Observer 為觀察者角色,Observable 為被觀察目標角色。

Observable 封裝了 Subject 基本功能:註冊觀察者、註銷觀察者等。這些功能是任何扮演 Observable 角色的類別都必須實作的,JDK 將這些通用功能專門封裝在一個類別中,設計合理。通常我們的類別只要繼承 Observable 即可成為 Observable 角色類別,使用非常簡單。

2. 實際開發中使用 Observer 模式的困難

專案實際開發往往更複雜。Java 不支援多重繼承,在很多時候成為使用 Observer 模式的絆腳石。例如,我們設計的一個類別已經繼承自某個類別,此時想同時讓它扮演 Observable 角色就變得棘手。如何達成「多重繼承」效果是擺在面前的難題。先分析 Observable 類別。

3. Observable 類別「觸發通知」的原理

Observable 必須「有變化」才能觸發通知觀察者,這是其本質體現。查看原始碼可知一二:

//……省略……
private boolean changed = false;
//……省略……
public void notifyObservers(Object arg) {
    //……省略……
    Object[] arrLocal;
    synchronized (this) {
        //……省略……
        if (!changed)
            return;
        arrLocal = obs.toArray();
        clearChanged();
    }
    //……省略……
}
protected synchronized void setChanged() {
    changed = true;
}

protected synchronized void clearChanged() {
    changed = false;
}

關鍵在於 notifyObservers(Object arg) 方法中的 if (!changed) return;。若 changed 屬性為 false,將直接返回,根本不會觸發通知。且 changed 初始化為 false,意味著若不主動設為 true,不會有任何變化,也就起不到「通知」作用。因此,設定 changed 屬性值是應用 JDK Observer 模式的關鍵。唯一入口是 setChanged()。下面分析 changed 屬性及相關方法 setChanged() 和 clearChanged()。

4. Observable 類別深度分析

Observable#changed 初始值為 false 很好理解。細心讀者會注意到 setChanged() 和 clearChanged() 修飾符都是 protected,而非 public。這是否必要且合理?答案是肯定的。

前文已提到,setChanged() 是設定 changed 的唯一入口,定義為 protected 意味著透過建立 Observable 物件再設定 changed 將變得不可能。從這意義上說,要應用 Observer 模式,必須繼承 Observable 類別。為什麼不能定義成 public?似乎定義成 public 更方便設定值?看 Observable 相關程式碼:

//……省略……
public void notifyObservers(Object arg) {
    //……省略……
    for (int i = arrLocal.length-1; i>=0; i--)
        ((Observer)arrLocal[i]).update(this, arg);
}

這段程式碼表達:找出所有已註冊的 Observer,逐個進行「通知」,透過呼叫 Observer#update(Observable, Object) 方法。注意 update 第一個參數是 this,且此程式碼在 Observable 類別內。這反覆強調:**發出「通知」的,必須是 observable 自己(Observable 類別或其子類別),其他任何類別都不行。**

這意味著我們的 observable 類別繼承 Observable 類別是必要的。若不繼承而採用組合方式,將無法保證正確傳遞 this。換句話說,組合方式使用 Observable 幾乎毫無意義。同時,修飾符定義為 protected,確保觸發通知只在 Observable 內部進行,不會在其他地方觸發,內聚性極強。若 setChanged() 定義為 public,將無法保證正確「傳遞 this」的硬性要求,不符合「只有 observable 能直接或間接通知 observer」這一 Observer 設計模式的硬性要求。由此可見 JDK 設計思想之深度。

5. 解決使用 Observer 模式的困難

借助 Adapter 設計模式(詳見筆者發表的 Adapter 相關文章)與 Java 支援多介面特性,基本可解決「多重繼承」問題。核心思想是結合繼承/實作與組合達到效果。前文分析已知 Observable 必須繼承使用,不能組合使用,因此只需將需扮演 Observable 角色的類別裝扮成 Adapter 角色,將該類別原繼承的類別裝扮成 Adaptee 角色。示例程式碼:

// 欲充當 observable 角色的類別原始碼:
public class MyObject extends BaseObject {
    public MyObject() {}
    public void method1() {}
}

// 充當 observable 角色後的程式碼:
public class MyObject extends Observable {
    private BaseObject baseObject = null;
    public MyObject(BaseObject baseObject) {
        this.baseObject = baseObject;
    }
}

6. 注意事項

若上例中 BaseObject 也用到需傳遞「this」的方法,上述組合方式可能失效。這是最糟情況。此時可考慮在 BaseObject 類別這些「瓶頸」處盡量採用介面取代類別(包含抽象類別)來解決。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved