Introduction
Simple scenario for mediator role would be to gather messages from each component (component is in some books referred as Colleague), and than mediator would broadcast that message to all other components.
In the next image we can see UML class diagram showing us dependencies between classes in implementation of Mediator pattern. Mediator and Component can be either abstract classes or interfaces, depending of the specific implementation.
From the security side, this is not very safe way to distribute all messages as a broadcast, but if implementation is not restricted with security rules (for example, UI classes) than it can have a good purpose to decouple communication between components.
Implementation
In this blog I’ll demonstrate simple chat application written in Java. Application will contain three tabs (three components, i.e. Colleague), and each of the tabs will be able to communicate with other tabs over the Mediator. For the message representation I’ll implement Intent class which instances will be able to contain string messages. Of course, current implementation of Intent can be extended and used to wrap other object types in a message as well.
Screenshot of the Mediator demo application
Current implementation of demo project is demonstrated in the next picture:
Source code from demo project can be downloaded from here.
Now, let’s take a look at the code.
This is simple Mediator interface that will be implemented in the Mediator class.
package com.testmediator;public interface IMediator {public void addComponent(IComponent _component);
public void send(Intent _message, IComponent _sender);
}
Class Mediator implements previous interface with two methods. Method addComponent is used for registering components, since the Mediator needs to know all actors of communication. Second method, send, simply broadcasts message to all of the components, except sender component.
package com.testmediator;import java.util.ArrayList;public class Mediator implements IMediator{private ArrayList<IComponent> components = new ArrayList<IComponent>();@Overridepublic void addComponent(IComponent _component) {
components.add(_component);}@Overridepublic void send(Intent _message, IComponent _sender) {
for(IComponent component : components){
if(!component.equals(_sender)){
component.receive(_message);}}}}
In the next code preview you can see Component interface that will be implemented in Component classes.
package com.testmediator;public interface IComponent {
public void receive(Intent _message);
public void send(Intent _message, IComponent _sender);
public IMediator getMediator();
}
As you can see in the Component implementation, each Component contains the reference to the same Mediator that is set up in the constructor. Other important methods are send and receive which implements sending message via Mediator instance and receiving message from Mediator broadcast, respectively. On the click of the send button message will be send to another component depending of selected recipient from the combo box.
package com.testmediator;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.awt.event.WindowAdapter;import javax.swing.JButton;import javax.swing.JComboBox;import javax.swing.JLabel;import javax.swing.JPanel;import javax.swing.JTextField;public class ComponentOne extends WindowAdapter implements IComponent, ActionListener{private IMediator m_mediator = null;
private JPanel m_panel = null;
private JComboBox<String> m_tabSelect = null;private JLabel m_label = null;
private JTextField m_text = null;
private JButton m_button = null;
private JLabel m_receivedMsg = null;
private Intent m_message = null;
public ComponentOne(IMediator mediator){
m_mediator = mediator;initGui();}@Overridepublic void receive(Intent _message) {
m_message = _message;if(m_message.getStringMsg("ToTab01")!=""){m_receivedMsg.setText(m_receivedMsg.getText() + " - " + m_message.getStringMsg("ToTab01"));}m_panel.repaint();}@Overridepublic void send(Intent _message, IComponent _sender) {
m_mediator.send(_message, this);}@Overridepublic IMediator getMediator() {
return m_mediator;}private void initGui(){
m_panel = new JPanel();m_tabSelect = new JComboBox<String>();
m_tabSelect.addItem("tab02");
m_tabSelect.addItem("tab03");
m_label = new JLabel("Tab01, enter the message: ");
m_text = new JTextField(20);m_button = new JButton("Send");
m_button.addActionListener(this);m_receivedMsg = new JLabel("Received: ");
m_panel.add(m_tabSelect);
m_panel.add(m_label);
m_panel.add(m_text);
m_panel.add(m_button);
m_panel.add(m_receivedMsg);
}public JPanel getPanel(){
return m_panel;}public Intent getMessage(){
return m_message;}@Overridepublic void actionPerformed(ActionEvent _event) {
Object source = _event.getSource();if(source.equals(m_button)){
Intent intent = new Intent();if(m_tabSelect.getSelectedItem() == "tab02"){intent.putStringMsg("ToTab02", m_text.getText());
}else if(m_tabSelect.getSelectedItem() == "tab03"){intent.putStringMsg("ToTab03", m_text.getText());
}send(intent,this);}}}
Simple Intent class represents wrapper for all messages. As you can see for now it contains only methods for putting string message and reading the same message. This class can be extended to wrap all other kinds of objects.
package com.testmediator;public class Intent {String m_stringMsgKey;
String m_stringMsg = "";
public void putStringMsg(String _key, String _value){m_stringMsgKey = _key;m_stringMsg = _value;}public String getStringMsg(String _messageKey){if(_messageKey == m_stringMsgKey){
return m_stringMsg;
}else{
return "";
}}}
Other classes from project are excluded from code preview since primary role of this blog was to present Mediator pattern. In a context of the project it is basically a simple desktop app developed using swing library.
Conclusion
Mediator pattern is pretty common to meet in a different solutions. For instance, Android platform is using very similar principle regarding communication between Activities using Intent objects as messages that will be broadcasted to all Activity instances in the app. This way complex peer-to-peer communication is replaced by the Mediator who is in charge of distributing messages, providing loose coupling between components which is in the almost all cases very good practice.