C# Mediator Pattern

Summary: in this tutorial, you’ll learn about the C# Mediator pattern to encapsulate object interaction with loose coupling.

Introduction to the C# Mediator pattern

The Mediator pattern defines an object (mediator) that encapsulates the interactions of other objects. The Mediator pattern promotes loose coupling by preventing objects from referring to each other explicitly and allows you to manage their interaction independently.

In the Mediator pattern, a mediator object serves as an intermediary between objects that need to interact with each other.

So instead of allowing objects to interact directly with each other, they communicate via the mediator object. The Mediator object can also provide a centralized point of control to coordinate the interactions between the objects:

C# Mediator Pattern

The Mediator pattern promotes loose coupling between objects because they don’t need to know about each other’s details and they only have the information about the mediator object. This allows the system easier to maintain and modify because changes to one object won’t affect the others.

The following UML diagram illustrates the Mediator pattern:

C# Mediator Design Pattern

In this diagram:

  • Mediator defines an interface for communicating with Colleague objects.
  • ConcreteMediator implements cooperative behavior by coordinating Colleague objects. The ConcreteMeditor maintains a list of colleagues.
  • Colleague is an interface or abstract class that defines objects that need to interact with each other.
  • ConcreteColleague is a concrete class of the Colleague class.

The following shows an implementation of the Mediator pattern in C#:

namespace MediatorPattern;

public abstract class Mediator
{
    public abstract void Send(string message, Colleague colleague);
}

public abstract class Colleague
{
    private Mediator _mediator;

    public Colleague(Mediator mediator)
    {
        _mediator = mediator;
    }

    public virtual void Send(string message)
    {
        _mediator.Send(message, this);
    }
    public abstract void Receive(string message);
}


public class Colleague1 : Colleague
{
    public Colleague1(Mediator mediator) : base(mediator)
    {
    }

    public override void Receive(string message) => Console.WriteLine($"Colleague1 received {message}");
}
public class Colleague2 : Colleague
{
    public Colleague2(Mediator mediator) : base(mediator)
    {
    }

    public override void Receive(string message) => Console.WriteLine($"Colleague2 received {message}");
}

public class ConcreteMediator : Mediator
{
    public Colleague1 Colleague1;
    public Colleague2 Colleague2;

    public override void Send(string message, Colleague colleague)
    {
        if (colleague == Colleague1)
        {
            Colleague2.Receive($"{message} from {nameof(Colleague1)}");
        }
        else if (colleague == Colleague2)
        {
            Colleague1.Receive($"{message} from {nameof(Colleague2)}");
        }
    }
}
public class Program
{
    public static void Main(string[] args)
    {
        var mediator = new ConcreteMediator();
        var c1 = new Colleague1(mediator);
        var c2 = new Colleague2(mediator);

        mediator.Colleague1 = c1;
        mediator.Colleague2 = c2;

        c1.Send("Hello");
        c2.Send("Hi");

        c1.Send("Bye");
        c2.Send("Bye bye");

    }
}Code language: C# (cs)

How it works.

First, define a Mediator abstract class that has the Send() method for sending a message to a Colleague:

public abstract class Mediator
{
    public abstract void Send(string message, Colleague colleague);
}Code language: C# (cs)

Next, define the Colleague class abstract class that serves as a base class for other colleague objects:

public abstract class Colleague
{
    private Mediator _mediator;

    public Colleague(Mediator mediator)
    {
        _mediator = mediator;
    }

    public virtual void Send(string message)
    {
        _mediator.Send(message, this);
    }
    public abstract void Receive(string message);
}Code language: C# (cs)

The Colleague object knows the Mediator object. The Send() method uses the mediator object to send a message.

Then, define two Colleague concrete classes that inherit from the Colleague class:

public class Colleague1 : Colleague
{
    public Colleague1(Mediator mediator) : base(mediator)
    {
    }

    public override void Receive(string message) => Console.WriteLine($"Colleague1 received {message}");
}

public class Colleague2 : Colleague
{
    public Colleague2(Mediator mediator) : base(mediator)
    {
    }

    public override void Receive(string message) => Console.WriteLine($"Colleague2 received {message}");
}Code language: C# (cs)

The Colleague1 and Colleague2 classes implement the Receive() method that shows the message that they receive to the console.

After that, define the ConcreteMediator class that extends the Mediator class. The ConcreteMediator knows the Colleague1 and Colleague2 objects. Its Send() method coordinates the communication between these objects:

public class ConcreteMediator : Mediator
{
    public Colleague1 Colleague1;
    public Colleague2 Colleague2;

    public override void Send(string message, Colleague colleague)
    {
        if (colleague == Colleague1)
        {
            Colleague2.Receive($"{message} from {nameof(Colleague1)}");
        }
        else if (colleague == Colleague2)
        {
            Colleague1.Receive($"{message} from {nameof(Colleague2)}");
        }
    }
}Code language: C# (cs)

Finally, create the ConcreteMediator, Colleague1, and Colleague2 objects and send messages from Colleague1 and Colleague2:

public class Program
{
    public static void Main(string[] args)
    {
        var mediator = new ConcreteMediator();
        var c1 = new Colleague1(mediator);
        var c2 = new Colleague2(mediator);

        mediator.Colleague1 = c1;
        mediator.Colleague2 = c2;

        c1.Send("Hello");
        c2.Send("Hi");

        c1.Send("Bye");
        c2.Send("Bye bye");

    }
}Code language: C# (cs)

C# Mediator pattern variant

The following provides a variant of the Mediator pattern but in a more elegant way:

namespace MediatorPattern;

public abstract class Mediator
{
    public abstract void Send(string message, Colleague colleague);
}

public abstract class Colleague
{
    public Mediator? Mediator { set; get;}
    public virtual void Send(string message) => Mediator?.Send(message, this);
    public abstract void Receive(string message);
}

public class Colleague1 : Colleague
{
    public override void Receive(string message) => Console.WriteLine($"Colleague1 received {message}");
}
public class Colleague2 : Colleague
{
    public override void Receive(string message) => Console.WriteLine($"Colleague2 received {message}");
}

public class ConcreteMediator : Mediator
{

    private readonly List<Colleague> _colleagues = new();

    public void Register(Colleague colleague)
    {
        colleague.Mediator = this;
        _colleagues.Add(colleague);
    }


    public override void Send(string message, Colleague receiver)
    {
        // Send a message from the list of colleagues,
        // which are not receiver, to the receiver
        _colleagues
                .Where(c => c != receiver)
                .ToList()
                .ForEach(c => c.Receive(message));
    }
}
public class Program
{
    public static void Run(string[] args)
    {
        var mediator = new ConcreteMediator();
        var c1 = new Colleague1();
        var c2 = new Colleague2();

        mediator.Register(c1);
        mediator.Register(c2);

        c1.Send("Hello");
        c2.Send("Hi");

        c1.Send("Bye");
        c2.Send("Bye bye");

    }
}
Code language: C# (cs)

In this implementation:

  • The Colleague class has a Mediator property so that you can set the mediator object.
  • The ConcreteMediator class maintains a list of Colleague objects instead of referring to Colleague1 and Colleague2 directly.
  • The Register() method of ConcreteMediator class adds a Colleague to a Colleague list.
  • The Send() method sends a message from a list of colleagues except the receiver (colleague) to the receiver (colleague)

Mediator pattern in .NET

The MediatR is a package that is a simple mediator implementation in . NET.

Summary

  • Use the C# Mediator pattern to encapsulate object interaction with loose coupling.
Was this tutorial helpful ?