如何使用觀察者模式

如何使用觀察者模式

觀察者模式是很普遍的一種設計模式, 有很多第三方套件提供好用的方法來處理訊息傳遞, 本質上就是觀察者模式。

Java 本身就提供觀察者模式的 API 讓開發者使用, 因此, 如果你懶得自己寫觀察者模式或者不想使用第三方套件, 就可以使用原味內建的, 以下就是觀察者模式的範例。

完整程式碼

GitHub

情境

在手機 APP 內, A 畫面要跟 B 畫面溝通, 因此需要傳送一些數據給 B 畫面, 透過Observer Pattern, 就可以很輕鬆的達成這樣的需求。

程式碼說明

舉個例子來說。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        CountObservable count = new CountObservable();
        count.addObserver(new CountObserver());
        count.setCount(1);
        count.setCount(2);
        count.setCount(3);
    }
    public static class CountObservable extends Observable {
        public void setCount(int count){
            setChanged();//一定要標記這個方法 不然無法不會發出通知
//          notifyObservers();//通知所有的觀察者
            notifyObservers(count);
        }
    }

    public class CountObserver implements Observer {

        @Override
        public void update(Observable o, Object arg) {
            System.out.println("count = " + arg);
        }
    }
}

由上方程式碼可以看到我們新增了兩個類別, 一個叫做 CountObservable 另外一個叫做 CountObserver, 都分別為 Observable 以及 Observer 的子類別, 如此一來就可以透過這兩個類別的連結來達到我們傳送訊息的目的了。

首先, 建立好 CountObservable 類別, 這個類別代表著你是被觀察的對象, 因為 Observable 我們可以視為可以觀察的, 代表著有人需要知道這個類別的變化。

當 CountObservable 繼承了 Observable, 代表著這個類別可以被觀察, 所以一旦發生了事件需要通知註冊的觀察者時, 就可以透過內建的涵式幫忙發出通知。

public static class CountObservable extends Observable {
    public void setCount(int count){
        setChanged();//一定要標記這個方法 不然無法不會發出通知
//      notifyObservers();//通知所有的觀察者
        notifyObservers(count);
    }
}

其中我們看到 setCount 方法內去呼叫 setChanged 這個方法, 為什麼? 想知道為什麼就要進去看原始碼。

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

原始碼顯示將一個 boolean 變數變成 true 的狀態, 再繼續往下追可以看到 notifyObservers 方法內有一段程式碼。

synchronized (this) {
    if (!hasChanged())
        return;
}

往 hasChanged 方法追蹤可以看到以下程式碼。

public synchronized boolean hasChanged() {
    return changed;
}

所以我們可以清楚明白為什麼要通知訊息有變化的時候, 必須先設定 setChanged 方法了, 因為它被拿來判斷資料是否有變動。

那如果要通知所有觀察者資料有變動, 則可以透過以下兩種方法來處理。

notifyObservers();
notifyObservers(count);

上面兩種方法在內部都會呼叫到同一個方法, 只是單純傳入的參數不同而已。

void notifyObservers(null);
void notifyObservers(Object arg)

如果你有傳入參數, 那麼就會在 CountObserver 的子類別內, 所覆寫的 update 被傳入, 所以接下來會說明怎麼處理我們的 CountObserver 類別, 接著又建立好一個類別叫做 CountObserver, 它用來接收觀察的對象如果產生變化以後, 我該進行那些處理? 首先, 先覆寫 update 這個方法。

@Override
public void update(Observable o, Object arg) {
    System.out.println("count = " + arg);
}

等到如果有收到訊息通知的時候, 這個方法就會被 call, 因此我們在這邊將訊息印出來, 要拿到傳入的值則是來自於第二個參數, 這個參數如果是物件則必須自己轉型, 稍微說明一下第一個參數, 第一個參數就是被觀察者類別的物件自己, 你可以在被觀察者的類別設定方法, 就可以透過這個物件去呼叫它。

當我們將兩個類別都設置好了以後, 就可以進行連結了。

CountObservable count = new CountObservable();
count.addObserver(new CountObserver());
count.setCount(1);
count.setCount(2);
count.setCount(3);

最終你就可以看到傳進 CountObserver 的 update 值如下:

count = 1
count = 2
count = 3