Mediator

Define an object that encapsulates how a set of objects interact. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and it lets you vary their interaction independently.



Usage in JavaScript:
medium low


Summary


The Mediator pattern provides central authority over a group of objects by encapsulating how these objects interact. This model is useful for scenarios where there is a need to manage complex conditions in which every object is aware of any state change in any other object in the group.

The Mediator patterns are useful in the development of complex forms. Take for example a page in which you enter options to make a flight reservation. A simple Mediator rule would be: you must enter a valid departure date, a valid return date, the return date must be after the departure date, a valid departure airport, a valid arrival airport, a valid number of travelers, and only then the Search button can be activated.

Another example of Mediator is that of a control tower on an airport coordinating arrivals and departures of airplanes.


Diagram



Participants


The objects participating in this pattern are:

  • Mediator -- In sample code: Chatroom
    • defines an interface for communicating with Colleague objects
    • maintains references to Colleague objects
    • manages central control over operations
  • Colleagues -- In sample code: Participants
    • objects that are being mediated by the Mediator
    • each instance maintains a reference to the Mediator

JavaScript Code


In the example code we have four participants that are joining in a chat session by registering with a Chatroom (the Mediator). Each participant is represented by a Participant object. Participants send messages to each other and the Chatroom handles the routing.

This example is simple, but other complex rules could have been added, such as a 'junk filter' to protect participants from receiving junk messages.

The log function is a helper which collects and displays results.


var Participant = function(name) {
    this.name = name;
    this.chatroom = null;
};

Participant.prototype = {
    send: function(message, to) {
        this.chatroom.send(message, this, to);
    },
    receive: function(message, from) {
        log.add(from.name + " to " + this.name + ": " + message);
    }
};

var Chatroom = function() {
    var participants = {};
    return {
        register: function(participant) {
            participants[participant.name] = participant;
            participant.chatroom = this;
        },
        send: function(message, from, to) {
            if (to) {                      // single message
                to.receive(message, from);    
            } else {                       // broadcast message
                for (key in participants) {   
                    if (participants[key] !== from) {
                        participants[key].receive(message, from);
                    }
                }
            }
        }
    };
};

// log helper
var log = (function() {
    var log = "";
    return {
        add: function(msg) { log += msg + "\n"; },
        show: function() { alert(log); log = ""; }
    }
})();


function run() {

    var yoko = new Participant("Yoko");
    var john = new Participant("John");
    var paul = new Participant("Paul");
    var ringo = new Participant("Ringo");

    var chatroom = new Chatroom();
    chatroom.register(yoko);
    chatroom.register(john);
    chatroom.register(paul);
    chatroom.register(ringo);

    yoko.send("All you need is love.");
    yoko.send("I love you John.");
    john.send("Hey, no need to broadcast", yoko);
    paul.send("Ha, I heard that!");
    ringo.send("Paul, what do you think?", paul);

    log.show();
}
Run



JavaScript Optimized Code


The Namespace pattern is applied to keep the code out of the global namespace. Our namespace is Patterns.Classic. A Revealing Module named Mediator returns (i.e. reveals) a single item: Chatroom. The chatroom's register method now creates and returns the newly created Participant instance. This allows us to keep to code in the run method simple and compact.

The Patterns object contains the namespace function which constructs namespaces non-destructively, that is, if a name already exists it won't overwrite it.

The log function is a helper which collects and displays results.


var Patterns = {
    namespace: function (name) {
        var parts = name.split(".");
        var ns = this;

        for (var i = 0, len = parts.length; i < len; i++) {
            ns[parts[i]] = ns[parts[i]] || {};
            ns = ns[parts[i]];
        }

        return ns;
    }
};

Patterns.namespace("Classic").Mediator = (function () {
    var Participant = function (name) {
        this.name = name;
        this.chatroom = null;
    };

    Participant.prototype = {
        send: function (message, to) {
            this.chatroom.send(message, this, to);
        },
        receive: function (message, from) {
            log.add(from.name + " to " + this.name + ": " + message);
        }
    };

    var Chatroom = function () {
        var participants = {};
        return {
            register: function (name) {
                var participant = new Participant(name);
                participants[participant.name] = participant;
                participant.chatroom = this;

                return participant;
            },
            send: function (message, from, to) {
                if (to) {                       // single message
                    to.receive(message, from);
                } else {                        // broadcast message
                    for (key in participants) {
                        if (participants[key] !== from) {
                            participants[key].receive(message, from);
                        }
                    }
                }
            }
        };
    };

    return {
        Chatroom: Chatroom
    }
})();


// log helper
var log = (function () {
    var log = "";
    return {
        add: function (msg) { log += msg + "\n"; },
        show: function () { alert(log); log = ""; }
    }
})();


function run() {

    var chatroom = new Patterns.Classic.Mediator.Chatroom();

    var yoko = chatroom.register("Yoko");
    var john = chatroom.register("John");
    var paul = chatroom.register("Paul");
    var ringo = chatroom.register("Ringo");

    yoko.send("All you need is love.");
    yoko.send("I love you John.");
    john.send("Hey, no need to broadcast", yoko);
    paul.send("Ha, I heard that!");
    ringo.send("Paul, what do you think?", paul);

    log.show();
}
Run



  Iterator
Memento