Mediator Design Pattern Example

Mediator Design Pattern: Sometimes we need a mechanism to facilitate the interaction between objects in a manner in that objects are not aware of the existence of other objects. In other words Mediator helps in achieving loose coupling between various objects by keeping objects from referring to each other explicitly, and it makes their interaction independent.

There are some point which we need to keep in mind while choosing this design pattern.

  • Mediator pattern helps to achieve loose coupling.
  • You should know if there is a coupling between the components, in you code
  • Once you identified the coupling between the components, you can think of  using this design pattern.
I will explain it with example how you can decouple your various components from each other with help of a Mediator. Mediator provides us the freedom to do modification in any component without touching other components.


Lets take an example: You want to design a UI Screen which will have many components to display. And on click of a button in one component, other components have to be updated, Components can update their texts, data models and  views based on the functional requirement.
For more clarity lets suppose a screen has five components, and these components are added in screen at Left, Right, Top, Bottom and  Center as shown in the below image.

Actions: On each button click all five/four Components need to update their view and data accordingly.

Now how you achieve this functionality for the UI screen?
There are multiple ways to do this.

  • One way of doing this is create a MainScreen class and in this class create all buttons, labels etc add all these components to the main screen. On click of each button update all components data and view using their object instances as set model and set text etc.
  • If the screen has a status bar the you need to create its components in main screen. Then you need to set Status bar text using one of its component  instance like statusLabel.setText("message").
The problem in this approach is all the components are tightly coupled with each other. So if you want to change any component then you have to change lots of code in MainScreen. for example if I want to replace component-4 with some new component then it is difficult to do.

So to avoid high coupling we can use Mediator design pattern, which make your code less coupled and each component can be modified independently. Means change in one component will not affect other component in any way.

Let see how to achieve this and how to implements design patterns.

Create Component-1, let name it as TopComponent  class, and add the UI buttons, text fields and required items to it. TopComponent class extends JPanel and implements ActionListener to handle buttons click as below.
TopComponent.java

public class TopComponent extends JPanel  implements ActionListener{
        private JButton button1;
          public TopComponent (){//constructor
                createUI();
         }
      private void createUI(){
               //create your items ,label and other UI components as required and add
             button1 = new JButton("Button1");
             button1.addActionListener(this);
             add(button1);
        }
     public void actionPerformed(ActionEvent e){
            if(e.getSource() == button1){
                //do the task as required
             }
      }
}//end class

Same way create Component-2 , Component-3, Component-4 and Component-5 classes as LeftComponet CenterComponent, RightComponent and BottomComponent.
Now we will apply the mediator design pattern and change these classes accordingly. To do this we can  follow these steps
  • Create a class UpdateEvent. 
  • Create an interface ComponentView.
  • Create a class Mediator.
  • Update components and MainScreen class for mediator related changes.
UpdateEvent .java

public class UpdateEvent {
       public static final int UPDATE = 0;
       public static final int SHOW = 1;
       public static final int HIDE = 2;
       public static final int LOAD = 3;
       private int eventType = -1;
      public UpdateEvent (int eventType){
                this.eventType  = eventType ;
       }
     public int getEventType(){
               return eventType;
     }
}//end class

Create an interface ComponentView as shown here
ComponentView.java

          public interface ComponentView{
                  updateView(UpdateEvent  e);
          }//end interface


Create a Mediator class as following
Mediator.java

public class Mediator{
       // array list which will hold the reference of all components and will interact with each other.
      private ArrayList<ComponentView>  componentViews = new ArrayList<ComponentView>();

       public void register(ComponentView view){

           componentViews .add(view);
      }

       public void fireUpdate(UpdateEvent e){

         //  mediator will send this event to all other components, only interested components will handle it.
            for(int i=0; i< componentViews.size(); i++){
                     ComponentView view = componentViews.get(i);
                     view.updateViwe (e);
                }
       }
}//end class

Now we will modify all the Component-1,2,3,4 and Component-5 classes as following 
TopComponent.java

