Design Patterns Series 17 - Mediator Pattern

The Mediator Pattern :

The Mediator Pattern supports coordination between objects. it makes the coupling looser by having all objects report state changes to the mediator and take commands from the mediator.

When you use a mediator, you are encapsulating the interaction between objects. Each object no longer has to know in detail how to interact with the other objects. The coupling between objects goes from tight and brittle to loose and agile.

You can use the Mediator Pattern to "define an object that encapsulate 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."

The  Mediator Pattern should your first choice as a possible solution any time you have a set of objects that are tightly coupled. If every one of a series of objects has to know the internal details of the other objects, and maintaining those relationships becomes a problem, think of the Mediator Pattern. Using a mediator means the interaction code has to reside in only one place, and that makes it easier to maintain.

Using a mediator can hide a more series problem: If you have multiple objects that are too tightly coupled, your encapsulation may be faulty. Might be time to rethink how you have broken your program into objects.

The Mediator Pattern is something like a multiplexed Facade Pattern where, instead of supplanting the interface of a single object, you are making the multiplexed interface among multiple objects easier to work with.

Suppose we are working on a e-booking site, the site consists of four pages, home page, availability page, booking page,and exit page. The user can go form one page to another page without be restricted by certain path. He may go from availability page to exit page or from home page to exit page directly.

For example, The home page asks the user if he wants to book or exit,when the user do his selection, passes the matching state change to the mediator object to decide which page will be displayed. To give each page access to the mediator, we pass the mediator object to its constructor.

Each page should know its mediator and the user response(action) and have a Go() method to go to the next page, So we will create a base class, Page class, for all pages to inherit.

Page Class
public abstract class Page
{
    public Page(Mediator mediator)
    {
        Mediator = mediator;
        Response = "n";
    }
    protected Mediator Mediator { get; set; }
    protected String Response { get; set; }

    public abstract void Go();
}

Note, we passes the mediator object to the constructor and instantiate Response property with "n" for No. Now we will create four classes, one for each page. Each class will inherit Page class and implement its own Go() method.

Home Class
public class Home : Page
{
    public Home(Mediator mediator)
        : base(mediator)
    { }

    public override void Go()
    {
        Console.Write("Do You want to get Avaialability [y/n]?");
        try 
        {
            Response = Console.ReadLine();
        }
        catch
        {}
        if (Response.Equals("y", StringComparison.CurrentCultureIgnoreCase))
        {
            Mediator.Handle("Home.Availability");
        }
        else
        {
            Mediator.Handle("Home.Thanks");
        }
    }
}
Inside the Go() method we ask the user what he want to do. This question will differ from one page to another, in the home page it may go to availability page or to exit page so according to his response we call the Handle() method of mediator object and pass it the state or the path that user selected.

In the availability page he/she will choose to go to booking page or to exit page.

Availability Class
public override void Go()
{
    Console.Write("Are you ready to book [y/n]?");
    try 
    {
        Response = Console.ReadLine();
    }
    catch
    {}
    if (Response.Equals("y", StringComparison.CurrentCultureIgnoreCase))
    {
        Mediator.Handle("Availability.Booking");
    }
    else
    {
        Mediator.Handle("Availability.Thanks");
    }
}
In the booking page he/she will choose to book and exit or exit without booking.

Booking Class
public override void Go()
{
    Console.Write("Do You want to book Now[y/n]?");
    try 
    {
        Response = Console.ReadLine();
    }
    catch
    {}
    if (Response.Equals("y", StringComparison.CurrentCultureIgnoreCase))
    {
        Console.WriteLine("Thanks for your booking");
    }
    Mediator.Handle("Booking.Thanks");
}
Now there is no any pages to go so we will ask him to visit our site again.

Exit Class
public override void Go()
{
    Console.WriteLine("Please visit us again");
}
Now we will create our Mediator class and its Handle() method but the mediator should have a method that will take the user to home page so we will add another method beside Handle method call GetHome().

Mediator Class
public class Mediator
{
    private Page Page { get; set; }

    public void Handle(String path)
    {
        switch (path)
        {
            case "Home.Availability":
                Page = new Availability(this);
                Page.Go();
                break;
            case "Availability.Booking":
                Page = new Booking(this);
                Page.Go();
                break;
            case "Home.Exit":
            case "Availability.Exit":
            case "Booking.Exit":
                Page = new Exit(this);
                Page.Go();
                break;
        }
    }
    public Page GetHome()
    {
        return new Home(this);
    }
}
As you see the Handle method accepts the path selected by the user and set the page object with the appropriated page object to go to. To test our mediator just we will create an instance of Mediator class and go to the home page.

Mediator mediator = new Mediator();
Page homepage = mediator.GetHome();
homepage.Go();
Just the user went to home page, he will be asked if he want to get availability or not.
Do You want to get Avaialability [y/n]?
Here's a user who doesn't want to get availability.
Do You want to get Avaialability [y/n]?n
Please visit us again
Here's a user who really want to get availability.
Do You want to get Avaialability [y/n]?y
Are you ready to book [y/n]?
Here's a user who's not ready to make booking.
Do You want to get Avaialability [y/n]?y
Are you ready to book [y/n]?n
Please visit us again
Here's a user who's ready to make booking.
Do You want to get Avaialability [y/n]?y
Are you ready to book [y/n]?y
Do You want to book Now[y/n]?
Here's a user who's will not book now.
Do You want to get Avaialability [y/n]?y
Are you ready to book [y/n]?y
Do You want to book Now[y/n]?n
Please visit us again
Here's a user who made a booking.
Do You want to get Avaialability [y/n]?y
Are you ready to book [y/n]?y
Do You want to book Now[y/n]?y
Thanks for your booking
Please visit us again
As you can see, the mediator is able to coordinate all the pages. When something happens, a page lets the mediator know, and the mediator takes the appropriate next step.

you can download the full source code here

Comments

Popular posts from this blog

Android : How to change progress bar color at runtime programmatically?

ASP.Net MVC : Conditional Validation using ValidationAttribute

How to fire RowCommand event of nested GridView?