js常用設計模式9-中介者模式 1,現實中的中介者 2,中介者模式的例子------文字版泡泡堂遊戲 3,爲遊戲增加隊伍 4,存在的問題 5,中介者模式的優缺點

面向對象設計鼓勵將行爲分散到對象中,把對象劃分爲更小的粒度,有助於增強對象的可複用性。但是粒度太小又導致對象之間聯繫增加,進一步導致複用性降低。
中介者模式的作用就是解除對象與對象之間緊密的耦合。

1,現實中的中介者

機場指揮者:調度範圍所的全部飛機
房產中介:你懂的

2,中介者模式的例子------文字版泡泡堂遊戲

定義玩家構造函數,有3個原型方法:win,lose,die:

/**
 * 中介者:解除對象和對象之間的耦合,專門用來分發邏輯事件
 */
//這個版本只能有兩個玩家,代碼也十分拉胯
function Player(name) {
  this.name = name
  this.enemy = null //敵人
}
Player.prototype.win = function () {
  console.log(this.name + ':win')
}
Player.prototype.lose = function () {
  console.log(this.name + ":lose")
}
Player.prototype.die = function () {
  this.lose()
  this.enemy.win()
}

var player1 = new Player('玩家1')
var player2 = new Player('玩家2')

player1.enemy = player2
player2.enemy = player1

player1.die()

3,爲遊戲增加隊伍

之前只有倆玩家,當玩家變多的時候就需要分組了。目前暫時分成兩組
(1)定義一個數組players來保存所有玩家,常見玩家之後,循環players來給每個玩家設置隊友和敵人:

var players = []

(2)改寫構造函數,增加一些屬性:

function Player(name, teamColor) {
  this.partners = [] //隊友列表
  this.enemies = [] //敵人列表
  this.state = 'live' //玩家狀態
  this.name = name //角色名字
  this.teamColor = teamColor //隊伍顏色
}

(3)玩家勝利之後表現出來:

Player.prototype.win = function () {   // 玩家團隊勝利
  console.log('winner:' + this.name) 
}

Player.prototype.lose = function () {   // 玩家團隊失敗
  console.log('loser:' + this.name)
}

(4)玩家死亡之後,遍歷其他隊友的生存狀況,如果全部die,那麼隊伍lose,敵人win:

Player.prototype.die = function () {
  var all_dead = true
  this.state = 'dead'

  for (var i = 0, partner; partner = this.partners[i++];) {
    if (partner.state !== 'dead') {
      all_dead = false
    }
  }
  if (all_dead == true) {
    this.lose()
    for (var i = 0, partner; partner = this.partners[i++];) {
      partner.lose()
    }
    for (var i = 0, enemy; enemy = this.enemies[i++];) {
      enemy.win()
    }
  }
}

(5) 工廠模式創建玩家:

var playerFactory = function (name, teamColor) {
  var newPlayer = new Player(name, teamColor)

  for (var i = 0, player; player = players[i++];) {
    if (newPlayer.teamColor === player.teamColor) {
      player.partners.push(newPlayer)
      newPlayer.partners.push(player)
    } else {
      player.enemies.push(newPlayer)
      newPlayer.enemies.push(player)
    }
  }
  players.push(newPlayer)

  return newPlayer
}

(6)遊戲耍起來:

var player1 = playerFactory('玩家1', 'red')
var player2 = playerFactory('玩家2', 'red')
var player3 = playerFactory('玩家3', 'red')

var player4 = playerFactory('玩家4', 'blue')
var player5 = playerFactory('玩家5', 'blue')
var player6 = playerFactory('玩家6', 'blue')

player1.die()
player2.die()
player3.die()

4,存在的問題

上面的遊戲的玩家,有隊友和敵人的屬性,以至於只能有兩隻隊伍,而且當一個玩家die的時候,要通知所有的隊友和敵人並移除,這個操作很不好,
這個是很不利於我們的遊戲走向國際化的,下面我們來改造上面的代碼
(1)先定義Player構造函數和原型方法,現在player對象不再負責具體的邏輯,而是把操作轉交給中介者對象playerDirector。

