Make your own free website on Tripod.com

The Mediator (Behavioral) Design Pattern

Gopalan Suresh Raj

 

The Problem
One of the goals of object-oriented design is to distribute behavior among different objects. This kind of partitioning is good since it encourages reuse.

The Solution
Mediator is a behavioral pattern. This pattern helps to model a class whose object at run-time is responsible for controlling and coordinating the interactions of a group of other objects. It helps encapsulate collective behavior in a separate mediator object. Mediator promotes loose coupling by keeping objects from referring to each other explicitly, and allowing the designer to vary their interaction independently. Objects don’t need to know about each other, they just need to know their Mediator. Mediators are generally used where complex protocols must be managed, and when centralized access points are desirable. Thus, as defined by Gamma et al, "A mediator serves as an intermediary that keeps objects in a group from referring to each other explicitly".

The Mediator provides a common connection point, centralized (and subclassable) behavior and behavior management, all with a common interface. As the number of colleagues increase, the number of total communication pathways is vastly reduced. The Mediator Pattern thus allows programs to organize the relationships and interactions between controls, frames, nonvisual objects and events in a controller class specifically tailored for an application.

Explanation
Example Scenario 1 - One typical use of the Mediator Pattern in a Microsoft Foundation Classes application: A dialog box presents a collection of controls such as buttons, check boxes, list boxes, text fields etc. There are often dependancies between these controls on a dialog. For example, a disabled button gets enabled when text in an edit box changes. Selecting an entry in a list-box may change the contents of a text box. Once text appears in this field, other menus may get enabled. Different dialogs have different dependancies between the controls that they display. If each of the controls (either the list boxes or edit boxes) were to be coded with knowledge of other controls in the name of resolving dialog-specific dependancies, the whole system would end up a huge monolith mess. The problem can be solved by encapsulating the collective behavior in a separate mediator object. This mediator object would be responsible for controlling and coordinating the interactions of a group of control objects. This mediator object thus serves as an intermediary and prevents the objects from referring to each other explicitly. MFC uses the mediator pattern in the implementation of the CDialog class. Similarly, Java's AWT uses the mediator pattern in the implementation of the java.awt.Dialog class.
For example, aFontDialog can be a mediator between aListBox control and anEntryField edit control on a dialog box.

 

