中介者模式的做用就是解除對象與對象之間的緊耦合關係,全部對象經過中介者來通訊,而不是互相引用,因此當一個對象發生改變時,只須要通知中介者對象便可。
javascript
下面經過一個泡泡糖遊戲來介紹中介者模式的應用。
在剛開始設計遊戲的時候,只支持兩個玩家同時進行對戰。首先定義一個玩家類,它有三個方法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()
複製代碼
獲得結果:
中介者模式是迪米特法則的一種實現,迪米特法則也叫最少知識原則,是指一個對象應該儘量少了解其它的對象。若是對象之間耦合性過高,一個對象的變化將會影響其它對象,在中介者模式中,對象之間幾乎不知道彼此的存在,它們之間經過中介者來相互影響。 中介者模式也存在一些缺點,最大的缺點是系統中會新增一個巨大的中介者對象,由於中介者對象之間交互的複雜性,所有轉移到中介者對象上,因此維護好中介者也是很困難的事。中介者模式能夠很方便地解耦對象之間的關係,可是對象之間的關係並不必定須要解耦,因此在寫代碼時須要權衡對象之間的耦合程度。通常來講,若是對象之間的複雜耦合確實致使了調用和維護困難,並且這些耦合會隨着項目的變化還在繼續增長,咱們就能夠考慮使用中介者模式來重構代碼。