js設計模式--發佈訂閱模式

前言

本系列文章主要根據《JavaScript設計模式與開發實踐》整理而來,其中會加入了一些本身的思考。但願對你們有所幫助。ajax

文章系列

js設計模式--單例模式segmentfault

js設計模式--策略模式設計模式

js設計模式--代理模式異步

js設計模式--迭代器模式網站

概念

發佈—訂閱模式又叫觀察者模式,它定義對象間的一種一對多的依賴關係,當一個對象的狀 態發生改變時,全部依賴於它的對象都將獲得通知。this

場景

DOM事件

document.body.addEventListener('click', function () {
    alert(2);
  }, false);
  document.body.addEventListener('click', function () {
    alert(3);
  }, false);
  document.body.addEventListener('click', function () {
    alert(4);
  }, false);

  document.body.click(); // 模擬用戶點擊

優缺點

優勢:發佈—訂閱模式的優勢很是明顯,一爲時間上的解耦,二爲對象之間的解耦。
缺點:建立訂閱者自己要消耗必定的時間和內存,而 且當你訂閱一個消息後,也許此消息最後都未發生,但這個訂閱者會始終存在於內存中。prototype

例子

銷售處訂閱房源

簡單的發佈訂閱

var Event = function() {
  this.list = []
}

Event.prototype.add = function(listener) {
  this.list.push(listener)
}

Event.prototype.triggle = function() {
  this.list.forEach(listener => {
    listener()
  })
}

var event = new Event()
event.add(()=>{console.log('房源1--80平--200萬')})
event.add(()=>{console.log('房源2--200平--1000萬')})

event.triggle()

或者設計

var event = {
  list: [],
  add(listener) {
    this.list.push(listener)
  },
  triggle() {
    this.list.forEach(listener => {
      listener()
    })
  }
}

event.add(()=>{console.log('房源1--80平--200萬')})
event.add(()=>{console.log('房源2--200平--1000萬')})

event.triggle()

但這種不能區分是發不了什麼消息,好比有兩羣人:訂閱80平房源報價和訂閱200瓶房源報價的人,這裏兩羣人都會獲得通知代理

改進

var event = {
  list: {},
  add(type, listener) {
    if (!this.list[type]) {
      this.list[type] = []
    }
    this.list[type].push(listener)
  },
  triggle(type) {
    this.list[type] && this.list[type].forEach(listener => {
      listener()
    })
  }
}

event.add('80平', ()=>{console.log('房源1--80平--200萬')})
event.add('80平', ()=>{console.log('房源2--80平--300萬')})
event.add('200平', ()=>{console.log('房源2--200平--1000萬')})

event.triggle('80平')

這裏還少了一個取消訂閱的功能code

增長取消訂閱

var event = {
  list: {},
  add(type, listener) {
    if (!this.list[type]) {
      this.list[type] = []
    }
    this.list[type].push(listener)
  },
  triggle(type) {
    this.list[type] && this.list[type].forEach(listener => {
      listener()
    })
  },
  remove(type, fn) {
    if (!this.list[type]) return
    var index = this.list[type].findIndex(listener => listener === fn)
    this.list[type].splice(index, 1)
  }
}

var f1 = ()=>{console.log('房源1--80平--200萬')}
var f2 = ()=>{console.log('房源2--80平--300萬')}
var f3 = ()=>{console.log('房源2--200平--1000萬')}

event.add('80平', f1)
event.add('80平', f2)
event.add('200平', f3)

event.remove('80平', f2)
event.triggle('80平') // 房源1--80平--200萬

上面代碼結構還不是很清晰,咱們再模擬銷售部真實的場景

更真實的銷售部場景

  1. 銷售部
  • 銷售部有不少房源,如80平的,100平的等
  • 客戶能夠到銷售部登記本身想買的房源面積,並留下姓名。到時候若是有房源,銷售部就會通知客戶
  • 客戶因爲一些緣由決定不買房的時候,能夠取消訂閱
  1. 客戶
  • 當有房源時,客戶有一個接聽報價的方法
var Event = function () {
  this.list = {}
}

Event.prototype.add = function (area, client) {
  if (!this.list[area]) this.list[area] = []
  this.list[area].push(client)
}

Event.prototype.remove = function (area, client) {
  if (!this.list[area]) return
  var index = this.list[area].findIndex(item => item === client)
  this.list[area].splice(index, 1)
}


Event.prototype.triggle = function (area, price) {
  if (!this.list[area]) return
  this.list[area].forEach(client => {
    client.listen(area, price)
  })
}

var Client = function (name) {
  this.name = name
}

Client.prototype.listen = function (area, price) {
  console.log(`${this.name}收到${area}平的房源報價${price}`)
}


var client1 = new Client('client1')
var client2 = new Client('client2')


var event = new Event()
event.add('80平', client1)
event.add('100平', client1)
event.add('80平', client2)
event.add('300平', client1)
event.remove('300平', client1)

event.triggle('80平', 200) // client1收到80平平的房源報價200 client2收到80平平的房源報價200
event.triggle('100平', 500) // client1收到100平平的房源報價500
event.triggle('200平', 1000) //
event.triggle('300平', 1000) //

上面的代碼雖然已經很好了,可是仍是有一個缺點:訂閱者接收不到訂閱以前發佈的消息,以下客戶3也想訂閱80平的房源,但他收不到任何消息

var client3 = new Client('client3')
event.add('80平', client3)

咱們但願客戶3也能收到消息

必須先訂閱再發布嗎

咱們增長一個cache字段來記錄歷史房源報價

var Event = function () {
  this.list = {}
  this.cache = {}
}

Event.prototype.add = function (area, client) {
  if (!this.list[area]) this.list[area] = []
  this.list[area].push(client)
  this.cache[area].forEach(price => {
    client.listen(area, price)
  })
}

Event.prototype.remove = function (area, client) {
  if (!this.list[area]) return
  var index = this.list[area].findIndex(item => item === client)
  this.list[area].splice(index, 1)
}


Event.prototype.triggle = function (area, price) {
  if (!this.cache[area]) this.cache[area] = []
  this.cache[area].push(price)

  if (!this.list[area]) return
  this.list[area].forEach(client => {
    client.listen(area, price)
  })
}

var Client = function (name) {
  this.name = name
}

Client.prototype.listen = function (area, price) {
  console.log(`${this.name}收到${area}平的房源報價${price}`)
}


var client1 = new Client('client1')
var client2 = new Client('client2')


var event = new Event()
// event.add('80平', client1)
// event.add('100平', client1)
// event.add('80平', client2)
// event.add('300平', client1)
// event.remove('300平', client1)

event.triggle('80平', 200) // client1收到80平平的房源報價200 client2收到80平平的房源報價200
event.triggle('100平', 500) // client1收到100平平的房源報價500
event.triggle('200平', 1000) //
event.triggle('300平', 1000) //

var client3 = new Client('client3')
event.add('80平', client3)
event.add('100平', client3)

網站登陸

假如咱們正在開發一個商城網站,網站裏有 header 頭部、nav 導航、消息列表、購物車等模塊。這幾個模塊的渲染有一個共同的前提條件,就是必須先用 ajax 異步請求獲取用戶的登陸信息。

這裏留給讀者本身實現

相關文章
相關標籤/搜索