面向對象設計鼓勵將行爲分散到對象中,把對象劃分爲更小的粒度,有助於增強對象的可複用性。但是粒度太小又導致對象之間聯繫增加,進一步導致複用性降低。
中介者模式的作用就是解除對象與對象之間緊密的耦合。
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,中介者模式的優缺點
優點是用中間者之後,對象之間解耦,以中介者和對象的一對多,取代了對象之間的網狀結構。對象的維護變方便了。
缺點是中介者會變得比較複雜,難以維護。