Javascript設(shè)計(jì)模式理論與實(shí)戰(zhàn):觀察者模式
觀察者模式主要應(yīng)用于對(duì)象之間一對(duì)多的依賴關(guān)系,當(dāng)一個(gè)對(duì)象發(fā)生改變時(shí),多個(gè)對(duì)該對(duì)象有依賴的其他對(duì)象也會(huì)跟著做出相應(yīng)改變,這就非常適合用觀察者模式來(lái)實(shí)現(xiàn)。使用觀察者模式可以根據(jù)需要增加或刪除對(duì)象,解決一對(duì)多對(duì)象間的耦合關(guān)系,使程序更易于擴(kuò)展和維護(hù)。
基礎(chǔ)知識(shí)
觀察者模式定義了對(duì)象間的一種一對(duì)多依賴關(guān)系,每當(dāng)一個(gè)對(duì)象發(fā)生改變時(shí),其相關(guān)依賴對(duì)象皆得到通知并被進(jìn)行相應(yīng)的改變。觀察者模式又叫做發(fā)布-訂閱 模式。生活中有很多類似的關(guān)系,比如微信公眾號(hào)訂閱,多個(gè)讀者訂閱一個(gè)微信公眾號(hào),一旦公眾號(hào)有更新,多個(gè)讀者都會(huì)收到更新,而這種情況在應(yīng)用程序中也非 常常見(jiàn),js綁定各種事件本質(zhì)上就是觀察者模式的實(shí)現(xiàn)。
觀察者模式是一個(gè)非常有用的設(shè)計(jì)模式,它主要有兩個(gè)角色組成:
(1)目標(biāo)對(duì)象:作為一對(duì)多關(guān)系中的一,可以用來(lái)管理觀察者的增加和刪除
(2)觀察者對(duì)象:觀察目標(biāo)對(duì)象,一旦目標(biāo)發(fā)生改變則做出相應(yīng)的反應(yīng)
觀察者模式的實(shí)現(xiàn)
基本示例
在Web開(kāi)發(fā)中,我們經(jīng)常遇到這種情況,ajax請(qǐng)求數(shù)據(jù)后,要同時(shí)更新數(shù)據(jù)到頁(yè)面的不同部分中,這種情況我們可以最直接的在ajax的回調(diào)中更新 頁(yè)面,但是如果要更新的位置很多,我們就要去修改回調(diào)函數(shù),這樣代碼的維護(hù)性和擴(kuò)張性不高,這種情況下,我們就可以用觀察者模式來(lái)實(shí)現(xiàn)。
首先,需要一個(gè)最基本的目標(biāo)對(duì)象,我們定義如下:
- function Subject() {
- this.observers = [];
- }
- Subject.prototype = {
- constructor: Subject,
- subscribe: function (fn) {
- this.observers.push(fn);
- return this;
- },
- unsubscribe: function (fn) {
- this.observers = this.observers.filter(function (item) {
- if (item !== fn) {
- return item;
- }
- });
- return this;
- },
- fire: function (data, context) {
- this.observers.forEach(function (item) {
- item.call(context, data);
- });
- return this;
- }
- };
目標(biāo)對(duì)象Subject中有一個(gè)數(shù)組,這個(gè)數(shù)組保存觀察者列表,而目標(biāo)對(duì)象提供三個(gè)方法:觀察對(duì)象,取消觀察對(duì)象,觸發(fā)對(duì)象更新。
我們通過(guò)subscribe方法增加觀察者,保存到observers數(shù)組中,如果有需要可以通過(guò)unsubscribe方法取消訂閱,然后更新數(shù)據(jù)時(shí)調(diào)用fire方法觸發(fā),從而通知各個(gè)觀察者進(jìn)行相應(yīng)處理。
假設(shè)我們頁(yè)面有一個(gè)主視圖和一個(gè)側(cè)視圖,兩個(gè)視圖都要進(jìn)行相應(yīng)的修改,我們可以定義兩個(gè)對(duì)象如下:
- function SideView() { }
- SideView.prototype.render = function (data) {
- console.log("Side data:" + data);
- }
- function MainView() { }
- MainView.prototype.render = function (data) {
- console.log("MainView data:" + data);
- }
上面代碼定義了兩個(gè)對(duì)象,分別為側(cè)視圖和主視圖,兩個(gè)對(duì)象都有相應(yīng)的渲染頁(yè)面的方法render,然后我們將兩個(gè)方法添加到觀察者列表中。
- var subject = new Subject();
- var sideView = new SideView();
- var mainView = new MainView();
- subject.subscribe(sideView.render)
- subject.subscribe(mainView.render);
- subject.fire("test");
通過(guò)調(diào)用fire方法,傳入“test”,從而觸發(fā)兩個(gè)render函數(shù)。從這段代碼中,我們可以很輕松地通過(guò)subscribe來(lái)添加觀察者對(duì)象,而不必每次都去修改fire方法。
jQuery中的觀察者模式
jQuery中實(shí)現(xiàn)觀察者模式非常方便,簡(jiǎn)短的幾句代碼就可以實(shí)現(xiàn)
- (function ($) {
- var obj = $({});
- $.subscribe = function () {
- obj.on.apply(obj, arguments);
- }
- $.unsubscribe = function () {
- obj.off.apply(obj, arguments);
- }
- $.fire = function () {
- obj.trigger.apply(obj, arguments);
- }
- })(jQuery);
在jQuery中,通過(guò)on方法來(lái)綁定事件,off來(lái)移除事件,trigger來(lái)觸發(fā)事件,本質(zhì)上就是一種觀察者模式。上面代碼中,我們通過(guò)一個(gè)obj對(duì)象來(lái)保存觀察者對(duì)象,我們只要像平時(shí)綁定事件一樣使用就可以,如下:
- $.subscribe("render", function () {
- console.log("test");
- })
- $.subscribe("render", function () {
- console.log("test2");
- })
- $.fire("render");
這段代碼分別輸出test和test2.我們綁定了兩個(gè)處理函數(shù)到render上,然后通過(guò)fire觸發(fā)render事件,這就實(shí)現(xiàn)了觀察者模式一對(duì)多依賴的特點(diǎn)。
總結(jié)
觀察者模式是一種很常用的設(shè)計(jì)模式,因?yàn)槲覀兊膽?yīng)用程序中涉及到依賴關(guān)系的非常多。常見(jiàn)的比如消息通知,向用戶發(fā)送一個(gè)消息需要同時(shí)通知到站內(nèi)信, 郵件,短信等多種消息,這種一對(duì)多的情況非常適合使用觀察者模式來(lái)實(shí)現(xiàn)。使用觀察者模式的關(guān)鍵是在于理清目標(biāo)對(duì)象和觀察者對(duì)象,目標(biāo)對(duì)象通過(guò)一個(gè)數(shù)組對(duì)觀 察者對(duì)象進(jìn)行管理,更新數(shù)據(jù)的時(shí)候再循環(huán)調(diào)用觀察者對(duì)象,從而實(shí)現(xiàn)觀察者模式。