JS設計模式——觀察者模式(通俗易懂)

Observer模式的概念

Observer模式是行爲模式之一,它的做用是當一個對象的狀態發生變化時,可以自動通知其餘關聯對象,自動刷新對象狀態。html

Observer模式提供給關聯對象一種同步通訊的手段,使某個對象與依賴它的其餘對象之間保持狀態同步。html5

Observer模式的角色

Subject(被觀察者)面試

被觀察的對象。當須要被觀察的狀態發生變化時,須要通知隊列中全部觀察者對象。Subject須要維持(添加,刪除,通知)一個觀察者對象的隊列列表。app

ConcreteSubjectdom

被觀察者的具體實現。包含一些基本的屬性狀態及其餘操做。函數

Observer(觀察者)this

接口或抽象類。當Subject的狀態發生變化時,Observer對象將經過一個callback函數獲得通知。spa

ConcreteObservercode

觀察者的具體實現:獲得通知後將完成一些具體的業務邏輯處理。server

觀察者模式( 又叫發佈者-訂閱者模式 )應該是最經常使用的模式之一. 在不少語言裏都獲得大量應用. 包括咱們平時接觸的dom事件. 也是js和dom之間實現的一種觀察者模式.

div.onclick = function click (){
  alert ("click")
}

只要訂閱了div的click事件. 當點擊div的時候, function click就會被觸發.

那麼到底什麼是觀察者模式呢. 先看看生活中的觀察者模式。

好萊塢有句名言. 「不要給我打電話, 我會給你打電話」. 這句話就解釋了一個觀察者模式的前因後果。 其中「我」是發佈者, 「你」是訂閱者。

再舉個例子,我來公司面試的時候,完事以後每一個面試官都會對我說:「請留下你的聯繫方式, 有消息咱們會通知你」。

在這裏「我」是訂閱者, 面試官是發佈者。因此我不用天天或者每小時都去詢問面試結果, 通信的主動權掌握在了面試官手上。而我只須要提供一個聯繫方式。

觀察者模式能夠很好的實現2個模塊之間的解耦。 假如我正在一個團隊裏開發一個html5遊戲. 當遊戲開始的時候,須要加載一些圖片素材。

加載好這些圖片以後開始才執行遊戲邏輯. 假設這是一個須要多人合做的項目. 我完成了Gamer和Map模塊, 而個人同事A寫了一個圖片加載器loadImage.

loadImage的代碼以下

loadImage(imgAry, function () {
  Map.init();
  Gamer.init();
})

當圖片加載好以後, 再渲染地圖, 執行遊戲邏輯. 嗯, 這個程序運行良好. 忽然有一天, 我想起應該給遊戲加上聲音功能. 我應該讓圖片加載器添上一行代碼.

loadImage(imgAry, function () {
  Map.init();
  Gamer.init();
  Sount.init();
})

但是寫這個模塊的同事A去了外地旅遊. 因而我打電話給他, 喂. 你的loadImage函數在哪, 我能不能改一下, 改了以後有沒有反作用.

如你所想, 各類不淡定的事發生了. 若是當初咱們能這樣寫呢:

loadImage.listen("ready", function () {
  Map.init();
})
loadImage.listen("ready", function () {
  Gamer.init();
})
loadImage.listen("ready", function () {
  Sount.init();
})

 loadImage完成以後, 它根本不關心未來會發生什麼, 由於它的工做已經完成了. 接下來它只要發佈一個信號

loadImage.trigger( "ready" );

 

下面是對觀察者模式的實現

/**
 * 發佈訂閱模式(觀察者模式)
 * handles: 事件處理函數集合
 * on: 訂閱事件
 * emit: 發佈事件
 * off: 刪除事件
**/

class PubSub {
  constructor() {
    this.handles = {};
  }

  // 訂閱事件
  on(eventType, handle) {
    if (!this.handles.hasOwnProperty(eventType)) {
      this.handles[eventType] = [];
    }
    if (typeof handle == 'function') {
      this.handles[eventType].push(handle);
    } else {
      throw new Error('缺乏回調函數');
    }
    return this;
  }

  // 發佈事件
  emit(eventType, ...args) {
    if (this.handles.hasOwnProperty(eventType)) {
      this.handles[eventType].forEach((item, key, arr) => {
        item.apply(null, args);
      })
    } else {
      throw new Error(`"${eventType}"事件未註冊`);
    }
    return this;
  }

  // 刪除事件
  off(eventType, handle) {
    if (!this.handles.hasOwnProperty(eventType)) {
      throw new Error(`"${eventType}"事件未註冊`);
    } else if (typeof handle != 'function') {
      throw new Error('缺乏回調函數');
    } else {
      this.handles[eventType].forEach((item, key, arr) => {
        if (item == handle) {
          arr.splice(key, 1);
        }
      })
    }
    return this; // 實現鏈式操做
  }
}

// 下面作一些騷操做
let callback = function () {
  console.log('you are so nice');
}

let pubsub = new PubSub();
pubsub.on('completed', (...args) => {
  console.log(args.join(' '));
}).on('completed', callback);

pubsub.emit('completed', 'what', 'a', 'fucking day');
pubsub.off('completed', callback);
pubsub.emit('completed', 'fucking', 'again');

 

輸出值:

what a fucking day

you are so nice

fucking again

相關文章
相關標籤/搜索