2019年3月25日11:01:22
觀察者模式( observer pattern)
寫在前面的話
正值金三銀四跳槽季,設計模式也是常問的問題之一。本人在3月2日的一次面試的二面中,問到設計模式,問到了觀察者模式,而且要求寫了偽代碼。當時我腦子里就第一個想到的就是《大話設計模式》里面的一個例子,就是員工集體開小差,前台妹妹負責在老板回來時通知所有人。當時回答得結結巴巴,寫得代碼勉勉強強,驚喜的是二面過了。歸,溫習之。
定義
世界上有這么一天,當你來到他們的世界,他們成為你終生的訂閱者,隨你快樂而快樂,隨你憂愁而憂愁。
觀察者模式定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態發生變化時,會通知所有觀察者對象,使它們能夠自動更新自己。————《設計模式:可復用面向對象軟件的基礎》
觀察者模式是一種對象行為型模式。
使用場景
當一個對象的改變(名詞)需要改變(動詞)其他對象的時候。
觀察者模式可以解決什么問題:
-
1、應該定義對象間一對多的依賴關系,而不使對象緊密耦合。(達到依賴關系,又不緊耦合)
-
2、應該確保一個對象改變時,無限制的依賴對象自動更新。
-
3、應該一個對象可以通知無限制的其他對象。
第2種情況,消息中間件就是實現之一,當一個生產者發送消息過來,無限制的消費者拿到消息自動更新自己。
第3中情況,發布-訂閱推模式,微信公眾號應該就是其中有代表性的,當有內容更新,主動通知訂閱者。
角色
抽象主題(Subject): 定義了被觀察者常用的方法,訂閱(attach)、取消訂閱(detach)和通知(notify)功能
具體主題(ConcreteSubject):實現抽象主題定義的方法,通過attach和detach方法維護一個觀察者的集合,當自己維護的狀態(state)改變時通知(notify)所有觀察者
抽象觀察者(Observer):定義更新自己的方法
具體觀察者(ConcreteObserver):實現更新自己的的方法
圖示
觀察者類圖:
觀察者序列圖:
代碼示例
代碼示例就使用上面說過的那個例子,員工集體開小差,前台小妹負責在老板回來的時候通知所有人。
首先,前台妹妹是具體主題角色,員工是具體觀察者角色。其次,前台妹妹維護着觀察者集合,在老板回來了這個狀態上通知所有觀察者;觀察者在前台妹妹通知之后執行自己的更新方法,該干嘛干嘛。
抽象主題:
public interface Observable {
public void attach(Observer observer);
public void detach(Observer observer);
public void notifyObservers();
}
前台妹妹(具體主題即被觀察者):
public class Receptionist implements Observable {
private String state = "老板出去了";
private ArrayList<Observer> observers = new ArrayList<>();
public String getState() {
return state;
}
public void setState(String state) {
this.state = state;
System.out.println(state);
}
@Override
public void attach(Observer observer) {
observers.add(observer);
}
@Override
public void detach(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
observers.forEach(observer -> observer.update());
}
}
看NBA員工觀察者:
public class NBAObserver implements Observer{
@Override
public void update() {
System.out.println("正在看NBA直播的關掉NBA直播,工作");
}
}
炒股員工觀察者:
public class StockObserver implements Observer {
@Override
public void update() {
System.out.println("正在炒股的關閉股市面板,工作");
}
}
觀察者模式測試類:
public class ObserverTest {
public static void main(String[] args) {
Receptionist mm = new Receptionist();
Observer nba = new NBAObserver();
Observer stock = new StockObserver();
mm.attach(nba);
mm.attach(stock);
// mm看到老板回來了
mm.setState("老板回來了");
// 通知已經訂閱的員工
mm.notifyObservers();
}
}
測試結果:
筆記:明明是前台妹妹在觀察老板是否回來了,為什么前台妹妹是被觀察者呢?
“老板回來了”這個狀態是前台妹妹的內部狀態,觀察者模式是對象之間的關系,看NBA的和炒股的觀察者監聽前台妹妹的狀態,發生變化時更新自己。
Java類庫中的觀察者模式
在java類庫中有java.util.Observer和java.util.Observable作為觀察者和被觀察者,因為作用有限,在Java 9中已經棄用(deprecated)。
優點
-
滿足了當一個對象的改變需要改變其他對象這個條件的前提下,實現了松耦合。
-
符合開閉原則,繼承抽象主題添加被觀察者,繼承抽象觀察者添加觀察者。
缺點
-
如果一個觀察目標對象有很多直接和間接的觀察者的話,將所有的觀察者都通知到會花費很多時間。
-
如果在觀察者和觀察目標之間有循環依賴的話,觀察目標會觸發它們之間進行循環調用,可能導致系統崩潰。
-
觀察者模式沒有相應的機制讓觀察者知道所觀察的目標對象是怎么發生變化的,而僅僅只是知道觀察目標發生了變化。
總結
觀察者模式定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某一個主題對象。這個主題對象在狀態發生變化時,會通知所有觀察者對象,使它們能夠自動更新自己。
參考
《大話設計模式》
完
2019年3月26日15:02:09