Browse TypeScript Design Patterns & Application Architecture

Implementing Mediator Pattern in TypeScript

Explore how to implement the Mediator Pattern in TypeScript, centralizing complex communications within a mediator class, and enhancing scalability and maintainability.

6.5.1 Implementing Mediator in TypeScript

The Mediator Pattern is a behavioral design pattern that centralizes complex communications and control logic between objects, promoting loose coupling and enhancing scalability. By introducing a mediator object, we can manage the interactions between various components, known as colleagues, without them needing to communicate directly. This approach simplifies the communication logic and makes the system more maintainable and flexible.

Understanding the Mediator Pattern

In a typical software system, components often need to communicate with each other. Without a mediator, each component would need to be aware of the others, leading to a tightly coupled system. The Mediator Pattern addresses this by introducing a mediator object that encapsulates the communication logic, allowing components to interact indirectly.

Key Components of the Mediator Pattern

  1. Mediator Interface: Defines the methods for communication between colleagues.
  2. ConcreteMediator: Implements the mediator interface and coordinates communication between colleague objects.
  3. Colleague Classes: Represent the components that interact with each other through the mediator.

Implementing the Mediator Pattern in TypeScript

Let’s dive into implementing the Mediator Pattern in TypeScript by creating a simple chat room application where users (colleagues) communicate through a central chat room (mediator).

Step 1: Define the Mediator Interface

The mediator interface declares methods for communication between colleague objects.

1// Mediator interface
2interface Mediator {
3  sendMessage(message: string, colleague: Colleague): void;
4  addColleague(colleague: Colleague): void;
5}

Step 2: Implement the ConcreteMediator Class

The ConcreteMediator class implements the Mediator interface and manages the communication between colleague objects.

 1// ConcreteMediator class
 2class ChatRoom implements Mediator {
 3  private colleagues: Colleague[] = [];
 4
 5  addColleague(colleague: Colleague): void {
 6    this.colleagues.push(colleague);
 7  }
 8
 9  sendMessage(message: string, sender: Colleague): void {
10    this.colleagues.forEach(colleague => {
11      if (colleague !== sender) {
12        colleague.receive(message);
13      }
14    });
15  }
16}

Step 3: Define the Colleague Classes

Colleague classes represent the components that interact through the mediator. They hold a reference to the mediator and use it to communicate with other colleagues.

 1// Colleague class
 2class Colleague {
 3  constructor(private mediator: Mediator, private name: string) {
 4    this.mediator.addColleague(this);
 5  }
 6
 7  send(message: string): void {
 8    console.log(`${this.name} sends: ${message}`);
 9    this.mediator.sendMessage(message, this);
10  }
11
12  receive(message: string): void {
13    console.log(`${this.name} receives: ${message}`);
14  }
15}

Step 4: Demonstrate Communication Through the Mediator

Let’s create a simple demonstration of how the mediator facilitates communication between colleagues.

 1// Create a chat room (mediator)
 2const chatRoom = new ChatRoom();
 3
 4// Create colleagues
 5const alice = new Colleague(chatRoom, 'Alice');
 6const bob = new Colleague(chatRoom, 'Bob');
 7const charlie = new Colleague(chatRoom, 'Charlie');
 8
 9// Send messages
10alice.send('Hello, everyone!');
11bob.send('Hi, Alice!');
12charlie.send('Hey, folks!');

Handling Different Types of Messages or Events

The mediator can be extended to handle different types of messages or events. For instance, we can modify the sendMessage method to include message types or priorities.

1// Enhanced sendMessage method
2sendMessage(message: string, sender: Colleague, type: string = 'text'): void {
3  this.colleagues.forEach(colleague => {
4    if (colleague !== sender) {
5      colleague.receive(`[${type.toUpperCase()}] ${message}`);
6    }
7  });
8}

Promoting Scalability with the Mediator Pattern

One of the key advantages of the Mediator Pattern is its ability to promote scalability. By centralizing the communication logic, we can easily add new colleagues without modifying existing ones. This is particularly useful in large systems where components frequently change or evolve.

Adding New Colleagues

To add a new colleague, simply create a new instance of the Colleague class and pass the mediator as a parameter. The mediator will automatically manage the communication.

1// Adding a new colleague
2const dave = new Colleague(chatRoom, 'Dave');
3dave.send('Hello, I am new here!');

Considerations and Potential Challenges

While the Mediator Pattern offers numerous benefits, it also introduces some challenges that need to be considered:

  1. Increased Complexity: The mediator can become a complex component if it manages too many interactions or complex logic. It’s essential to keep the mediator’s responsibilities clear and well-defined.

  2. Performance Impacts: In systems with a large number of colleagues or frequent interactions, the mediator can become a bottleneck. It’s crucial to optimize the mediator’s logic and ensure it scales with the system’s needs.

  3. Single Point of Failure: The mediator acts as a central hub for communication. If it fails, the entire communication system can be disrupted. Implementing redundancy or failover mechanisms can mitigate this risk.

Visualizing the Mediator Pattern

To better understand the Mediator Pattern, let’s visualize the interaction between the mediator and colleagues using a class diagram.

    classDiagram
	    class Mediator {
	        <<interface>>
	        +sendMessage(message: string, colleague: Colleague)
	        +addColleague(colleague: Colleague)
	    }
	
	    class ChatRoom {
	        +colleagues: Colleague[""]
	        +addColleague(colleague: Colleague)
	        +sendMessage(message: string, sender: Colleague)
	    }
	
	    class Colleague {
	        -mediator: Mediator
	        -name: string
	        +send(message: string)
	        +receive(message: string)
	    }
	
	    Mediator <|.. ChatRoom
	    ChatRoom o-- Colleague
	    Colleague --> Mediator : uses

Try It Yourself

Now that we’ve explored the Mediator Pattern, it’s time to experiment with the code. Here are some suggestions to modify the example:

  1. Add a New Message Type: Extend the sendMessage method to handle different message types, such as image or video.

  2. Implement a Logging Feature: Add a logging mechanism to the ChatRoom class to record all messages sent through the mediator.

  3. Create a Private Messaging System: Modify the mediator to support private messages between specific colleagues.

References and Further Reading

Knowledge Check

Before we conclude, let’s reinforce what we’ve learned with a few questions:

  • How does the Mediator Pattern promote loose coupling between components?
  • What are some potential challenges of using the Mediator Pattern?
  • How can the mediator be optimized to handle a large number of interactions?

Embrace the Journey

Remember, mastering design patterns is a journey. As you continue to explore and implement patterns, you’ll gain a deeper understanding of how to build scalable and maintainable systems. Keep experimenting, stay curious, and enjoy the journey!

Quiz Time!

Loading quiz…
Revised on Thursday, April 23, 2026