public class TopComponent extends JPanel  implements ActionListener, ComponentView{
        private JButton button1;
        private Mediator mediator;
          public TopComponent (Mediator mediator){//constructor
                 this.mediator = mediator;
                 this.mediator.register(this);
                createUI();
         }
      private void createUI(){
               //create your items ,label and other UI components as required and add
             button1 = new JButton("Button1");
             button1.addActionListener(this);
             add(button1);
        }
     public void actionPerformed(ActionEvent e){
            if(e.getSource() == button1){
                //do the task as required
                mediator.fireUpdate(new UpdateEvent(UpdateEvent.UPDATE ));
               //This will send the update event to the mediator, and mediator will send
              // this event to all other components.
             }
      }
    //this  component will only interested in LOAD event sent by other component so handle it here
     public void updateView(UpdateEvent e){
            if(e.getEventType() == UpdateEvent.LOAD){
            // load the data and update the view accordingly
           }
      }
}//end class


LeftComponent.java


public class LeftComponent extends JPanel  implements ActionListener, ComponentView{
        private JButton button1;
        private JButton button2;
        private Mediator mediator;// keep an instance of mediator

        public LeftComponent (Mediator mediator){//constructor
                 this.mediator = mediator;
                 this.mediator.register(this);
                createUI();
         }

      private void createUI(){

               //create your items ,label and other UI components as required and add
             button1 = new JButton("Button1");
             button1.addActionListener(this);
             add(button1);
        }

     public void actionPerformed(ActionEvent e){
                 //This will send the load event to the mediator, and mediator will send
                 // this event to all other components.
            if(e.getSource() == button1){
                //do the task as required
                mediator.fireUpdate(new UpdateEvent(UpdateEvent.HIDE));
                //this will make other components to hide some info if needed
             }else   if(e.getSource() == button2){
                //do the task as required
                mediator.fireUpdate(new UpdateEvent(UpdateEvent.LOAD));
                //this will make other components to Load data again if needed
             }
      }
 //this component will only interested in UPDATE event so handle it here
   public void updateView(UpdateEvent e){
            if(e.getEventType() == UpdateEvent.UPDATE){
            //  update the view accordingly
           }
  }
}//end class


Similarly we can modify other components classes.

The MainScreen code may look similar as given here.
MainScreen.java

class MainScreen extends JFrame{
        private Mediator mediator;

         MainScreen (){
             super();
           mediator = new Mediator();
        }
    public void createUI(){
       //Create all your components here and add to the main screen
       TopComponent component1 = new TopComponent(mediator );
       LeftComponent component2 = new LeftComponent(mediator );
       CenterComponent component3 = new CenterComponent(mediator );
       RightComponent component4 = new RightComponent (mediator );
       BottomComponent component5 = new BottomComponent(mediator );//status bar

      getContentPane().add(component1, BorderLayout.PAGE_START);

      getContentPane().add(component2, BorderLayout.LINE_START);
      getContentPane().add(component3, BorderLayout.CENTER);
      getContentPane().add(component4, BorderLayout.LINE_END);
      getContentPane().add(component5, BorderLayout.PAGE_END);
   }

   public static void main(String[] arg){

        MainScreen mainScreen = new MainScreen ();
        mainScreen.createUI();
        mainScreen.setVisible(true);
    }

}//end class


In this approach if you want to replace any component with some other component then you only need to change the file lines of code as shown here.

Let say you want to replace RightComponent to some NewComponent then you just need to change the code as :

 //  RightComponent component4 = new RightComponent (mediator );

   NewComponent component4 = new NewComponent (mediator );

That's it you are done. No need to touch any other component code.


Mediator pattern is best suited for distributed modules or systems, Let see one example.


Problem
Here module A and module B are interacting with each other, and module A knows about module B (means A need to have an instance of module B). Same way Module B knows about module A, since on some activity in module  A, module B needs to be updated and vice versa.

So here both module A and B are tightly coupled with each other.



Solution : Coupling between module A and module B can be removed easily by introducing a Mediator here as A--M--B.  shown in below image.



Solution

Now module A even dose not know that there is any module B exist or not.

No comments :

Post a Comment