The aFontDialog object knows the controls in the dialog and coordinates their interaction. It acts as a hub of communication for these controls. The following object interaction diagram shows how the objects cooperate to handle a change in aListBox's selection.

  1. The Client creates aFontDialog and invokes it.

  2. The list box tells the FontDialog ( it's mediator ) that it has changed

  3. The FontDialog (the mediator object) gets the selection from the list box

  4. The FontDialog (the mediator object) passes the selection to the entry field edit box

Example Scenario 2 - One typical use of the mediator pattern in COM/OLE: Font resources are shared data structures, shared between applications and the GUI system. These font resources may be quite large and complex. If the system had a lot of components (either ActiveX or JavaBeans), each component would have to allocate, manage and destroy their own font resources. Every time each of these components repaint, they would have to create the font, select it into the device context, use it, select it out and then delete it. Imagine a container having a number of such components each one doing this on every repaint. It would be a hog on computing resources (both time spent and memory used) devoted to performing this tedius and inefficient task.
A separate mediator object can assist here by maintaining ownership of a reuseable pool of font resources on an application or system-wide basis without affecting the application or the GUI system. It would also help if the mediator object could 'cache' frequently-used fonts. This mediator object can thus act as a centralized point of access. That is exactly how COM has implemented it. The COM runtime sometimes acts as a mediator object. COM uses the mediator pattern to implement the
OleCreateFontIndirect() call. There are two interfaces you can use to access the created font. The IFont interface provides a v-table method for accessing font resources and the IFontDisp interface provides a dispatch interface for accessing font resources.
The solution is so powerful and elegant that large graphic bitmaps and other picture resources are also managed by implementing the mediator pattern. COM uses the mediator pattern in the implemention of the
OleCreatePictureIndirect() and OleLoadPicture() calls.

  1. Client calls OleLoadPicture() passing in the pointer to a stream that contains the picture's data (This is equivalent to calling OleCreatePictureIndirect() followed by IPersistStream::Load.)

  2. The COM/OLE Runtime creates a new picture object and initializes it from the contents of the stream

  3. It then returns a IPicture or IPictureDisp interface pointer to the client

As mentioned earlier, the COM runtime acts as the mediator object, holding on to graphics resources that can be shared among a host of clients. There are two interfaces you can use to access the created graphics. The IPicture interface provides a v-table method of accessing graphics resources and the IPictureDisp interface provides a dispatch interface method of accessing graphics resources. The COM Runtime acts as a separate mediator object that assists by maintaining ownership of a reuseable pool of graphics resources on an application or system-wide basis without affecting the application or the GUI system. It thus acts as a centralized point of access serving up references to graphics resources to a lot of clients.

Participants

Mediator (CDialog)
  • defines an interface for communicating with Colleague classes
Concrete Mediator (CChildDialog)
  • implements cooperative behavior by coordinating colleague objects
  • knows and maintains its colleagues
Colleague classes (CListBox, CEdit)
  • each colleague knows its mediator
  • each colleague communicates with its mediator whenever it would have otherwise communicated with another colleague

Collaborations
Colleagues send and receive requests from the Mediator object. The Mediator implements this co-operative behavior by serving as a communications hub and routing requests between the appropriate colleagues.

Checks and Balances of using this pattern

Plusses Minus
  • Changing the system behavior means just subclassing the mediator. Other objects can be used as is.
  • Since the mediator and its colleages are only tied together by a loose coupling, both the mediator and colleague classes can be varied and reused independant of each other.
  • Since the mediator promotes a One-to-Many relationship with its colleagues, the whole system is easier to understand (as opposed to a many-to-many relationship where everyone calls everyone else).
  • It helps in getting a better understanding of how the objects in that system interact, since all the object interaction is bundled into just one class - the mediator class.
  • Since all the interaction between the colleagues are bundled into the mediator, it has the potential of making the mediator class very complex and monolithically hard to maintain.

Notes on Implementation
There is no need to define an abstract mediator class if all colleagues are only going to work with one mediator. The abstracting is only necessary when colleagues need to work with different mediators.

Since Colleagues have to communicate with their Mediator whenever an event of interest occurs, there are different ways to implement the Mediator-Colleague communication.

  1. The Mediator can be implemented as an Observer (using the Observer pattern) and the Colleagues as Subjects, which send notification to the mediator whenever a state change occurs. The Mediator then can propagate the effect of that change to other colleagues.
  2. The other approach is to use some form of delegation. When sending a message to the Mediator, the Colleague passes itself as an argument, thus allowing the mediator to identify the sender.

Source Code
We implement a Mediator which acts as a communications hub between its colleagues. Our concrete mediator object, contains two colleagues -'The
OneColleague and the NextColleague'. Whenever the mediator's colleagueChanged() method is called (with a new Colleague passed in as an argument), it compares the contents of the new colleague with the contents of one of its colleagues (the OneColleague). If there is a match it sets the contents of its other colleague (the NextColleague) to match the first colleague (the OneColleague).

The Class Diagram for our implementation with description is shown below:

Mediator (Mediator)
  • defines an interface for communicating with Colleague classes
Concrete Mediator (ConcreteMediator)
  • implements cooperative behavior by coordinating colleague objects
  • knows and maintains its colleagues
Colleague (Colleague)
  • defines an interface for communicating with the Mediator class
Concrete Colleague classes (OneColleague, NextColleague)
  • each colleague knows its mediator
  • each colleague communicates with its mediator whenever it would have otherwise communicated with another colleague

The Object interaction diagram with explanation is shown below:

  1. The application creates a ConcreteMediator object.
  2. It invokes the createConcreteMediator() method on the aConcreteMediator object.
  3. The ConcreteMediator creates an instance of OneColleague passing in aConcreteMediator reference and a _text string.
  4. The ConcreteMediator creates an instance of NextColleague passing in aConcreteMediator reference and a _text string.
  5. The application then creates a aNewColleague object (an instance of OneColleague) passing in aConcreteMediator reference and a _text string.
  6. The application calls the mediator's colleagueChanged() method passing in aNewColleague reference.
  7. The mediator in response calls the getSelection() methods of both _aOneColleage and aNewColleague and checks if their _text strings match.
  8. If the strings on both _aOneColleage and aNewColleague match, it sets the value of the _text string in _aNextColleague to match the other _text strings.

Our source code implementation of the Mediator pattern coded in Java is shown below.

//
// Mediator.java
//

import java.io.*;

abstract class Mediator
{

public abstract void colleagueChanged(Colleague theChangedColleague);

public static void main(String args[])
{

ConcreteMediator aConcreteMediator = new ConcreteMediator();
aConcreteMediator.createConcreteMediator();

(aConcreteMediator.getOneColleage()).Display();
(aConcreteMediator.getNextColleague()).Display();

OneColleague newColleague = new OneColleague(aConcreteMediator, "OneColleague");

aConcreteMediator.colleagueChanged( (OneColleague)newColleague );

(aConcreteMediator.getOneColleage()).Display();
(aConcreteMediator.getNextColleague()).Display();

}


}
//
// Colleague.java
//

abstract class Colleague
{
private Mediator _aMediator;

public Colleague(Mediator m)
{
_aMediator = m;
}

public void changed()
{
_aMediator.colleagueChanged(this);
}

public Mediator getMediator()
{
return(_aMediator);
}

public abstract void Display();
}
//
// ConcreteMediator.java
//

class ConcreteMediator extends Mediator
{
private OneColleague _aOneColleague;
private NextColleague _aNextColleague;


public void colleagueChanged( Colleague theChangedColleague )
{

if ((( OneColleague)theChangedColleague).getSelection().equals( _aOneColleague.getSelection() ) )
{
_aNextColleague.setText( _aOneColleague.getSelection() );
}

}

public void createConcreteMediator()
{

_aOneColleague = new OneColleague(this, "OneColleague");
_aNextColleague = new NextColleague(this, "NextColleague");

}

public OneColleague getOneColleage()
{
return(_aOneColleague);
}

public NextColleague getNextColleague()
{
return(_aNextColleague);
}
}
//
// OneColleague.java
//

class OneColleague extends Colleague
{
private String _text;

public OneColleague(Mediator m, String t)
{
super( m );
_text = t;
}

public void setText(String text)
{
_text = text;
}

public String getSelection()
{
return(_text);
}

public void Display()
{
System.out.println("OneColleague = " + _text);
}
}
Whenever the mediator's colleagueChanged() method is called (with a new Colleague passed in as an argument), it compares the contents of the new colleague with the contents of one of its colleagues (the OneColleague). If there is a match it sets the contents of its other colleague (the NextColleague) to match the first colleague (the OneColleague).

You can get the Java source files from here Mediator.zip

Compile the files

F:\MyProjects\Mediator > javac Mediator.java ConcreteMediator.java Colleague.java OneColleague.java NextColleague.java

and run it with

F:\MyProjects\Mediator > java Mediator

OR

If you have JBuilder c/s, open the mediator.jpr, compile and run it.

//
// NextColleague.java
//

class NextColleague extends Colleague
{
private String _text;

public NextColleague(Mediator m, String t)
{
super( m );
_text = t;
}

public void setText(String text)
{
_text = text;
}

public String getSelection()
{
return( _text );
}

public void Display()
{
System.out.println("NextColleague = " + _text);
}
}

Also See
Facade and Observer.


Go to the Component Engineering Cornucopia page

This site was developed and is maintained by Gopalan Suresh Raj

This page has been visited times since October 14,1998.

Last Updated : Oct14, '98

If you have any questions, comments, or problems regarding this site, please write to me I would love to hear from you.


Copyright (c) 1997-98, Gopalan Suresh Raj - All rights reserved. Terms of use.

All products and companies mentioned at this site,are trademarks of their respective owners.