設計模式——觀察者模式

吳俊榮發表於2020-12-18

觀察者模式

觀察者模式定義了一種一對多的依賴關係,讓多個觀察者物件同時監聽某一個主題物件。這個主題物件在狀態上發生變化時,會通知所有觀察者物件,使它們能夠自動更新自己。

觀察者模式是物件的行為模式,又叫釋出-訂閱(Publish/Subscribe)模式、模型-檢視(Model/View)模式、源-監聽器(Source/Listener)模式或從屬者(Dependents)模式。

模式結構

模式涉及角色

抽象被觀察者:持有觀察者的引用和被觀察者狀態標記,包括一些新增,刪除,通知觀察者的方法等。

抽象觀察者:有一個回撥 update 方法。

具體被觀察者:對抽象被觀察者的實現。

具體觀察者:對抽象觀察者的實現,回撥 update 方法實現。

模式結構圖

這裡寫圖片描述
(圖片來自網路,侵刪)

示例

因為 Java 的 jdk 中已經有觀察者模式的工具類,所以以 jdk 中的觀察者模式為例。jdk 中已經定義好了被觀察者類 Observable 和觀察者介面 Observer。Observable 類中有記錄狀態屬性和持有觀察者引用的向量,然後是一些新增,刪除,通知觀察者的方法,值得注意的是只有被觀察者物件的狀態發生變化時,才會回撥觀察者的 update 方法,見方法 public void notifyObservers(Object arg) 實現程式碼。

程式碼實現

被觀察者類 Observable

public class Observable {
    private boolean changed = false;
    //觀察者的引用向量
    private Vector<Observer> obs;

    public Observable() {
        obs = new Vector<>();
    }

    //新增觀察者
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }

    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }

    //通知觀察者
    public void notifyObservers() {
        notifyObservers(null);
    }

    public void notifyObservers(Object arg) {
        /*
         * a temporary array buffer, used as a snapshot of the state of
         * current Observers.
         */
        Object[] arrLocal;

        synchronized (this) {
            /* We don't want the Observer doing callbacks into
             * arbitrary code while holding its own Monitor.
             * The code where we extract each Observable from
             * the Vector and store the state of the Observer
             * needs synchronization, but notifying observers
             * does not (should not).  The worst result of any
             * potential race-condition here is that:
             * 1) a newly-added Observer will miss a
             *   notification in progress
             * 2) a recently unregistered Observer will be
             *   wrongly notified when it doesn't care
             */
            if (!changed)   //被觀察者狀態沒有變化不會通知觀察者的
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }

    protected synchronized void setChanged() {
        changed = true;
    }

    //***其他方法就不在羅列了

觀察者介面 Observer

public interface Observer {
    /**
     * This method is called whenever the observed object is changed. An
     * application calls an <tt>Observable</tt> object's
     * <code>notifyObservers</code> method to have all the object's
     * observers notified of the change.
     *
     * @param   o     the observable object.
     * @param   arg   an argument passed to the <code>notifyObservers</code>
     *                 method.
     */
    void update(Observable o, Object arg);
}

具體被觀察者類 ConcreteObservable

/**
 * 具體被觀察者
 */
class ConcreteObservable extends Observable{

    public ConcreteObservable(){
        super();
        setChanged();
    }

}

兩個具體的觀察者類 ConcreteObserver1,ConcreteObserver2


/**
 * 具體觀察者
 */
class ConcreteObserver1 implements Observer{

    @Override
    public void update(Observable observable, Object object){
        System.out.println("觀察者ConcreteObserver1 狀態: " + object.toString());
    }
}

class ConcreteObserver2 implements Observer{

    @Override
    public void update(Observable observable, Object object){
        System.out.println("觀察者ConcreteObserver2 狀態: " + object.toString());
    }
}

測試程式碼

public class ObserverPattern {

    public static void main(String[] args){

        ConcreteObservable observable = new ConcreteObservable();
        ConcreteObserver1 concreteObserver1 = new ConcreteObserver1();
        ConcreteObserver2 concreteObserver2 = new ConcreteObserver2();
        observable.addObserver(concreteObserver1);
        observable.addObserver(concreteObserver2);

        observable.notifyObservers("狀態發生變化,新狀態是 ABC");

    }

}

其實看著這個程式碼很容易想起之前寫的 Java——回撥機制,其實觀察者模式也是回撥的一種實現,可以看到被觀察者類 Observable 持有觀察者引用的向量 private Vector<Observer> obs;,當被觀察者狀態發生變化時,會通過這些已經註冊的觀察者,回撥觀察者的 void update(Observable o, Object arg); 方法。

示例 demo 見:https://github.com/lzx2011/java-scaffold

執行結果

觀察者ConcreteObserver2 狀態: 狀態發生變化,新狀態是 ABC
觀察者ConcreteObserver1 狀態: 狀態發生變化,新狀態是 ABC

應用場景

當一個物件改變時,同時需要改變其他物件,應考慮使用觀察者模式。  
  

相關文章