Saturday, July 22, 2023

Mediator Design Pattern


Mediator is an object that mediates between other objects to allow communication between them. Without mediator these objects will have to communicate directly. Let there be Sales and Marketing teams which want to communicate. They can send message from one to another using Send method.
void Send(string to, string from, string message);
Mediator design pattern is useful when multiple services are consumed by client classes in a uniform way.
We see the below example which is without Mediator Design Pattern. It will be converted into Mediator Design Pattern.
CASE1. Without Mediator Design Pattern
Create a console application in Visual Studio. Add a class file named as Team.cs Write the following code in it.
using System;

namespace MediatorDesignPattern
{
    public class Sales
    {
        public string Name { get; set; }
        public void Send(string to, string from, string message)
        {
            Console.WriteLine($"{message} sent from {from} to {to}");
        }
    }

    public class Marketing
    {
        public string Name { get; set; }
        public void Send(string to, string from, string message)
        {
            Console.WriteLine($"{message} sent from {from} to {to}");
        }
    }
}

In Program.cs, create the objects of the Sales and Marketing and invoke their Send methods.

namespace MediatorDesignPattern
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Sales t1 = new Sales();
            Marketing t2 = new Marketing();
            t1.Name = "Sales";
            t2.Name = "Marketing";
            t1.Send(t1.Name, t2.Name,"MessageX");
            t2.Send(t2.Name, t1.Name,"MessageY");
        }
    }
}

Run the app. We get the following message.
MessageX sent from Marketing to Sales
MessageY sent from Sales to Marketing

Next, update the code in Team.cs file as given below. Club the Sales and Marketing classes into Team class. The Team is abstract class which is inherited by them.

using System;

namespace MediatorDesignPattern
{
    public abstract class Team
    {
        public abstract string Name { get; set; }
        public abstract void Send(string to, string from, string message);
    }
    public class Sales : Team
    {
        public override string Name { get; set; }
        public override void Send(string to, string from, string message)
        {
            Console.WriteLine($"{message} sent from {from} to {to}");
        }
    }

    public class Marketing : Team
    {
        public override string Name { get; set; }
        public override void Send(string to, string from, string message)
        {
            Console.WriteLine($"This message '{ message}' sent from {from} to {to}");
        }
    }
}
Program.cs is as before.
namespace MediatorDesignPattern
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Sales t1 = new Sales();
            Marketing t2 = new Marketing();
            t1.Name = "Sales";
            t2.Name = "Marketing";
            t1.Send(t1.Name, t2.Name,"MessageX");
            t2.Send(t2.Name, t1.Name,"MessageY");
        }
    }
}

Run the app. We get the following message as before.
MessageX sent from Marketing to Sales
MessageY sent from Sales to Marketing

IMPORTANT: Here both Sales and Marketing teams are using their own Send method ( as they are overriding the abstract Send method in their own way)
But it is possible that there will be a mediator interface with Send method which is implemented uniformly by a mediator class. This class is used by both Sales and Marketing teams. Now, Send method is part of mediator and both Sales and Marketing use it. For this purpose, we create an interface named as IMediatorTeam which will be implemented by MediatorTeam class. Look at the following further updated code.
CASE2. With Mediator Design Pattern
Interface.
namespace MediatorDesignPattern
{
    internal interface IMediatorTeam
    {
        void SendMessage(string to, string from, string message);
    }
}
Implementation.

using System;

namespace MediatorDesignPattern
{
    public class MediatorTeam : IMediatorTeam
    {
        public void SendMessage(string to, string from, string message)
        {
            Console.WriteLine($"The '{message}' is sent from {from} to {to} via mediator.");
        }
    }
}
Update Team class. Both Sales and Marketing classes have MediatorTeam as property so that each class can use the method of MediatorTeam.

namespace MediatorDesignPattern
{
    public abstract class Team
    {
        public abstract string Name { get; set; }
        public abstract MediatorTeam Mediator { get; set; }
        public abstract void Send(string to, string from, string message);
    }
    public class Sales : Team
    {
        public override string Name { get; set; }
        public override MediatorTeam Mediator { get; set; }
        public override void Send(string to, string from, string message)
        {
            Mediator.SendMessage("Marketing", "Sales", "SALE 2023");
        }
    }
    public class Marketing : Team
    {
        public override string Name { get; set; }
        public MediatorTeam Mediator { get; set; }
        public override void Send(string to, string from, string message)
        {
            Mediator.SendMessage("Sales", "Marketing", "BOOM in market");
        }
    }
}
In Program.cs
namespace MediatorDesignPattern
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Sales t1 = new Sales();
            Marketing t2 = new Marketing();
            MediatorTeam mediatorTeam = new MediatorTeam();
            t1.Name = "Sales";
            t2.Name = "Marketing";
            t1.Send(t1.Name, t2.Name,"MessageX");
            t2.Send(t2.Name, t1.Name,"MessageY");
        }
    }
}
Run the app.
We get exception.
System.NullReferenceException: 'Object reference not set to an instance of an object.'
MediatorDesignPattern.Sales.Mediator.get returned null.

To remove this error, we initialize the MediatorTeam. Since MediatorTeam is a property of Sales and Marketing, we initialize this property in MediatorTeam.
IMediator Interface

namespace MediatorDesignPattern
{
    internal interface IMediatorTeam
    {
        void Register(Team member);
        void SendMessage(string to, string from, string message);
    }
}

Mediator class. Note that in the Register method team member is initialized.

using System;
using System.Collections.Generic;
using System.Linq;

namespace MediatorDesignPattern
{
    public class MediatorTeam : IMediatorTeam
    {
        private List _teams = new List();
        public void Register(Team member)
        {
            _teams.Add(member);
            member.Mediator=this;  // initialize
        }
        public void SendMessage(string to, string from, string message)
        {
            Team team = _teams.Where(t => t.Name == to)
            .First();
            if (team != null)
            {
                Console.WriteLine($"The message '{message}' is sent from {from} to {to} via mediator.");
            }
        }
    }
}
Team class is same as before.

namespace MediatorDesignPattern
{
    public abstract class Team
    {
        public abstract string Name { get; set; }
        public abstract MediatorTeam Mediator { get; set; }
        public abstract void Send(string to, string from, string message);
    }
    public class Sales : Team
    {
        public override string Name { get; set; }
        public override MediatorTeam Mediator { get; set; }
        public override void Send(string to, string from, string message)
        {
            Mediator.SendMessage("Marketing", "Sales", "SALE 2023");
        }
    }
    public class Marketing : Team
    {
        public override string Name { get; set; }
        public override MediatorTeam Mediator { get; set; }
        public override void Send(string to, string from, string message)
        {
            Mediator.SendMessage("Sales", "Marketing", "BOOM in market");
        }
    }
}
In Program.cs We use Register method.

namespace MediatorDesignPattern
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Sales t1 = new Sales();
            Marketing t2 = new Marketing();
            MediatorTeam mediatorTeam = new MediatorTeam();
            t1.Name = "Sales";
            t2.Name = "Marketing";
            mediatorTeam.Register(t1);
            mediatorTeam.Register(t2);
            t1.Send(t1.Name, t2.Name,"MessageX");
            t2.Send(t2.Name, t1.Name,"MessageY");
        }
    }
}

Note: Creating mediator class is not enough. We must have a method in mediator class which registers the types which will use the mediator class. For this we use Register method in the above example. We could also have used Deregister method to remove a type from the mediator class.

No comments:

Post a Comment

Hot Topics