Mediator Design Pattern in Go

1. Definition

The Mediator Design Pattern defines an object that encapsulates how a set of objects interact. It promotes loose coupling by keeping objects from referring to each other explicitly, and it allows their interaction to be varied independently.

2. Problem Statement

When several components (or objects) need to communicate with each other, directly connecting them can lead to a system that's hard to maintain. Changes in one component might affect others, and adding new components can become complicated.

3. Solution

Introduce a mediator object that centralizes external communications. Components will no longer communicate with each other directly but through the mediator. This reduces dependencies between communicating objects, thereby reducing coupling.

4. Real-World Use Cases

1. Air traffic control center, which coordinates the movement of planes.

2. Chat rooms where multiple users interact through a central server.

3. GUI components where interactions between multiple UI elements are managed by a controller.

5. Implementation Steps

1. Define a Mediator interface that will define the contract for concrete mediators.

2. Create Concrete Mediators that implement the Mediator interface.

3. Components (or colleagues) will communicate with each other via the Mediator.

6. Implementation in Go

// Mediator interface
type Mediator interface {
	Send(message string, colleague *Colleague)
}
// Concrete Mediator
type ConcreteMediator struct {
	colleague1, colleague2 *Colleague
}
func (cm *ConcreteMediator) Send(message string, colleague *Colleague) {
	if colleague == cm.colleague1 {
		cm.colleague2.Receive(message)
	} else {
		cm.colleague1.Receive(message)
	}
}
// Colleague
type Colleague struct {
	name    string
	mediator Mediator
}
func (c *Colleague) Send(message string) {
	fmt.Println(c.name, "sends:", message)
	c.mediator.Send(message, c)
}
func (c *Colleague) Receive(message string) {
	fmt.Println(c.name, "received:", message)
}
// Client code
func main() {
	mediator := &ConcreteMediator{}
	colleague1 := &Colleague{name: "John", mediator: mediator}
	colleague2 := &Colleague{name: "Jane", mediator: mediator}
	mediator.colleague1 = colleague1
	mediator.colleague2 = colleague2
	colleague1.Send("Hello, Jane!")
	colleague2.Send("Hi, John!")
}

Output:

John sends: Hello, Jane!
Jane received: Hello, Jane!
Jane sends: Hi, John!
John received: Hi, John!

Explanation:

1. We've defined a Mediator interface and a ConcreteMediator to handle communication between Colleague objects.

2. Colleague objects know about the mediator and communicate exclusively through it.

3. In the client code, John sends a message to Jane, and Jane sends one back. All communication is done through the mediator.

7. When to use?

Use the Mediator Pattern when:

1. A set of objects communicate in well-defined but complex ways. The relationships are unstructured and hard to understand.

2. Reusing an object is difficult because it communicates with many other objects.

3. There's a need to specify and centralize external communications between components.


Comments