function Player(name, teamColor) {
  this.name = name
  this.teamColor = teamColor
  this.state = 'alive'
}

Player.prototype.win = function () {
  console.log(this.name + ':win')
}
Player.prototype.die = function () {
  this.state = 'dead'
  playerDirectory.receiveMessage('playerDead', this)
}
Player.prototype.lose = function () {
  console.log(this.name + ':lose')
}
// 移除玩家
Player.prototype.remove = function () {
  console.log(this.name + ':remove')
  playerDirectory.receiveMessage('removePlayer', this)
}
// 玩家換隊
Player.prototype.changeTeam = function (color) {
  playerDirectory.receiveMessage('changeTeam', this, color)
}

(2) 改寫工廠函數,現在的工廠函數用處很小了:

//工廠函數,用處很小了已經
var playerFactory = function (name, teamColor) {
  var newPlayer = new Player(name, teamColor)
  playerDirectory.receiveMessage('addPlayer', newPlayer)
  return newPlayer
}

(3)最主要也是最重要的,實現我們的中介者函數playerDirector,一般有兩種寫法:

  • 利用發佈-訂閱模式。將playerDirector作爲訂閱者,player作爲發佈者,一旦player狀態改變,就推送消息給playerDirector,playerDirector處理消息後反饋給其他player
  • 在playerDirector中開放一些接收消息的接口,各player可以調用接口給playerDirector發消息,playerDirector通過參數來識別發送者,然後把處理結果反饋給其他player
    這倆差不多,這裏使用第二種:
//中介者
var playerDirectory = (function () {
  //精髓就在這裏
  var players = {}, //保存所有玩家
    operations = {} //中介者可以執行的操作

  operations.addPlayer = function (newPlayer) {
    var teamColor = newPlayer.teamColor
    players[teamColor] = players[teamColor] || []  //如果是新的隊伍,則創建之

    players[teamColor].push(newPlayer)
  }
  operations.removePlayer = function (player) {
    var teamPlayers = players[player.teamColor]
    for (var i = 0; i < teamPlayers.length; i++) {
      if (teamPlayers[i] === player) {
        teamPlayers.splice(i, 1)
      }
    }
  }
  operations.changeTeam = function (player, newTeamColor) {
    operations.removePlayer(player)
    player.teamColor = newTeamColor
    operations.addPlayer(player)
  }
  operations.playerDead = function (player) {
    var teamColor = player.teamColor
    teamPlayers = players[teamColor]

    var all_dead = true
    for (var i = 0, player; player = teamPlayers[i++];) {
      if (player.state !== 'dead') {
        all_dead = false
        break;
      }
    }
    if (all_dead) {
      for (var i = 0, player; player = teamPlayers[i++];) {
        player.lose()
      }
      for (var color in players) {
        if (teamColor !== color) {
          var teamPlayers = players[color]
          for (var j = 0, player; player = teamPlayers[j++];) {
            player.win()
          }
        }
      }
    }
  }

  var receiveMessage = function () {
    var message = Array.prototype.shift.call(arguments)
    operations[message].apply(this, arguments)
  }

  return {
    receiveMessage: receiveMessage
  }
})()

(4)遊戲耍起來

var player1 = playerFactory('玩家1', 'red')
var player2 = playerFactory('玩家2', 'red')
var player3 = playerFactory('玩家3', 'red')

var player4 = playerFactory('玩家4', 'blue')
var player5 = playerFactory('玩家5', 'blue')
var player6 = playerFactory('玩家6', 'blue')

player1.die()
player3.remove()
player2.die()

5,中介者模式的優缺點

優點是用中間者之後,對象之間解耦,以中介者和對象的一對多,取代了對象之間的網狀結構。對象的維護變方便了。
缺點是中介者會變得比較複雜,難以維護。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章