JavaScript設計模式之中介者模式

定義

中介者模式的做用就是解除對象與對象之間的緊耦合關係,全部對象經過中介者來通訊,而不是互相引用,因此當一個對象發生改變時,只須要通知中介者對象便可。
javascript

objects images

圖一


mediator image

圖二


圖一中對象之間關係複雜,是多對多的關係;使用中介者模式後,將圖一中的多對多關係變成了圖二的一對多的關係。

應用

下面經過一個泡泡糖遊戲來介紹中介者模式的應用。
在剛開始設計遊戲的時候,只支持兩個玩家同時進行對戰。首先定義一個玩家類,它有三個方法win、lose、die,由於玩家的數目是2,因此當其中一個玩家死亡遊戲並結束了,同時通知他的對手勝利。因而有下面的代碼:java

class Player {
  constructor (name) {
    this.name = name
    this.enemy = null  // 敵人
  }

  win () {
    console.log(`${this.name} won`)
  }

  lose () {
    console.log(`${this.name} lost`)
  }

  die () {
    this.lose()
    this.enemy.win()
  }
}

// 建立兩個玩家
const player1 = new Player('蘑菇頭')
const player2 = new Player('閏土')

// 給玩家互設敵人
player1.enemy = player2
player2.enemy = player1
複製代碼

當玩家player1死亡的時候,只須要調用die方法,並結束了這局遊戲:數組

player1.die()  // 蘑菇頭 lost,閏土 won
複製代碼

兩個玩家的遊戲沒什麼意思,知足不了一堆玩家一塊兒玩,因而加了需求,能夠組隊玩。
給遊戲增長隊伍
首先定義一個數組players保存全部玩家,每次建立玩家,循環players給每一個玩家添加敵人或者隊友,而後給玩家添加隊友列表、敵人列表、隊伍顏色、玩家狀態,因此咱們改寫Player類:app

let players = []  // 

class Player {
  constructor (name, teamColor) {
    this.name = name
    this.partners = []
    this.enemies = []
    this.state = 'live'
    this.teamColor = teamColor
  }

  win () {
    console.log(`${this.name} won`)
  }

  lose () {
    console.log(`${this.name} lost`)
  }

  die () {
    let all_dead = true
    
    this.state = 'dead'  // 修改玩家的狀態

    for (let i = 0, partner; partner = this.partners[i++];) {
      if (partner.state !== 'dead') {
        all_dead = false
        break
      }
    }

    if (all_dead === true) {
      this.lose()

      for (let i = 0, partner; partner = this.partners[i++];) {
        partner.lose()
      }

      for (let i = 0, enemy; enemy = this.enemies[i++];) {
        enemy.win()
      }
    }

  }
}
複製代碼

最後定義工廠函數來建立玩家:函數

const playerFactory = function (name, teamColor) {
  const newPlayer = new Player(name, teamColor)
  
  for (let i = 0, player; player = players[i++];) {
    if (player.teamColor === newPlayer.teamColor) {
      player.partners.push(newPlayer)
      newPlayer.partners.push(player)
    } else {
      player.enemies.push(newPlayer)
      newPlayer.enemies.push(player)
    }
  }

  players.push(newPlayer)
  return newPlayer
}
複製代碼

建立玩家兩隊玩家:測試

const player1 = playerFactory('A', 'red')
const player2 = playerFactory('B', 'red')
const player3 = playerFactory('C', 'red')
const player4 = playerFactory('D', 'red')

const player5 = playerFactory('E', 'blue')
const player6 = playerFactory('F', 'blue')
const player7 = playerFactory('G', 'blue')
const player8 = playerFactory('H', 'blue')
複製代碼

而後讓紅隊玩家所有死亡:ui

player1.die()  // A lost
player2.die()  // B lost
player3.die()  // C lost
player4.die()  // D lost, E won、 F won、 G won、 H won
複製代碼

上面的代碼雖然能完成最後咱們想要的效果,可是每一個玩家之間都是緊密相關的,每一個玩家都會有一個partners和enemyies來保存其它玩家的引用,任何一個玩家死亡或者進行其它操做,都要顯示地通知其它玩家。在上面的例子中,只有8個玩家可能尚未對你產生足夠多的困擾。可是在實際應用中,每一個遊戲都有成千上萬的玩家,幾十支隊伍互相廝殺,這時候玩家狀態改變,若是經過循環去通知其它玩家,估計遊戲隨時會崩潰。
用中介者模式改造遊戲
首先仍是定義玩家類,只不過在玩家的類方法中,不須要負責執行具體的邏輯,而是把操做交給中介者,咱們命名中介者爲playDirector。看代碼:this

