Mediator Design Pattern in Ruby

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 allows their interaction to be centralized in the mediator.

2. Problem Statement

When you have a set of objects that need to communicate with one another, direct connections between them can lead to a system that's hard to understand, maintain, and change. The more each object knows about others, the more entangled they become, leading to a tightly-coupled system.

3. Solution

The Mediator Pattern suggests centralizing external communications between objects in a mediator object. This way, objects don't communicate directly with each other but instead go through the mediator. This reduces dependencies between collaborating objects, thereby decreasing coupling.

4. Real-World Use Cases

1. Air traffic control systems, where the control tower (mediator) ensures that planes don't collide.

2. Chat rooms, where the server (mediator) ensures the message gets to the right recipient.

3. GUI libraries, where components like buttons and text boxes communicate through a form (mediator).

5. Implementation Steps

1. Define a mediator interface that declares methods for communicating with colleagues.

2. Implement concrete mediators that coordinate how colleagues interact.

3. Define colleague classes that need to communicate with each other but do so through the mediator.

6. Implementation in Ruby

# Step 1: Mediator Interface
class Mediator
  def notify(sender, event)
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end
end
# Step 2: Concrete Mediator
class ConcreteMediator < Mediator
  def set_colleague1(colleague)
    @colleague1 = colleague
  end
  def set_colleague2(colleague)
    @colleague2 = colleague
  end
  def notify(sender, event)
    if sender == @colleague1
      @colleague2.react_to_event(event)
    else
      @colleague1.react_to_event(event)
    end
  end
end
# Step 3: Colleague Classes
class BaseColleague
  def initialize(mediator)
    @mediator = mediator
  end
end
class Colleague1 < BaseColleague
  def trigger_event
    puts "Colleague1 triggers event"
    @mediator.notify(self, :some_event)
  end
  def react_to_event(event)
    puts "Colleague1 reacts to event"
  end
end
class Colleague2 < BaseColleague
  def trigger_event
    puts "Colleague2 triggers event"
    @mediator.notify(self, :some_event)
  end
  def react_to_event(event)
    puts "Colleague2 reacts to event"
  end
end
# Client Code
mediator = ConcreteMediator.new
colleague1 = Colleague1.new(mediator)
colleague2 = Colleague2.new(mediator)
mediator.set_colleague1(colleague1)
mediator.set_colleague2(colleague2)
colleague1.trigger_event

Output:

Colleague1 triggers event
Colleague2 reacts to event

Explanation:

1. Mediator is an abstract class that defines the contract for concrete mediators.

2. ConcreteMediator knows about colleague classes and decides how one colleague reacts when an event occurs in another colleague.

3. BaseColleague is a base class for colleagues, and it holds a reference to a mediator.

4. Colleague1 and Colleague2 are concrete colleagues. They trigger events and react to events but communicate via the mediator.

5. In the client code, events are triggered in Colleague1, and as a result, Colleague2 reacts, demonstrating indirect communication through the mediator.

7. When to use?

Use the Mediator Pattern when:

1. You want to reduce the complexity of communication between objects.

2. You want to centralize external communications.

3. You want to encapsulate the way objects interact and work together in a separate mediator object.


Comments