The role of the mediator pattern is to decouple objects from each other. By adding a mediator object, all related objects communicate through the mediator object rather than referring to each other, so when an object changes, the mediator object only needs to be notified. The mediator loosens the coupling between objects and can change their interactions independently. The mediator pattern turns the netted many-to-many relationship into a relatively simple one-to-many relationship

The story background

If be playing the game of bubble hall, use bubble to defeat each other all player ability obtains victory. Now divide the teams into two groups for the game

Code implementation (without using the mediator pattern)

/ * * /functionPlayer( name, teamColor ){ this.partners = []; // enemies = []; // enemies = []; // List of enemies this.state ='live'; // Player status this.name = name; // Role name this.teamColor = teamColor; // team color}; Player.prototype.win =function(){// Team victory console.log()'winner: ' + this.name );
};
Player.prototype.lose = function(){// player team fails console.log('loser: '+ this.name ); }; /* Player.prototype.die = / / player.prototype. die =function(){// player dead var all_dead =true;
    this.state = 'dead'; // Set the player state to deadfor( var i = 0, partner; partner = this.partners[ i++ ]; ) {// Iterate over the teammate listif( partner.state ! = ='dead'){// If a teammate is not dead, the game has not failedfalse; break; }}if( all_dead === true){// If all teammates die this.lose(); // Tell yourself the game failedfor( var i = 0, partner; partner = this.partners[ i++ ]; ) { partner.lose(); }for( var i = 0, enemy; enemy = this.enemies[ i++ ]; ) { enemy.win(); }}}; /* Define a factory to create players */ var playerFactory =function( name, teamColor ){ 
    var newPlayer = new Player( name, teamColor );
    for( var i = 0, player; player = players[ i++ ]; ) {// Notify all players that a new character has been addedif(player.teamcolor === newPlayer.teamcolor){player.partners. Push (newPlayer); // Add each other to the list of teammates newplayer.partners.push (player); }else{ player.enemies.push( newPlayer ); // Add each other to the list of enemies newplayer.enemies. Push (player); } } players.push( newPlayer );returnnewPlayer; }; Var player1 = playerFactory('preserved egg'.'red' ),
    player2 = playerFactory( 'little girl'.'red' ), 
    player3 = playerFactory( 'baby'.'red' ), 
    player4 = playerFactory( 'jack'.'red'); Var player5 = playerFactory('dark girl of personages'.'blue' ),
    player6 = playerFactory( 'onion'.'blue' ), 
    player7 = playerFactory( 'overweight'.'blue' ), 
    player8 = playerFactory( 'the pirates'.'blue'); Let all red team players die: player1.die(); player2.die(); player4.die(); player3.die();Copy the code

The execution result

Refactoring approach

If there are more than eight players, there are hundreds. If a player drops a call or changes teams, the above code is completely unfixable. An intermediate object is needed to manage the states and relationships between each player. As shown in the figure below

Code refactoring (using the mediator pattern)

  • steps
    • Defines the Player constructor and prototype methods for the Player object
    • Define the intermediary object playerDirector
    • Transfer the action to the mediator object
/ * * * * * * * * * * * * * * * * * * * Player death * * * * * * * * * * * * * * * * * / Player in the prototype. Die =function(){
    this.state = 'dead';
    playerDirector.reciveMessage( 'playerDead', this ); // Send a message to the mediator, the player dies}; / * * * * * * * * * * * * * * * * * * * remove Player * * * * * * * * * * * * * * * * * / Player in the prototype. Remove =function() {/ / to broker sends the message, remove a player playerDirector reciveMessage ('removePlayer', this ); }; / * * * * * * * * * * * * * * * * * * * players in the team * * * * * * * * * * * * * * * * * / Player in the prototype. ChangeTeam =function( color ){
    playerDirector.reciveMessage( 'changeTeam', this, color ); // Send a message to the broker, the player changes teams}; / * * * * * * * * * * * * * * * * * * * define mediator object * * * * * * * * * * * * * * * * * / var playerDirector = (function(){var players = {}, // save all players operations = {}; / / broker can perform operations / * * * * * * * * * * * * * * * * a new player * * * * * * * * * * * * * * * * * * * * * * * * * * * / operations in addPlayer =function( player ){ var teamColor = player.teamColor; / / player's team colors players [teamColor] = players [teamColor] | | []; // Create a new team players[teamColor]. Push (player); // Add players to the team}; / * * * * * * * * * * * * * * * * to remove a player * * * * * * * * * * * * * * * * * * * * * * * * * * * / operations in removePlayer =function(player) {var teamColor = player teamColor, / / player's team colors teamPlayers = players [teamColor] | | []; // All members of the teamfor( var i = teamPlayers.length - 1; i >= 0; I --){// iterate over the deleteif( teamPlayers[ i ] === player ){ teamPlayers.splice( i, 1 ); }}}; / * * * * * * * * * * * * * * * * players in the team * * * * * * * * * * * * * * * * * * * * * * * * * * * / operations in changeTeam =function(player, newTeamColor){// Player changes teams operations.removePlayer(player); // Remove player.teamColor = newTeamColor from the original team; // Change the team color operations.addPlayer(player); // add to new team}; / * * * * * * * * * * * * * * * * player death * * * * * * * * * * * * * * * * * * * * * * * * * * * / operations playerDead =function(player){// player dies var teamColor = player.teamColor, teamPlayers = players[teamColor]; // Player's team var all_dead =true;
        for( var i = 0, player; player = teamPlayers[ i++ ]; ) {if( player.state ! = ='dead' ){
                all_dead = false;
                break; }}if ( all_dead === true){// All deadfor( var i = 0, player; player = teamPlayers[ i++ ]; ) { player.lose(); // all players lose}for ( var color in players ){ 
                if( color ! == teamColor ){ var teamPlayers = players[ color ]; // Players from other teamsfor( var i = 0, player; player = teamPlayers[ i++ ]; ) { player.win(); // All players in other teams win}}}}}; var reciveMessage =function() {
        var message = Array.prototype.shift.call( arguments ); 
        operations[ message ].apply( this, arguments );
    };
    return{ reciveMessage: reciveMessage } })(); / * * * * * * * * * * * * * * * * * * * factory set function * * * * * * * * * * * * * * * * * / var playerFactory =function( name, teamColor ){ var newPlayer = new Player( name, teamColor ); / / create a new player object playerDirector reciveMessage ('addPlayer', newPlayer ); // Send a message to the broker to add new playersreturnnewPlayer; }; Var player1 = playerFactory('preserved egg'.'red' ), 
    player2 = playerFactory('little girl'.'red' ),
    player3 = playerFactory( 'baby'.'red' ), 
    player4 = playerFactory('jack'.'red'); Var player5 = playerFactory('dark girl of personages'.'blue' ),
    player6 = playerFactory( 'onion'.'blue' ),  
    player7 = playerFactory( 'overweight'.'blue' ),
    player8 = playerFactory( 'the pirates'.'blue' );
player1.die(); 
player2.die();
player3.die(); 
player4.die();
Copy the code

The execution result

Use time

The mediator pattern can be very convenient to decouple modules or objects, but decoupling between objects is not necessarily necessary. In a real project, it is normal to have some dependencies between modules or objects. After all, we write programs to deliver projects quickly to production, not to stack patterns and over-design. The key is how to measure the coupling between objects. In general, if the complex coupling between objects does make it difficult to call and maintain, and these couplings grow exponentially as the project changes, then we can consider refactoring the code with the intermediary pattern.

summary

The intermediary pattern is a kind of realization that caters to Demeter’s law. Demeter’s law, also known as the least knowledge principle, states that one object should know as little as possible about another. If the coupling between objects is too high, the change of one object will inevitably affect other objects, while in the mediator mode, objects are almost unaware of each other’s existence, they can only affect each other through the mediator object.

Thus, the mediator pattern decouples the objects, replacing the mesh many-to-many relationship between objects with a one-to-many relationship between the mediator and the objects. Each object only needs to pay attention to the realization of its own function, and the interaction between objects is handed over to the intermediary object to realize and maintain.

However, there are some drawbacks to the intermediary model. Among them, the biggest disadvantage is the addition of an intermediary object in the system, because of the complexity of the interaction between objects, transferred to the complexity of the intermediary object, so that the intermediary object is often huge. The mediator object itself is often a difficult object to maintain.

Series of articles:

JavaScript Design Patterns and Development Practices