1. What is the Mediator Pattern

The Mediator Pattern is a behavioral design pattern that reduces the direct dependencies between objects by transferring their communication to a mediator object. In the Mediator Pattern, objects no longer communicate directly with each other, but rather through the mediator object.

2. Characteristics and Advantages of the Mediator Pattern

The characteristics and advantages of the Mediator Pattern are as follows:

  • Reduces direct coupling between objects, lowering system complexity.
  • Simplifies communication between objects by having a mediator object handle their communication.
  • Centralizes control of interactions between objects, facilitating extension and maintenance.

3. Real-world Examples of the Mediator Pattern

The Mediator Pattern has many applications in real-life scenarios. For example, in an airport scheduling system, the dispatcher acts as the mediator, while the different modules such as airplanes and ground traffic serve as colleague classes communicate and coordinate through the dispatcher.

4. Implementation of the Mediator Pattern in Golang

4.1 Introduction to UML Class Diagram

Below is the UML class diagram for the Mediator Pattern in Golang:

Mediator Pattern in Golang

4.2 Example Introduction

In this example, we will implement a simple chat room application using the Mediator Pattern to manage communication between different users.

4.3 Implementation Step 1: Define Mediator Interface and Concrete Mediator

First, we define a mediator interface and a concrete mediator class:

type Mediator interface {
    registerColleague(colleague Colleague)
    sendMessage(message string, colleague Colleague)
}

type ConcreteMediator struct {
    colleagues map[string]Colleague
}

func (m *ConcreteMediator) registerColleague(colleague Colleague) {
    m.colleagues[colleague.getName()] = colleague
}

func (m *ConcreteMediator) sendMessage(message string, colleague Colleague) {
    for _, c := range m.colleagues {
        if c != colleague {
            c.receiveMessage(message)
        }
    }
}

4.4 Implementation Step 2: Define Colleague Interface and Concrete Colleague

Next, we define a colleague interface and concrete colleague classes:

type Colleague interface {
    receiveMessage(message string)
    sendMessage(message string)
    getName() string
}

type ConcreteColleagueA struct {
    mediator Mediator
    name     string
}

func (c *ConcreteColleagueA) receiveMessage(message string) {
    fmt.Printf("%s received message: %s\n", c.name, message)
}

func (c *ConcreteColleagueA) sendMessage(message string) {
    c.mediator.sendMessage(message, c)
}

func (c *ConcreteColleagueA) getName() string {
    return c.name
}

type ConcreteColleagueB struct {
    mediator Mediator
    name     string
}

func (c *ConcreteColleagueB) receiveMessage(message string) {
    fmt.Printf("%s received message: %s\n", c.name, message)
}

func (c *ConcreteColleagueB) sendMessage(message string) {
    c.mediator.sendMessage(message, c)
}

func (c *ConcreteColleagueB) getName() string {
    return c.name
}

4.5 Implementation Step 3: Managing Colleagues in the Mediator

In the specific mediator class, we need to implement the registerColleague method and the sendMessage method to manage communication between colleague classes:

func main() {
    mediator := &ConcreteMediator{
        colleagues: make(map[string]Colleague),
    }

    colleagueA := &ConcreteColleagueA{
        mediator: mediator,
        name:     "Colleague A",
    }
    colleagueB := &ConcreteColleagueB{
        mediator: mediator,
        name:     "Colleague B",
    }

    mediator.registerColleague(colleagueA)
    mediator.registerColleague(colleagueB)

    colleagueA.sendMessage("Hello, World!")
    colleagueB.sendMessage("Hi, there!")
}

In the main function, we create a specific mediator object and two specific colleague objects, then register the colleague objects through the mediator object and perform communication testing.