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,中介者模式的优缺点

优点是用中间者之后,对象之间解耦,以中介者和对象的一对多,取代了对象之间的网状结构。对象的维护变方便了。
缺点是中介者会变得比较复杂,难以维护。

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