《JavaScript設計模式與開發實踐》模式篇(11)—— 中介者模式

中介者模式的做用就是解除對象與對象之間的緊耦合關係。增長一箇中介者對象後,全部的 相關對象都經過中介者對象來通訊,而不是互相引用,因此當一個對象發生改變時,只須要通知 中介者對象便可。中介者使各對象之間耦合鬆散,並且能夠獨立地改變它們之間的交互。中介者模式使網狀的多對多關係變成了相對簡單的一對多關係設計模式

故事背景

假如在玩泡泡堂的遊戲,使用泡泡擊敗對方全部玩家才能得到勝利。如今將隊伍分紅兩個組進行遊戲bash

代碼實現(未使用中介者模式)

/*玩家*/
function Player( name, teamColor ){ 
    this.partners = []; // 隊友列表 
    this.enemies = []; // 敵人列表 
    this.state = 'live'; // 玩家狀態 
    this.name = name; // 角色名字 
    this.teamColor = teamColor; // 隊伍顏色
};
Player.prototype.win = function(){ // 玩家團隊勝利 
    console.log( 'winner: ' + this.name );
};
Player.prototype.lose = function(){ // 玩家團隊失敗
    console.log( 'loser: ' + this.name );
};

/*玩家死亡的方法*/
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; break;
        } 
    }
    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();
        } 
    }
};
/*定義一個工廠來建立玩家*/
var playerFactory = function( name, teamColor ){ 
    var newPlayer = new Player( name, teamColor );
    for ( var 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; 
};
/*建立玩家*/
//紅隊:
var player1 = playerFactory( '皮蛋', 'red' ),
    player2 = playerFactory( '小乖', 'red' ), 
    player3 = playerFactory( '寶寶', 'red' ), 
    player4 = playerFactory( '小強', 'red' );
//藍隊:
var player5 = playerFactory( '黑妞', 'blue' ),
    player6 = playerFactory( '蔥頭', 'blue' ), 
    player7 = playerFactory( '胖墩', 'blue' ), 
    player8 = playerFactory( '海盜', 'blue' );

讓紅隊玩家所有死亡:
player1.die(); 
player2.die();
player4.die();  
player3.die();
複製代碼

執行結果

重構思路

若是玩家不止8個,有成百上千個。一個玩家若是掉線或者更換隊伍,上面的代碼徹底沒法解決。因此須要一箇中間對象去管理每一個玩家之間的狀態與關係。以下圖所示 app

代碼重構(使用中介者模式)

  • 步驟
    • 定義Player構造函數和player對象的原型方法
    • 定義中介者對象playerDirector
    • 把操做轉交給中介者對象
/*******************玩家死亡*****************/
Player.prototype.die = function(){
    this.state = 'dead';
    playerDirector.reciveMessage( 'playerDead', this );// 給中介者發送消息,玩家死亡
};
/*******************移除玩家*****************/
Player.prototype.remove = function(){ // 給中介者發送消息,移除一個玩家
    playerDirector.reciveMessage('removePlayer', this );
};
/*******************玩家換隊*****************/
Player.prototype.changeTeam = function( color ){
    playerDirector.reciveMessage( 'changeTeam', this, color ); // 給中介者發送消息,玩家換隊
};
/*******************定義中介者對象*****************/
var playerDirector= ( function(){
    var players = {}, // 保存全部玩家
    operations = {}; // 中介者能夠執行的操做
    /****************新增一個玩家***************************/ 
    operations.addPlayer = function( player ){
        var teamColor = player.teamColor; // 玩家的隊伍顏色
        players[ teamColor ] = players[ teamColor ] || []; // 若是該顏色的玩家尚未成立隊伍,則新成立一個隊伍 
        players[ teamColor ].push( player ); // 添加玩家進隊伍
    };
    /****************移除一個玩家***************************/ 
    operations.removePlayer = function( player ){
        var teamColor = player.teamColor, // 玩家的隊伍顏色
        teamPlayers = players[ teamColor ] || []; // 該隊伍全部成員
        for ( var i = teamPlayers.length - 1; i >= 0; 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 === true ){// 所有死亡
            for ( var i = 0, player; player = teamPlayers[ i++ ]; ){ 
                player.lose(); // 本隊全部玩家 lose
            }
            for ( var color in players ){ 
                if ( color !== teamColor ){
                    var teamPlayers = players[ color ];  // 其餘隊伍的玩家
                    for( var i = 0, player; player = teamPlayers[ i++ ]; ){
                        player.win(); // 其餘隊伍全部玩家 win
                    } 
                }
            } 
        }
    };

    var reciveMessage = function() {
        var message = Array.prototype.shift.call( arguments ); 
        operations[ message ].apply( this, arguments );
    };
    return {
        reciveMessage: reciveMessage
    } 
})();
/*******************設置工廠函數*****************/
var playerFactory = function( name, teamColor ){
    var newPlayer = new Player( name, teamColor ); // 創造一個新的玩家對象     
    playerDirector.reciveMessage( 'addPlayer', newPlayer ); // 給中介者發送消息,新增玩家
    return newPlayer; 
};
//紅隊
var player1 = playerFactory('皮蛋', 'red' ), 
    player2 = playerFactory('小乖', 'red' ),
    player3 = playerFactory( '寶寶', 'red' ), 
    player4 = playerFactory('小強', 'red' );
// 藍隊:
var player5 = playerFactory('黑妞', 'blue' ),
    player6 = playerFactory( '蔥頭', 'blue' ),  
    player7 = playerFactory( '胖墩', 'blue' ),
    player8 = playerFactory( '海盜', 'blue' );
player1.die(); 
player2.die();
player3.die(); 
player4.die();
複製代碼

執行結果

如今能夠隨時的進行掉線或者換隊操做,玩家之間的耦合基本上消失了。

使用時機

中介者模式能夠很是方便地對模塊或者對象進行解耦,但對象之間並不是必定須要解耦。在實際項目中,模塊或對象之間有一些依賴關係是很正常的。畢竟咱們寫程序是爲了快速完成項目交付生產,而不是堆砌模式和過分設計。關鍵就在於如何去衡量對象之間的耦合程度。通常來講, 若是對象之間的複雜耦合確實致使調用和維護出現了困難,並且這些耦合度隨項目的變化呈指數增加曲線,那咱們就能夠考慮用中介者模式來重構代碼。函數

小結

中介者模式是迎合迪米特法則的一種實現。迪米特法則也叫最少知識原則,是指一個對象應該儘量少地瞭解另外的對象。若是對象之間的耦合性過高,一個對象 發生改變以後,不免會影響到其餘的對象,而在中介者模式裏,對象之間幾乎不知道彼此的存在,它們只能經過中介者對象來互相影響對方。post

所以,中介者模式使各個對象之間得以解耦,以中介者和對象之間的一對多關係取代了對象 之間的網狀多對多關係。各個對象只需關注自身功能的實現,對象之間的交互關係交給了中介者 對象來實現和維護。ui

不過,中介者模式也存在一些缺點。其中,最大的缺點是系統中會新增一箇中介者對象,因 爲對象之間交互的複雜性,轉移成了中介者對象的複雜性,使得中介者對象常常是巨大的。中介 者對象自身每每就是一個難以維護的對象。this

系列文章:

《JavaScript設計模式與開發實踐》最全知識點彙總大全spa

相關文章
相關標籤/搜索