我的了解
很久之前有個老師給我們講觀察者模式時,舉了一個例子:
媽媽照顧嬰兒,他們在不同的房間,嬰兒在嬰兒房間A,媽媽在書房M,
為了保證嬰兒睡醒之後能及時得到照顧,媽媽每隔10分鐘就去房間A瞧瞧,看看嬰兒是否醒了.
媽媽為了防止忘記,給自己定了一個鬧鐘,每隔10分鐘響一次.
while (10分鐘之後) {
watchBaby();//檢查嬰兒是否睡醒
}
一段時間之後,媽媽覺得很累,而且效率很低,因為很多次媽媽去照看嬰兒時,發現嬰兒在睡覺啥事也沒有.
後來,媽媽發現了一個好方法,在嬰兒身上寄一個鈴铛,嬰兒隻要一動,鈴铛就會響,媽媽就能聽到.
這樣媽媽就可以安心工作了,隻要聽到鈴铛響就跑過去照看下.
關系圖:

其實上面的例子涉及到兩種模式:
- 媽媽每隔10分鐘主動去檢視 是輪詢模式;
- 媽媽不用主動去檢查,嬰兒醒了,媽媽就會聽到鈴铛, 是觀察者模式
那種模式效率更高呢?
一般情況下,觀察者模式效率更高,
輪詢,每隔一段時間去檢視,浪費了資源.
觀察者模式的角色
有如下角色:觀察者,被觀察者,事件,
有如下操作:注冊,通知
觀察者
案例中的媽媽就是觀察者,她觀察嬰兒,看它醒了沒有,有什麼需求
被觀察者
嬰兒是被觀察者,是被關注的對象.
一旦它發生什麼事件,觀察者就會有所響應
事件
觀察者關注的事件就是鈴铛是否響,鈴铛一響說明嬰兒醒了.嬰兒一醒,媽媽就要去照顧,比如喂奶等.
注冊(訂閱)
媽媽在嬰兒身上挂一個鈴铛,就是注冊事件.
一旦該事件發生,觀察者就會有所響應.一旦嬰兒醒了,媽媽就會聽到鈴铛聲(收到通知)
通知
發生事件(嬰兒醒了)時,被觀察者(嬰兒)會主動通知觀察者(媽媽)
java 類圖
示例
接口
觀察者:
/**
* 接口描述: 觀察者. <br />
*
* @author hanjun.hw
* @since 2018/11/6
*/
public interface Observer {
/**
* 觀察者收到通知後,觀察者進行響應
*
* @param observable
*/
void update(Observable observable);
}
被觀察者:
public abstract class Observable {
/**
* 訂閱觀察者
*
* @param observer
*/
public abstract void register(Observer observer);
/**
* 通知觀察者
*/
protected abstract void notifyAllObservers();
/**
* 觀察者真正感興趣的事件
*/
public void wakeUp() {
bellRinging();
notifyAllObservers();
}
/**
* 觀察者觀察的事件
*/
protected abstract void bellRinging();
}
觀察者實作
/**
* 類描述: 具體觀察者:媽媽. <br />
*
* @author hanjun.hw
* @since 2018/11/6
*/
public class Mother implements Observer {
@Override
public void update(Observable observable) {
System.out.println("嬰兒醒了,去照看 :" + observable);
}
}
被觀察者實作
/**
* 類描述: 被觀察者:嬰兒. <br />
*
* @author hanjun.hw
* @since 2018/11/6
*/
public class Baby extends Observable {
private HashSet<Observer> observers = new HashSet<>();
/**
* 把鈴铛系在嬰兒身上
* @param observer
*/
@Override
public void register(Observer observer) {
observers.add(observer);
}
/**
* 媽媽聽到鈴铛聲,做出響應
*/
@Override
protected void notifyAllObservers() {
observers.forEach(o -> o.update(this));
}
@Override
protected void bellRinging() {
System.out.println("嬰兒睡醒了 ,鈴铛響了");
}
}
測試
public static void main(String[] args) {
Baby concreteObservable = new Baby();
Mother concreteObserver = new Mother();
concreteObservable.register(concreteObserver);
concreteObservable.wakeUp();
}
應用場景
Google eventbus
組成部分
事件(什麼類型的事件)---對應鈴铛響了
事件監聽器,即事件處理程式(響應)----對應媽媽
注冊事件監聽器(register);----對應往嬰兒身上挂鈴铛
觸發事件(trigger/post);---嬰兒醒了,搖動了鈴铛
執行個體
事件,可以是任何自定義對象
/**
* Created by whuanghkl on 17/6/22.<br />
* 自定義事件
*/
public class AccessLoggerEvent {
}
事件監聽器
/**
* Created by whuanghkl on 17/6/22.<br />
* 事件監聽器
*/
@Component
public class AccessLoggerListener {
@Resource
private EventBus eventBus;
/**
* 訂閱
*/
@PostConstruct
public void init() {
eventBus.register(this);
}
@Subscribe
public void logEvent(AccessLoggerEvent event) {
System.out.println("logEvent");
}
}
事件監聽器自己注冊到eventBus
在控制器中觸發事件
AccessLoggerEvent accessLoggerEvent = new AccessLoggerEvent();
eventBus.post(accessLoggerEvent);
嬰兒是什麼? 是上述代碼(eventBus.post)所在類
java swing 按鈕單擊/文本框回車
JButton cancelButton = new JButton("Cancel");
cancelButton.setActionCommand("Cancel");
cancelButton.addActionListener(new ActionListener() {//ActionListener 是觀察者
@Override
public void actionPerformed(ActionEvent e) {//觀察者的響應
LoginDialog.this.dispose();
}
});
涉及的元素:
按鈕 -----對應嬰兒
按鈕增加事件 ,即調用 addActionListener() ---對應往嬰兒身上挂鈴铛
使用者點選按鈕;---嬰兒醒了,搖動了鈴铛
執行 addActionListener()
執行 addActionListener() 添加的ActionListener ---對應 媽媽聽到鈴聲之後去照看
媽媽是什麼? 是ActionListener 對象
react 資料的雙向綁定
參考:
https://juejin.im/post/59f2e9b16fb9a04529360146mq 消息隊列訂閱
多路複用機制epoll 也使用了觀察者模式,
與之相對的select:整個socket集合會被周遊一次,比如集合裡面有1024個socket,即便隻有一個socket有資料可讀,也周遊所有的1024個socket,這個是觀察者模式的反例.
epoll的實作邏輯就是觀察者模式的思想:
協定資料包到達網卡并被排入socket的接收隊列。
睡眠在socket的睡眠隊列wait_entry被喚醒,wait_entry_sk的回調函數epoll_callback_sk被執行。
epoll_callback_sk将目前socket插入epoll的ready_list中。
這樣就不用周遊整個socket隊列了,而隻需要周遊ready_list
觀察者模式的作用
解耦,比如 Google eventbus
提高響應速度(相對于輪詢)
一般是輔助的容易變化的業務
觀察者模式和mq(釋出訂閱) 的差別
mq:削峰,被觀察者和觀察者不用感覺對方的存在;
觀察者模式中,觀察者和被觀察者可以感覺到對方的存在
思考
設計模式是經驗的總結,是一套被反複使用的、多數人知曉的、經過分類編目的、代碼設計經驗的總結;
觀察者模式其實在日常生活中也有展現,
比如銀行業務 有短信通知,如果我綁定了手機号(訂閱),那麼銀行卡有交易時(關注的事件發生),就會自動給我們發短信(響應)
既能把觀察者模式了解成為一種思想,也能了解為解耦的一種政策