TypeScript Mediator Design Pattern

Ibrahim sengun
3 min readJan 25, 2024

--

What is mediator design pattern?

The mediator design pattern is a behavioral design pattern. It provides the ability to reduce complicated dependencies between objects. It achieves this by introducing a mediator object placed in the middle of communication between objects, thereby restricting objects from forming direct dependencies between them.

There are several terminologies in the mediator design pattern. These are:

  • Components: These are the objects that communicate with the mediator via reference.
  • Mediator: It defines the communication structure.
  • Concrete Mediator: It implements the mediator and stores the reference of components.
  • Client: This refers to the application or function that communicates with the system.

When should the mediator design pattern be used?

The mediator design pattern can be used when the code loses the ability to modify due to complex dependencies. In this case, with the help of the mediator design pattern, components that belong to the complex structure can be freed and made independent, making it easy to modify.

Another usage of the mediator design pattern can be when adding new basic behaviors to the system. Instead of creating more subclasses for a variety of basic behaviors, by using mediator classes, code can allows new behaviors to be formed without changing the components themselves.

How to implement mediator design pattern in TypeScript

Let’s apply the mediator design pattern to TypeScript. First, let’s imagine a scenario where we are handling a GUI interface with various types of components that communicate with each other. Even though initially, there aren’t any problems with this system, in later stages, as the number of components increases, it inadvertently causes increased complexity and dependencies in components. Consequently, we lose the independence of the components within the system. Due to their nature and direct communication between each other, they inadvertently become reliant on the components they communicate with, thereby losing their independence.

To solve this issue, we employ the mediator design pattern. With it, we create and place a mediator between communications in components. This way, by breaking the direct communication, we resolve the issues with dependencies and regain the components’ independence.

Mediator design pattern diagram

Mediator design pattern diagram

Mediator design pattern code

// Mediator interface
interface IMediator {
notify(sender: IButtons, event: string): void;
}

// Concrete Mediator
class ButtonMediator implements IMediator {
private button1!: Button;
private button2!: Button;

setButton1(button: Button): void {
this.button1 = button;
}

setButton2(button: Button): void {
this.button2 = button;
}

notify(sender: IButtons, event: string): void {
if (sender === this.button1) {
this.button2.handleEvent(event);
} else if (sender === this.button2) {
this.button1.handleEvent(event);
}
}
}

// Base Component
interface IButtons {
setMediator(mediator: IMediator): void;
handleEvent(event: string): void;
}

// Concrete Component
class Button implements IButtons {
private mediator!: IMediator;

constructor(private name: string) { }

setMediator(mediator: IMediator): void {
this.mediator = mediator;
}

handleEvent(event: string): void {
console.log(`${this.name} handling event: ${event}`);
}

click(): void {
console.log(`${this.name} clicked.`);
this.mediator.notify(this, 'click');
}
}

// Client
const mediator = new ButtonMediator();

const button1 = new Button('Button 1');
const button2 = new Button('Button 2');

button1.setMediator(mediator);
button2.setMediator(mediator);

mediator.setButton1(button1);
mediator.setButton2(button2);

button1.click();
// Output: Button 1 clicked. Notifying Button 2.
// Button 2 handling event: click

button2.click();
// Output: Button 2 clicked. Notifying Button 1.
// Button 1 handling event: click

--

--