摘要:觀察者模式維護單一事件對應多個依賴該事件的對象關系發布訂閱維護多個事件主題及依賴各事件主題的對象之間的關系觀察者模式是目標對象直接觸發通知全部通知,觀察對象被迫接收通知。
觀察者模式(Observer)
觀察者模式:定義了對象間一種一對多的依賴關系,當目標對象 Subject 的狀態發生改變時,所有依賴它的對象 Observer 都會得到通知。
簡單點:女神有男朋友了,朋友圈曬個圖,甜蜜宣言 “老娘成功脫單,希望你們歡喜”。各位潛藏備胎紛紛失戀,只能安慰自己你不是唯一一個。
模式特征一個目標者對象 Subject,擁有方法:添加 / 刪除 / 通知 Observer;
多個觀察者對象 Observer,擁有方法:接收 Subject 狀態變更通知并處理;
目標對象 Subject 狀態變更時,通知所有 Observer。
Subject 添加一系列 Observer, Subject 負責維護與這些 Observer 之間的聯系,“你對我有興趣,我更新就會通知你”。
代碼實現// 目標者類 class Subject { constructor() { this.observers = []; // 觀察者列表 } // 添加 add(observer) { this.observers.push(observer); } // 刪除 remove(observer) { let idx = this.observers.findIndex(item => item === observer); idx > -1 && this.observers.splice(idx, 1); } // 通知 notify() { for (let observer of this.observers) { observer.update(); } } } // 觀察者類 class Observer { constructor(name) { this.name = name; } // 目標對象更新時觸發的回調 update() { console.log(`目標者通知我更新了,我是:${this.name}`); } } // 實例化目標者 let subject = new Subject(); // 實例化兩個觀察者 let obs1 = new Observer("前端開發者"); let obs2 = new Observer("后端開發者"); // 向目標者添加觀察者 subject.add(obs1); subject.add(obs2); // 目標者通知更新 subject.notify(); // 輸出: // 目標者通知我更新了,我是前端開發者 // 目標者通知我更新了,我是后端開發者優勢
目標者與觀察者,功能耦合度降低,專注自身功能邏輯;
觀察者被動接收更新,時間上解耦,實時接收目標者更新狀態。
不完美觀察者模式雖然實現了對象間依賴關系的低耦合,但卻不能對事件通知進行細分管控,如 “篩選通知”,“指定主題事件通知” 。
比如上面的例子,僅通知 “前端開發者” ?觀察者對象如何只接收自己需要的更新通知?上例中,兩個觀察者接收目標者狀態變更通知后,都執行了 update(),并無區分。
“00后都在追求個性的時代,我能不能有點不一樣?”,這就引出我們的下一個模式。進階版的觀察者模式。“發布訂閱模式”,部分文章對兩者是否一樣都存在爭議。
僅代表個人觀點:兩種模式很類似,但是還是略有不同,就是多了個第三者,因 JavaScript 非正規面向對象語言,且函數回調編程的特點,使得 “發布訂閱模式” 在 JavaScript 中代碼實現可等同為 “觀察模式”。
發布訂閱模式(Publisher && Subscriber)發布訂閱模式:基于一個事件(主題)通道,希望接收通知的對象 Subscriber 通過自定義事件訂閱主題,被激活事件的對象 Publisher 通過發布主題事件的方式通知各個訂閱該主題的 Subscriber 對象。
發布訂閱模式與觀察者模式的不同,“第三者” (事件中心)出現。目標對象并不直接通知觀察者,而是通過事件中心來派發通知。
代碼實現// 事件中心 let pubSub = { list: {}, subscribe: function (key, fn) { // 訂閱 if (!this.list[key]) { this.list[key] = []; } this.list[key].push(fn); }, publish: function(key, ...arg) { // 發布 for(let fn of this.list[key]) { fn.call(this, ...arg); } }, unSubscribe: function (key, fn) { // 取消訂閱 let fnList = this.list[key]; if (!fnList) return false; if (!fn) { // 不傳入指定取消的訂閱方法,則清空所有key下的訂閱 fnList && (fnList.length = 0); } else { fnList.forEach((item, index) => { if (item === fn) { fnList.splice(index, 1); } }) } } } // 訂閱 pubSub.subscribe("onwork", time => { console.log(`上班了:${time}`); }) pubSub.subscribe("offwork", time => { console.log(`下班了:${time}`); }) pubSub.subscribe("launch", time => { console.log(`吃飯了:${time}`); }) // 發布 pubSub.publish("offwork", "18:00:00"); pubSub.publish("launch", "12:00:00"); // 取消訂閱 pubSub.unSubscribe("onwork");
發布訂閱模式中,訂閱者各自實現不同的邏輯,且只接收自己對應的事件通知。實現你想要的 “不一樣”。
DOM 事件監聽也是 “發布訂閱模式” 的應用:
let loginBtn = document.getElementById("#loginBtn"); // 監聽回調函數(指定事件) function notifyClick() { console.log("我被點擊了"); } // 添加事件監聽 loginBtn.addEventListener("click", notifyClick); // 觸發點擊, 事件中心派發指定事件 loginBtn.click(); // 取消事件監聽 loginBtn.removeEventListener("click", notifyClick);
發布訂閱的通知順序:
先訂閱后發布時才通知(常規)
訂閱后可獲取過往以后的發布通知 (QQ離線消息,上線后獲取之前的信息)
流行庫的應用jQuery 的 on 和 trigger,$.callback();
Vue 的雙向數據綁定;
Vue 的父子組件通信 $on/$emit
jQuery 的 $.Callback() 更像是觀察者模式的應用,不能更細粒度管控。
function notifyHim(value) { console.log("He say " + value); } function notifyHer(value) { console.log("She say " + value); } $cb = $.Callbacks(); // 聲明一個回調容器:訂閱列表 $cb.add(notifyHim); // 向回調列表添加回調:訂閱 $cb.add(notifyHer); // 向回調列表添加回調:訂閱 $cb.fire("help"); // 調用所有回調: 發布
利用 Object.defineProperty() 對數據進行劫持,設置一個監聽器 Observer,用來監聽數據對象的屬性,如果屬性上發生變化了,交由 Dep 通知訂閱者 Watcher 去更新數據,最后指令解析器 Compile 解析對應的指令,進而會執行對應的更新函數,從而更新視圖,實現了雙向綁定。
Observer (數據劫持)
Dep (發布訂閱)
Watcher (數據監聽)
Compile (模版編譯)
關于 Vue 雙向數據綁定原理,可自行參考其它文章,或推薦本篇 《 vue雙向數據綁定原理》。
Vue源碼傳送門
Vue 中,父組件通過 props 向子組件傳遞數據(自上而下的單向數據流)。父子組件之間的通信,通過自定義事件即 $on , $emit 來實現(子組件 $emit,父組件 $on)。
原理其實就是 $emit 發布更新通知,而 $on 訂閱接收通知。Vue 中還實現了 $once(一次監聽),$off(取消訂閱)。
// 訂閱 vm.$on("test", function (msg) { console.log(msg) }) // 發布 vm.$emit("test", "hi")
Vue源碼傳送門
Vue文檔傳送門
優勢對象間功能解耦,弱化對象間的引用關系;
更細粒度地管控,分發指定訂閱主題通知
不完美對間間解耦后,代碼閱讀不夠直觀,不易維護;
額外對象創建,消耗時間和內存(很多設計模式的通病)
觀察者模式 VS 發布訂閱模式 類似點都是定義一個一對多的依賴關系,有關狀態發生變更時執行相應的通知。
區別點發布訂閱模式更靈活,是進階版的觀察者模式,指定對應分發。
觀察者模式維護單一事件對應多個依賴該事件的對象關系;
發布訂閱維護多個事件(主題)及依賴各事件(主題)的對象之間的關系;
觀察者模式是目標對象直接觸發通知(全部通知),觀察對象被迫接收通知。發布訂閱模式多了個中間層(事件中心),由其去管理通知廣播(只通知訂閱對應事件的對象);
觀察者模式對象間依賴關系較強,發布訂閱模式中對象之間實現真正的解耦。
對象屬性數據攔截方式:Object.defineProperty() 屬性描述符;
ES6 Class set ;
ES6 Proxy 代理;
參考文章:
談談觀察者模式和發布訂閱模式
原生JavaScript實現觀察者模式
觀察者模式 vs 發布訂閱模式
vue雙向數據綁定原理
本文首發Github,期待Star!
https://github.com/ZengLingYong/blog
作者:以樂之名
本文原創,有不當的地方歡迎指出。轉載請指明出處。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/105523.html
摘要:設計模式與開發實踐讀書筆記。發布訂閱模式又叫觀察者模式,它定義了對象之間的一種一對多的依賴關系。附設計模式之發布訂閱模式觀察者模式數據結構和算法系列棧隊列優先隊列循環隊列設計模式系列設計模式之策略模式 《JavaScript設計模式與開發實踐》讀書筆記。 發布-訂閱模式又叫觀察者模式,它定義了對象之間的一種一對多的依賴關系。當一個對象的狀態發生改變時,所有依賴它的對象都將得到通知。 例...
摘要:發布訂閱模式訂閱者把自己想訂閱的事件注冊到調度中心,當發布者發布該事件到調度中心,也就是該事件觸發時,由調度中心統一調度訂閱者注冊到調度中心的處理代碼。 發布-訂閱模式,看似陌生,其實不然。工作中經常會用到,例如 Node.js EventEmitter 中的 on 和 emit 方法;Vue 中的 $on 和 $emit 方法。他們都使用了發布-訂閱模式,讓開發變得更加高效方便。 一...
摘要:設計模式與開發實踐讀書筆記。看此文章前,建議先看設計模式之發布訂閱模式觀察者模式在中,已經介紹了什么是發布訂閱模式,同時,也實現了發布訂閱模式。 《JavaScript設計模式與開發實踐》讀書筆記。 看此文章前,建議先看JavaScript設計模式之發布-訂閱模式(觀察者模式)-Part1 在Part1中,已經介紹了什么是發布-訂閱模式,同時,也實現了發布-訂閱模式。但是,就Part1...
閱讀 1551·2021-11-25 09:43
閱讀 2332·2019-08-30 15:55
閱讀 1465·2019-08-30 13:08
閱讀 2666·2019-08-29 10:59
閱讀 810·2019-08-29 10:54
閱讀 1551·2019-08-26 18:26
閱讀 2545·2019-08-26 13:44
閱讀 2653·2019-08-23 18:36