定義
觀察者模式定義了一種一對多的依賴關系,讓多個觀察者對象同時監聽某一個主題對象,這個主題對象在狀態發生變化時,會通知所有的觀察者對象,使他們能夠自動地更新自己。
類圖表示
參與者
根據依賴倒置原則可知,我們希望模塊與模塊之間不是直接耦合到一起,而是依賴於抽象,所以觀察者模式抽象出了Subject和Observer。這里的參與者分為4種對象:
1. Subject(主題或者叫通知者):抽象主題,狀態發生改變時,會通知所有與之關聯的對象。同時提供操作關聯對象的方法attach、datach(增加和刪除)
2. Observer(觀察者):抽象的觀察者接口,為所有的具體觀察者定義接口,在主題發生改變的時候更新自己
3. ConcreteSubject(具體主題):主題的具體實現,管理觀察者對象,在主題發生改變的時候,通知觀察者
4. ConcreteObserver(具體觀察者):觀察者的具體實現
通過參與者,我們可以看出這樣設計的好處,無論是具體主題還是具體觀察者,都不會直接調用對方。而是調用統一的接口,這樣維護、擴展和重用都比較方便,並且由於耦合的雙方都只依賴於抽象,而不依賴於具體,所以無論對於具體主題還是具體觀察者它們都不需要知道彼此的具體實現,各自的變化都不會影響到另一邊。這也符合迪米特原則。(但是抽象主題和抽象觀察者還是彼此依賴的)
實例
這里給出的實例源於《大話設計模式》中觀察者模式一章。背景是在一家公司中,老板會經常不在公司,所以球迷小張和股迷小趙就賄賂了前台的小姐姐,每次老板到公司視察的時候,前台小姐姐就會通知小張和小趙老板來了,它們就會放下手中的事情,繼續工作。
這里的前台小姐姐,就是具體主題,狀態就是老板來了,小張和小趙就是具體觀察者,分析出他們之間的關系,接下來我們就可以寫出觀察者模式的代碼了。
代碼實現:
/** * 主題. * * @author jialin.li * @date 2019-12-26 14:53 */ public interface Subject { /** * 增加一個觀察者 */ void attach(Observer observer); /** * 刪除一個觀察者 */ void detach(Observer observer); /** * 通知 */ void notifyObserver(); /** * 表示當前狀態 */ String getStatus(); void setStatus(String status); }
/** * 觀察者 * * @author jialin.li * @date 2019-12-26 14:54 */ public abstract class Observer { String name; Subject subject; public Observer(String name, Subject subject) { this.name = name; this.subject = subject; } public abstract void update(); }
import java.util.ArrayList; import java.util.List; /** * 前台. * * @author jialin.li * @date 2019-12-26 15:01 */ public class Receptionist implements Subject { private String status; private List<Observer> observers = new ArrayList<>(); @Override public void attach(Observer observer) { observers.add(observer); } @Override public void detach(Observer observer) { observers.remove(observer); } @Override public void notifyObserver() { for (Observer observer : observers) { observer.update(); } } @Override public String getStatus() { return status; } @Override public void setStatus(String status) { this.status = status; } }
/** * 球迷. * * @author jialin.li * @date 2019-12-26 15:05 */ public class NBAObserver extends Observer{ public NBAObserver(String name, Subject subject) { super(name, subject); } @Override public void update() { System.out.println(name+": 關閉了NBA球賽,繼續工作"); } }
/** * 股迷 * * @author jialin.li * @date 2019-12-26 15:04 */ public class StockObserver extends Observer{ public StockObserver(String name, Subject subject) { super(name, subject); } @Override public void update() { System.out.println(name+": 關閉了股票行情,繼續工作"); } }
/** * 測試方法 * * @author jialin.li * @date 2019-12-26 14:53 */ public class Main { public static void main(String[] args) { Receptionist xiaojiejie = new Receptionist(); NBAObserver xiaozhang = new NBAObserver("小張", xiaojiejie); StockObserver xiaozhao = new StockObserver("小趙", xiaojiejie); xiaojiejie.attach(xiaozhang); xiaojiejie.attach(xiaozhao); xiaojiejie.setStatus("老板來了"); System.out.println(xiaojiejie.getStatus()); xiaojiejie.notifyObserver(); } }
一個觀察者模式的就編寫完成了,運行一下吧
老板來了
小張: 關閉了NBA球賽,繼續工作
小趙: 關閉了股票行情,繼續工作
觀察者模式等於發布-訂閱模式?
不等於,在發布-訂閱模式中,消息的發送方,叫做發布者(publishers),消息的接收方叫做訂閱者(Subscriber),發送者是不會直接向訂閱者發送信息的,而是通過一個broker(也有資料叫做eventChannel)進行消息的轉發。下圖總結了這兩個模式的主要差別

總結起來就是:
1.觀察者模式中,主題和觀察者是可以感受到對方存在的,主題會對觀察者進行管理。而在發布-訂閱模式中,發布者和訂閱者不知道對方的存在,他們通過消息代理進行通訊。
2.觀察者模式中,主題和觀察者實現了松耦合,而發布-訂閱模式,發布者和訂閱者是完全解耦合的。
3.觀察者模式大多數時候是同步的,比如當事件觸發,Subject就會去調用觀察者的方法。而發布-訂閱模式大多數時候是異步的(使用消息隊列)。
4.觀察者 模式需要在單個應用程序地址空間中實現,而發布-訂閱更像交叉應用模式。
Tomcat中的觀察者模式:
Tomcat中的容器分為engine、host、context、wrapper ,容器之間是父子關系,通過組合模式將這些容器組合起來,每個容器都具有統一的生命狀態以及生命周期方法。
父容器的init()、start()等方法會調用子容器的方法(組合模式)。int()、start()等方法的調用,是因為其父容器狀態變化觸發的,所以在Tomcat中組件的生命周期被定義成一個個的狀態(LifecycleState),而狀態的轉變則是一種事件,容器會對每一種的事件設置監聽器,在監聽器中實現一些邏輯,並且對監聽器進行管理(增加或刪除),這就是一種典型的觀察者模式.
觀察者模式相關的方法:
public interface Lifecycle { public LifecycleState getState(); public LifecycleListener[] findLifecycleListeners(); public void addLifecycleListener(LifecycleListener listener); public void removeLifecycleListener(LifecycleListener listener); }