class Player {
  constructor (name, teamColor) {
    this.name = name
    this.teamColor = teamColor
    this.state = 'live'
  }

  win () {
    console.log(`${this.name} won`)
  }

  lose () {
    console.log(`${this.name} lost`)
  }

  die () {
    this.state = 'dead'
    playDirector.receiveMessage('playerDead', this)
  }

  // 移除玩家
  remove () {
    playDirector.receiveMessage('removePlayer', this)
  }

  // 玩家換隊
  changeTeam () {
    playDirector.receiveMessage('changeTeam', this)
  }
}
複製代碼

改寫建立玩家的工廠函數,這個工廠只須要建立玩家,不須要給玩家設置隊友和敵人:spa

const playerFactory = function (name, teamColor) {
  const newPlayer = new Player(name, teamColor)
  playDirector.receiveMessage('addPlayer', newPlayer)

  return newPlayer
}
複製代碼

經過前面的代碼,咱們能夠看出,中介者須要暴露一個receiveMessage接口,負責接收player對象發送的消息,而後中介者收到消息後進行處理。下面實現中介者:prototype

const playDirector = (function () {
  let players = {},  // 保存全部玩家
    operations = {} // 中介者能夠執行的操做

  operations.addPlayer = function (player) {
    const teamColor = player.teamColor
    players[teamColor] = players[teamColor] || []

    players[teamColor].push(player)
  }

  operations.removePlayer = function (player) {
    let teamColor = player.teamColor,
      teamPlayers = players[teamColor] || []
    
    teamPlayers = teamPlayers.filter((item) => {
      return item !== player
    })
  }

  operations.changeTeam = function (player, teamColor) {
    operations.removePlayer(player)
    player.teamColor = teamColor

    operations.addPlayer(player)
  }

  operations.playerDead = function (player) {
    let teamColor = player.teamColor,
      teamPlayers = players[teamColor] || []

    let all_dead = true
    
    this.state = 'dead'  // 修改玩家的狀態

    for (let i = 0, player; player = teamPlayers[i++];) {
      if (player.state !== 'dead') {
        all_dead = false
        break
      }
    }

    if (all_dead === true) {

      for (let i = 0, player; player = teamPlayers[i++];) {
        player.lose()
      }

      for (let color in players) {
        if (color !== teamColor) {
          let teamPlayers = players[color]
          for (let i = 0, player; player = teamPlayers[i++];) {
            player.win()
          }
        }
      }
    }
  }

  const receiveMessage = function () {
    const message = arguments[0]
    operations[message].apply(this, Array.prototype.slice.call(arguments, 1))
  }

  return {
    receiveMessage
  }
})()
複製代碼

經過添加中介者,玩家與玩家之間的耦合關係已經解除,某個玩家的操做不須要通知其它玩家,只要給中介者發送一條消息,中介者處理完消息以後再把結果反饋給其它玩家。
測試代碼:

const player1 = playerFactory('A', 'red')
const player2 = playerFactory('B', 'red')
const player3 = playerFactory('C', 'red')
const player4 = playerFactory('D', 'red')

const player5 = playerFactory('E', 'blue')
const player6 = playerFactory('F', 'blue')
const player7 = playerFactory('G', 'blue')
const player8 = playerFactory('H', 'blue')

player1.die()
player2.die()
player3.die()
player4.die()
複製代碼

獲得結果:

result image

總結

中介者模式是迪米特法則的一種實現,迪米特法則也叫最少知識原則,是指一個對象應該儘量少了解其它的對象。若是對象之間耦合性過高,一個對象的變化將會影響其它對象,在中介者模式中,對象之間幾乎不知道彼此的存在,它們之間經過中介者來相互影響。 中介者模式也存在一些缺點,最大的缺點是系統中會新增一個巨大的中介者對象,由於中介者對象之間交互的複雜性,所有轉移到中介者對象上,因此維護好中介者也是很困難的事。中介者模式能夠很方便地解耦對象之間的關係,可是對象之間的關係並不必定須要解耦,因此在寫代碼時須要權衡對象之間的耦合程度。通常來講,若是對象之間的複雜耦合確實致使了調用和維護困難,並且這些耦合會隨着項目的變化還在繼續增長,咱們就能夠考慮使用中介者模式來重構代碼。

相關文章
相關標籤/搜索