DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Please enter at least three characters to search
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Zones

Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks

The software you build is only as secure as the code that powers it. Learn how malicious code creeps into your software supply chain.

Apache Cassandra combines the benefits of major NoSQL databases to support data management needs not covered by traditional RDBMS vendors.

Generative AI has transformed nearly every industry. How can you leverage GenAI to improve your productivity and efficiency?

Modernize your data layer. Learn how to design cloud-native database architectures to meet the evolving demands of AI and GenAI workloads.

Related

  • Cutting-Edge Object Detection for Autonomous Vehicles: Advanced Transformers and Multi-Sensor Fusion
  • Writing DTOs With Java8, Lombok, and Java14+
  • Graph API for Entra ID (Azure AD) Object Management
  • A Comprehensive Guide to IAM in Object Storage

Trending

  • Spring and PersistenceContextType.EXTENDED
  • Contextual AI Integration for Agile Product Teams
  • Navigating the LLM Landscape: A Comparative Analysis of Leading Large Language Models
  • Role of Cloud Architecture in Conversational AI
  1. DZone
  2. Coding
  3. Languages
  4. The Mediator Pattern: Deep Dive

The Mediator Pattern: Deep Dive

An in depth guide to the mediator pattern in Java.

By 
John Thompson user avatar
John Thompson
·
Apr. 01, 16 · Tutorial
Likes (14)
Comment
Save
Tweet
Share
21.7K Views

Join the DZone community and get the full member experience.

Join For Free

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

Design Patterns: Elements of Reusable Object-Oriented Software

The Mediator Pattern, which is similar to the Command, Chain of Responsibility, and Iterator patterns, are part of the Behavioral pattern family of the Gang of Four design patterns. Behavioral patterns address responsibilities of objects in an application and how they communicate between them. The Mediator pattern allows the loose coupling between a set of objects in an application by handling the interactions between the objects.

Behavioral Patterns: Introduction

Well-designed enterprise applications are composed of lightweight objects with specific responsibilities divided between them in accordance with the Single Responsibility principle, one of the SOLID design principles. However, the benefits of an application composed of a large number of small objects come with a challenge – the communication between them. Objects need to communicate to perform business requirements, and as the number of objects grows, connections between the objects required can quickly become unmanageable. In addition, objects communicating between them needs to know about each other – they are all tightly coupled that violates the basics of the SOLID principles. The Mediator pattern is a proven solution to address such recurring challenges and the problems arising out of them.

The Mediator pattern says that instead of allowing a set of objects to directly interact with them, define an object (mediator) that will handle the interactions. What the mediator essentially says to such set of objects is “talk with me instead of talking among yourselves”. This figure conceptually shows how objects interact without and with a mediator.
 Mediator Pattern - Object interactions without and with mediator

The Mediator pattern, as shown in the above figure employs a mediator object to enable other objects of the application to interact without knowing each other’s identities. The mediator also encapsulates a protocol that objects can follow.

It is the mediator object that encapsulates all interaction logic between objects in a system. Therefore, if an existing object is updated with new interaction rules or a new object is added to the system, it is only the mediator object that you need to update. In the absence of the mediator, you would need to update all the corresponding objects that which to interact. Through the use of the Mediator Pattern your code becomes more encapsulated, thus changes are not as extensive.

Let’s now identify the participants of the Mediator Pattern.

Participants of the Mediator Pattern

Imagine a war zone where armed units are moving into the enemy’s territory. Armed units can include a soldier, tank, grenadier, and sniper units. The strategy being employed is that whenever one unit attacks, other units should stop attacking and take cover. To do so, the unit that is currently attacking needs to notify the other units.

In the programming world, you can easily model this requirement by creating classes for each armed unit. In each class, whenever its object is about to start attacking, you can implement the logic to notify objects of the other classes. Now, imagine that a new unit joins in. The consequence – all the existing classes need to be updated. In the worst case, imagine that the battle tactics change so that both the soldier and sniper units can now attack simultaneously. Again, the consequence is a lot of changes to the code base. We can address such challenges, similarly as done in real life. Place a Commander in a base camp that will act as the mediator. All units, instead of communicating between themselves will communicate with the mediator. The mediator based on the notifications received from some units can send a request to one or more other units to perform actions as requirements demand.

Let’s model the mediator with a Commander interface and a concrete CommanderImpl subclass of the interface. In the Commander interface, we can declare methods to send messages to objects representing armed units and also methods that armed unit’s objects can communicate with the Commander. In the CommanderImpl subclass, we will maintain a reference to the objects representing armed units and override the methods of Commander. For our example, let’s create an interface as ArmedUnit whose implementing classes will represent specific armed units. We will create two such concrete classes: SoldierUnit and TankUnit, and each of them will hold references to Commander.

