1、介面回撥
在學習觀察者模式之前,我們先了解一下介面回撥的概念。兩者的原理有些類似,理解了介面回撥就非常容易理解觀察者模式。
所謂介面回撥一般應用的場合是:你不知道這個方法什麼時候回返回,但是你希望在該方法結束的時候拿到方法執行的結果。常見的,比如一個方法內部開啟了執行緒,而我們希望線上程執行結束的時候拿到執行緒的執行結果。
在下面的例子中,方法 cal()
定義了一個區域性變數 i,隨後開啟了一個執行緒,並線上程執行的時候修改了 i 的值。我們希望線上程執行完畢的時候達到 i 的執行結果。
public int cal() {
int i;
new Thread(new Runnable() {
Thread.sleep(3000); // throw ... ignore it
i++;
}).start();
return i;
}
複製程式碼
使用 return 肯定不行,因為方法結束時,執行緒可能還沒有結束,那麼 return 返回的結果是無法被預料到的,可能是執行緒執行完畢之後的,也可能是沒有被執行緒修改就返回了的。
我們可以使用介面回撥解決這個問題。我們可以在呼叫該方法的時候傳入一個介面 Callback 的例項。線上程中執行完所有的邏輯之後,我們使用該介面的 call()
方法將 i 回撥出來:
public void cal(Callback callback) {
int i;
new Thread(new Runnable() {
Thread.sleep(3000); // throw ... ignore it
i++;
if (callback != null) {
callback.call(i); // 1
}
}).start();
}
複製程式碼
當然,介面回撥更像是將我的 call()
方法注入到了上述程式碼中的 1 處。這近似於所謂的函式程式設計的概念,就是將介面作為一個函式注入到了方法中。
介面回撥有非常豐富的應用場景,典型的是一些非同步的場景,比如 “監聽” 一個按鈕的執行結果等等。
2、觀察者模式
好了,瞭解了上面的介面回撥之後,我們來看下觀察者模式。首先,我們來了解一下觀察者設計模式中的一些概念。
如果使用 1 中的的例子來做類比的話,那麼 i 就是我們的主題(Subject),我們進行介面回撥的類(將實現的 Callback 傳入到 cal 方法時所處的類)叫做觀察者(Subscriber)。
上面的是觀察者模式的UML模型。這裡的 Subject
介面就是主題的介面,而 ConcreteSubkect
是它的具體實現類,即具體的 主題。Observer
介面是觀察者的介面,ConcreteObserver
是具體的觀察者。一個主題往往會通過列表來維護一系列的觀察者,然後當主題發生變化的時候會便利這個列表來通知所有的觀察者。所以,這裡的 Subject
介面定義了三個方法,從上到下依次用於向主題中新增觀察者,從主題中移除觀察者以及通知所有的觀察者主題的更新。
下面我們給出一份最簡單的觀察者設計模式的程式碼:
1.首先是主題的定義類:
public class ConcreteSubject implements Subject {
// 通過佇列維護觀察者列表
private List<Observer> observers = new LinkedList<>();
// 註冊一個觀察者
@Override
public void registerObserver(Observer o) {
observers.add(o);
}
// 移除一個觀察者
@Override
public void removeObserver(Observer o) {
int i = observers.indexOf(o);
if (i >= 0) {
observers.remove(o);
}
}
// 通知所有觀察者主題的更新
@Override
public void notifyObservers() {
for (Observer o : observers) {
o.method();
}
}
}
複製程式碼
2.接下來是觀察者的定義類:
public class ConcreteObserver implements Observer {
// 該觀察者訂閱的主題
private Subject subject;
public ConcreteObserver(Subject subject) {
this.subject = subject;
// 將當前觀察者新增到主題訂閱列表中
subject.registerObserver(this);
}
// 當主題發生變化的時候,主題會遍歷觀察者列表並通過呼叫該方法來通知觀察者
@Override
public void method() {
// ...
}
}
複製程式碼
上面就是觀察者的基本的實現方式,這裡的實現邏輯比較簡單,但當你學習更加複雜的觀察者設計的之前理解它是很有必要的。
3、總結
觀察者與被觀察者之間是屬於輕度的關聯關係,並且是抽象耦合的,這樣,對於兩者來說都比較容易進行擴充套件。
觀察者模式是一種常用的觸發機制,它形成一條觸發鏈,依次對各個觀察者的方法進行處理。但同時,這也算是觀察者模式一個缺點,由於是鏈式觸發,當觀察者比較多的時候,效能問題是比較令人擔憂的。並且,在鏈式結構中,比較容易出現迴圈引用的錯誤,造成系統假死。並且因為觀察者列表中維護了一份觀察者的引用,當它們沒有被及時地釋放的話,可能會引起記憶體洩漏。