Your Guide to Design Patterns – Mediator Pattern

Design Patterns - Mediator Pattern

This week we’ll continue our exploration of design patterns and look at a behavioural pattern, the Mediator pattern. This is a very commonly used pattern.

The Gang of Four book defines the Mediator pattern as follows:

“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.”

The Mediator pattern encourages loose coupling between objects by having a central class that handles the interactions between the other objects. This mediator class is the only component that has detailed knowledge of the other classes and their methods. The classes do not communicate directly with each other. They can only collaborate with the mediator object.

A good example of the Mediator pattern is that of an air traffic controller who controls the in- and out-bound flights from an airport. The pilots of the individual planes don’t communicate directly with each other. The air traffic controller is the mediator who coordinates all flights and communicates with the individual pilots.

Mediator Pattern: Design Example

Back in our first person shooter game, we’ve already created controllable mechanised fighting units. Let’s say we want to launch an attack with these fighting units. We may need the fighting units transported to the battlefields using ships, helicopters and planes.

Units that can be transported obviously can’t fight, advance, or retreat while they are being transported, so we can’t use our existing FightingUnit interface. Let’s create a new interface defining a Transportable unit.

public interface Transportable {
    public void embark();
    public void disembark();
    public void idle();
    public void activate();
}

Our Transportable objects will wait until a transporter is ready to transport them. They will then embark, and be idle until the destination is reached. At that point, they will be activated and disembark. From then we can use the FightingUnit interface methods again. We will obviously retrofit the relevant FightingUnit classes with the Transportable interface.

TransportUnit will transport Transportable objects from the departure point to a destination.

public interface TransportUnit {
   public void arrive();
   public void depart();
   public void travel();
   public void startEmbarkation();
   public void startDisembarkation();
}

Once a TransportUnit has arrived at the point of departure, it can start the embarkation process. Once all the Transportable objects have embarked and are idle, it can depart, travel to the destination, and start the disembarkation process.

Mediator Pattern: Solution Code

We don’t want the transporters and the transportable units to communicate directly with each other, because that would lead to multiple inter-dependencies. An elegant solution would be to use a central controller class – the mediator – that would communicate with the separate units and direct traffic.

The mediator encapsulates all the interaction logic between transportable units and transport vehicles. If any of the existing classes are updated, or if we add new classes or rules to the system, we only have to update the mediator, and not every other class as well.

The various components only need to tell the mediator about any relevant events and pass some contextual information along. The mediator executes the business rules and invokes any necessary component methods.

HeadQuarters class would be ideal as a centralised mediator for directing transport traffic.

// The mediator class
public class HeadQuarters {

   private Collection units;
   private Collection transporters;

   // Constructor 
   public HeadQuarters() {
      units = new ArrayList<>();
      transporters = new ArrayList<>();
   }

   // Registering the components 
   public void registerTransportable(Transportable unit) {
      units.add(unit);
   }
   public void registerTransportUnit(TransportUnit unit) {
      transporters.add(unit);
   }

   // Interaction logic 
   public void startEmbarkationProcess(TransportUnit transporter){
      System.out.println(
       "HeadQuarters starting embarkation process on " + transporter);
      for (Transportable t : units) {
         t.embark();
         t.idle();
      }
      System.out.println("Embarkation process finished.");
   }

   public void startDisembarkationProcess(TransportUnit transporter){
      System.out.println(
       "HeadQuarters starting disembarkation process on " + transporter);
      for (Transportable t : units) {
         t.activate();
         t.disembark();
      }
      System.out.println("Disembarkation process finished.");
   }

   public void unitActivated(Transportable unit) {
      System.out.println("Unit activated...");
   }
   public void unitIdled(Transportable unit) {
      System.out.println("Unit idled...");
   }
   public void unitEmbarked(Transportable unit) {
      System.out.println("Unit embarked...");
   }
   public void unitDisembarked(Transportable unit) {
      System.out.println("Unit disembarked...");
   }

} // end of class 

The transport components contain a reference to the mediator (here the HeadQuarters). This is usually passed in as an argument to the constructor.

We must make sure that the components call only the mediator’s notification methods rather than any methods of other components. The business logic is contained in the mediator class.