With our example, let’s look at the participants of the Mediator pattern.

  • Mediator (Commander): Is an interface that declares methods for communicating with Colleague objects.
  • ConcreteMediator (CommanderImpl): Implements Mediator. This class maintains and coordinates Colleague objects.
  • Colleague (SoldierUnit and TankUnit): Communicates with its Mediator when their state changes and responds to requests from the Mediator.

Applying the Mediator Pattern

Going ahead with our example on armed units, let’s apply the Mediator pattern to it. We will start with the Mediator interface followed by the ConcreteMediator classes.

Commander.java

package guru.springframework.gof.mediator.mediator;

import guru.springframework.gof.mediator.colleague.ArmedUnit;

import guru.springframework.gof.mediator.colleague.SoldierUnit;

import guru.springframework.gof.mediator.colleague.TankUnit;

public interface Commander{

  void registerArmedUnits(ArmedUnit soldierUnit,ArmedUnit tankUnit);

  void setAttackStatus(booleanattackStatus);

  boolean canAttack();

  void startAttack(ArmedUnit armedUnit);

  void ceaseAttack(ArmedUnit armedUnit);

CommanderImpl.java

package guru.springframework.gof.mediator.mediator;


import guru.springframework.gof.mediator.colleague.ArmedUnit;
import guru.springframework.gof.mediator.colleague.SoldierUnit;
import guru.springframework.gof.mediator.colleague.TankUnit;

public class CommanderImpl implements Commander {
    ArmedUnit soldierUnit, tankUnit;
    boolean attackStatus = true;

    @Override
    public void registerArmedUnits(ArmedUnit soldierUnit, ArmedUnit tankUnit) {
        this.soldierUnit = soldierUnit;
        this.tankUnit = tankUnit;
    }

    @Override
    public void setAttackStatus(boolean attackStatus) {
        this.attackStatus = attackStatus;
    }

    @Override
    public boolean canAttack() {
        return attackStatus;
    }
    @Override
    public void startAttack(ArmedUnit armedUnit) {
        armedUnit.attack();
    }
    @Override
    public void ceaseAttack(ArmedUnit armedUnit) {
        armedUnit.stopAttack();
    }
}

In the Commander interface (Mediator) above, we declared five methods that we implemented in the CommanderImpl class (ConcreteMediator). In the CommanderImpl class, we added two reference to ArmedUnit (Colleague) and initialized them through the registerArmedUnits() method from Line 12 – Line 16. In this class, we also declared the attackStatus boolean variable in Line 10. This variable will hold the state whether any ArmedUnit is currently attacking. Colleague objects can set this state by calling the setAttackStatus() method that we wrote from Line 18 – Line 21. The canAttack() method that we wrote from Line 23 – Line 26 returns the current state of the attackStatus variable. The startAttack() and ceaseAttack() methods make calls to the attack() and stopAttack() methods on the ArmedUnit object passed as method parameter.

We will next move ahead and create the Colleague interface and the implementation classes.

ArmedUnit.java

package guru.springframework.gof.mediator.colleague;


public interface ArmedUnit {
    void attack();
    void stopAttack();

}

SoldierUnit.java

package guru.springframework.gof.mediator.colleague;


import guru.springframework.gof.mediator.mediator.Commander;

public class SoldierUnit implements ArmedUnit{

    private Commander commander;
    public SoldierUnit(Commander commander){
        this.commander=commander;
    }
    @Override
    public void attack()
    {
      if(commander.canAttack())
      {
          System.out.println("SoldierUnit: Attacking.....");
          commander.setAttackStatus(false);
      }
       else{
          System.out.println("SoldierUnit: Cannot attack now. Other units attacking....");
      }
    }
    @Override
    public void  stopAttack(){
        System.out.println("SoldierUnit: Stopped Attacking.....");
        commander.setAttackStatus(true);
    }


}

TankUnit.java

package guru.springframework.gof.mediator.colleague;

import guru.springframework.gof.mediator.mediator.Commander;

public class TankUnit implements ArmedUnit{
    private Commander commander;
    public TankUnit(Commander commander){
        this.commander=commander;
    }
    @Override
    public void  attack()
    {
        if(commander.canAttack())
        {
            System.out.println("TankUnit: Attacking.....");
            commander.setAttackStatus(false);
        }
        else{
            System.out.println("TankUnit: Cannot attack now. Other units attacking....");
        }
    }
    @Override
    public void  stopAttack(){
        System.out.println("TankUnit: Stopped attacking.....");
        commander.setAttackStatus(true);
    }
}

We first wrote the ArmedUnit interface with methods that the implementing SoldierUnit and TankUnit (Colleague) classes override. In both the implementing classes, we maintained a reference to the Commander object that we initialized through the constructor. Also, in both the classes, we implemented the overridden attack() and stopAttack() methods declared in the ArmedUnit interface. In the attack() method, we communicated with the mediator by calling the canAttack() method. If the method returns true, the Colleague object is confirmed that it can attack. We also wrote a stopAttack() method. The Mediator calls this method whenever it wants a unit to stop attacking. Notice that the Colleague classes are not aware of each other.  Programmatically we are not maintaining any reference between Colleague objects. They only communicate with the Mediator and the Mediator communicates with them. This is the essence of the Mediator pattern. Let’s write some test code so we can observe the Mediator Pattern in action.

CommanderImplTest.java

package guru.springframework.gof.mediator.mediator;

import guru.springframework.gof.mediator.colleague.ArmedUnit;
import guru.springframework.gof.mediator.colleague.SoldierUnit;
import guru.springframework.gof.mediator.colleague.TankUnit;
import org.junit.Test;

import static org.junit.Assert.*;


public class CommanderImplTest {

