Gopalan Suresh Raj's Web Cornucopia
An Oasis for the parched Enterprise Services Engineer/Developer

    Design-Patterns-Behavioral-The Mediator

    Design-Patterns

    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.

    Sometimes, the interactions between these objects become so much that every object in the system ends up knowing about every other object. Lots of such interactions prevent an object from working without the support of a lot of other objects and thus the whole system ends up becoming one huge monolith - you ended up in the same mess you tried to un-entangle in the first place! Since the behavior may be distributed among different objects, it may sometimes become very difficult to change the system behavior without defining a lot of subclasses. Sometimes, complex protocols need to be managed and centralized points of access are desirable.

    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.

    Alt

    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

    Explanation - 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.

    Alt

    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

    1. Mediator (CDialog)
      • defines an interface for communicating with Colleague classes
    2. Concrete Mediator (CChildDialog)
      • implements cooperative behavior by coordinating colleague objects
      • knows and maintains its colleagues
    3. 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 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
    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.  

    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:

    Alt

    1. Mediator (Mediator)
      • defines an interface for communicating with Colleague classes
    2. Concrete Mediator (ConcreteMediator)
      • implements cooperative behavior by coordinating colleague objects
      • knows and maintains its colleagues
    3. Colleague (Colleague)
      • defines an interface for communicating with the Mediator class
    4. 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.

    package com.gopalan.designpatterns.behavioral.mediator;
    
    public 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();
    	}
    }
    

    Listing 1: Mediator.java

    package com.gopalan.designpatterns.behavioral.mediator;
    
    public 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();
    }
    

    Listing 2: Colleague.java

    package com.gopalan.designpatterns.behavioral.mediator;
    
    public 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);
    	}
    }
    

    Listing 3: ConcreteMediator.java

    package com.gopalan.designpatterns.behavioral.mediator;
    
    public 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);
    	}
    }
    

    Listing 4: OneColleague.java

    package com.gopalan.designpatterns.behavioral.mediator;
    
    public 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);
    	}
    }
    

    Listing 5: NextColleague.java

    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).

    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
    

    Also See

    Facade and Observer.

    Author Bibliography

    Gopalan Suresh Raj is a Senior Analyst, Software Architect, and Developer with expertise in multi-tiered systems development, enterprise service architectures, and distributed computing. He is also an active author, including contributions to Professional JMS Programming, Wrox Press, 2001, Enterprise Java Computing-Applications and Architecture, Cambridge University Press, 1999, and The Awesome Power of JavaBeans, Manning Publications Co., 1998. He has submitted papers at international fora, and his work has been published in numerous technical journals. Visit him at his Web Cornucopia© site (webcornucopia.com) or mail him at gopalan@webcornucopia.com.

    Back