In the embark()disembark()idle() and activate() methods below, the mediator’s notification methods are called:

public abstract class AbstractTransportableFightingUnit 
         extends AbstractFightingUnit
         implements Transportable {

   // Mediator reference
   private HeadQuarters headquarters;

   public AbstractTransportableFightingUnit(HeadQuarters hq) {
      this.headquarters = hq;
      headquarters.registerTransportable(this);
   }
   @Override
   public void embark() {
      System.out.println(toString() + " embarking...");
      headquarters.unitEmbarked(this);
   }
   @Override
   public void disembark() {
      System.out.println(toString() + " disembarking...");
      headquarters.unitDisembarked(this);
   }
   @Override
   public void idle() {
      System.out.println(toString() + " idling...");
      headquarters.unitIdled(this);
   }
   @Override
   public void activate() {
      System.out.println(toString() + " activating...");
      headquarters.unitActivated(this);
   }
} // end of class 

The TransportShip code is similar:

public class TransportShip implements TransportUnit {

   // Mediator reference 
   private HeadQuarters headquarters;

   public TransportShip(HeadQuarters hq) {
      this.headquarters = hq;
      headquarters.registerTransportUnit(this);
   }

   @Override
   public void arrive(){
      System.out.println(toString() + " arriving...");
      // call a mediator method if necessary 
   }
   @Override
   public void startEmbarkation(){
      System.out.println(toString() + " starting embarkation...");
      headquarters.startEmbarkationProcess(this);
   }
   @Override
   public void startDisembarkation(){
      System.out.println(toString() + " starting disembarkation...");
      headquarters.startDisembarkationProcess(this);
   }
   @Override
   public void depart(){
      System.out.println(toString() + " departing...");
      // call a mediator method if necessary 
   }
   @Override
   public void travel(){
      System.out.println(toString() + " travelling...");
      // call a mediator method if necessary 
   }

   @Override
   public String toString() {
      return getClass().getName();
   }
} // end of class

The client code creates the mediator and various transport objects. The ship arrives in its own time, and once ready, starts the embarkation process and tells the mediator about the event. It doesn’t order the Transportable objects to embark; the HeadQuarters mediator controls the Transportable units. A code snippet follows:

// in client code

HeadQuarters hq = new HeadQuarters();

Transportable unit1 = new MechanisedFightingRobot(hq);
Transportable unit2 = new MechanisedFightingVehicle(hq);
TransportUnit ship  = new TransportShip(hq);

// ship arrives in its own time 
ship.arrive();

// the mediator controls the Transportable units in the 
// startEmbarkation() and startDisembarkation() methods 

ship.startEmbarkation(); 
ship.depart();
ship.travel();
ship.arrive();
ship.startDisembarkation();

// more code...

Pros and Cons of the Mediator Pattern

The Mediator pattern is one of the most commonly used patterns due to the decoupling of inter-component communication logic. A disadvantage is that the mediator object can easily grow in size and complexity until it becomes unmanageable.

We can implement the Mediator pattern in a number of ways. We can link the components directly to the mediator object as we have done here. We can also implement a Mediator as an Observer. The mediator then plays the role of the publisher, while the components act as subscribers to the mediator’s events.

What’s Next?

In the weeks ahead, we’ll continue examining some of the more useful design patterns. Stay tuned!

As always, please share your comments and questions.

Leave a Comment

Your email address will not be published. Required fields are marked *

Code like a Java Guru!

Thank You

We're Excited!

Thank you for completing the form. We're excited that you have chosen to contact us about training. We will process the information as soon as we can, and we will do our best to contact you within 1 working day. (Please note that our offices are closed over weekends and public holidays.)

Don't Worry

Our privacy policy ensures your data is safe: Incus Data does not sell or otherwise distribute email addresses. We will not divulge your personal information to anyone unless specifically authorised by you.

If you need any further information, please contact us on tel: (27) 12-666-2020 or email info@incusdata.com

How can we help you?

Let us contact you about your training requirements. Just fill in a few details, and we’ll get right back to you.

Your Java tip is on its way!

Check that incusdata.com is an approved sender, so that your Java tips don’t land up in the spam folder.

Our privacy policy means your data is safe. You can unsubscribe from these tips at any time.