    @Test
    public void testMediator() throws Exception {
        Commander commander= new CommanderImpl();
        ArmedUnit soldierUnit=new SoldierUnit(commander);
        ArmedUnit tankUnit=new TankUnit(commander);
        commander.registerArmedUnits(soldierUnit, tankUnit);
        commander.startAttack(soldierUnit);
        commander.startAttack(tankUnit);
        commander.ceaseAttack(soldierUnit);
        commander.startAttack(tankUnit);
    }
}
In the test method of the CommanderImplTest class above, we created an object of type Commander and then objects, one each of SoldierUnit and TankUnit (Colleague objects). While creating the Colleague objects, we passed the Commander object as a constructor argument. At runtime, both the SoldierUnit and TankUnit objects will have reference to the mediator (Commander).

Next, we called the registerArmedUnits() of Commander passing the instantiated SoldierUnit and TankUnit objects. Now, the Commander have references to both the SoldierUnit and TankUnit (Colleague objects). Then, we called the startAttack() method passing the SoldierUnit and TankUnit objects one after the other. We expect that the TankUnit object on communicating back with the mediator (Commander) will find that another unit (which in our example is SoldierUnit) is already attacking and therefore the TankUnit object will not attack. It is only when the test method calls the ceaseAttck() method on Commander passing the currently attacking SoldierUnit object, the TankUnit should be able to attack. We verified this by calling the startAttack() method of Commander passing the TankUnit object.

The output of the test code is this.

-------------------------------------------------------
 T E S T S
-------------------------------------------------------

Running guru.springframework.gof.mediator.mediator.CommanderImplTest
SoldierUnit: Attacking.....
TankUnit: Cannot attack now. Other units attacking....
SoldierUnit: Stopped Attacking.....
TankUnit: Attacking.....
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.075 sec - in

The Mediator Pattern in the Spring Framework

In Spring MVC, there is a great example of the Mediator Pattern in action with how Spring MVC uses the Dispatcher Servlet in conjunction with the controllers.

Consider this diagram from the official Spring MVC documentation:

Spring MVC and the Mediator PatternHere you can see how the Front Controller (aka Dispatcher Servlet) acts as a mediator between web request, the controller objects, and the view objects. The individual controllers are unaware of each other. Each controller is also blissfully unaware of the context of the web request. The view templates are unaware of the each other, and the controllers. It is the responsibility of the Front Controller to decide with the controller and which view template to utilize when building a response to a web request.

Because of the usage of the Mediator pattern, changes in the individual controllers do not affect other controllers. Changes in the web request object, will not cause downstream changes in the controllers, nor views.

Summary

The Mediator pattern is considered one of the most important and widely adopted patterns. It is mainly because of its ability to encapsulate communication logic between sets of objects to fulfill some business requirements. A disadvantage of this pattern often being discussed in the developer community is that the mediator object can gradually get inflated and finally become overly complex. But, by following SOLID design principles, specifically the Single Responsibility principle, you can segregate the responsibilities of a mediator into separate classes.

Mediator (software) Mediator pattern Object (computer science)

Published at DZone with permission of John Thompson, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • Cutting-Edge Object Detection for Autonomous Vehicles: Advanced Transformers and Multi-Sensor Fusion
  • Writing DTOs With Java8, Lombok, and Java14+
  • Graph API for Entra ID (Azure AD) Object Management
  • A Comprehensive Guide to IAM in Object Storage

Partner Resources

×

Comments
Oops! Something Went Wrong

The likes didn't load as expected. Please refresh the page and try again.

ABOUT US

  • About DZone
  • Support and feedback
  • Community research
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 100
  • Nashville, TN 37211
  • support@dzone.com

Let's be friends:

Likes
There are no likes...yet! 👀
Be the first to like this post!
It looks like you're not logged in.
Sign in to see who liked this post!