hubMediator

Description

The Mediator design pattern is a behavioral pattern that defines an object that centralizes communication between various components, instead of allowing them to communicate directly.

Real World Example

A great example of a mediator may be the airport and how traffic is controlled there. The air traffic control tower acts as a mediator, ensuring that airplanes and runways communicate through it rather than directly with each other.

Code Solution

To build our example, we will need three classes. Airplane and Runway will be objects that want to communicate with each other, and ControlTower will be our mediator responsible for this communication.

class Airplane
  attr_reader :code

  def initialize(code:)
    @code = code
  end
end

class Runway
  attr_reader :code, :status
  
  def initialize(code:, status: 'Available')
    @code = code
    @status = status
  end
end

class ControlTower
 attr_reader :code, :airplanes, :runways

 def initialize(code:)
   @code = code
   @airplanes = []
   @runways = []
 end

  def add_airplane(airplane)
    @airplanes << airplane
  end

  def add_runway(runway)
   @runways << runway
  end  
end
To use the Mediator pattern, we will have to add the tower attribute to both the Airplane and Runway classes. This attribute will be assigned when you add these objects to the ControlTower object.

class Airplane
  attr_reader :code
  attr_accessor :tower

  # ...
end

class Runway
  attr_reader :code, :status
  attr_accessor :tower
  
  # ...
end

class ControlTower
  # ...

  def add_airplane(airplane)
    @airplanes << airplane
    airplane.tower = self
  end

  def add_runway(runway)
    @runways << runway
    runway.tower = self
  end  
end
At this point we can start adding our communication methods. The first one will be request_take_off, this request will be sent from the Airplane to the ControlTower.

class Airplane
  # ...

  def request_takeoff
    tower.request_takeoff(self)
  end
end
After the ControlTower receives this request, it will display a log that such a request has been sent, then it will find an available Runway(or throw an error that there is none), and finally it will send a notification to the Runway.

class ControlTower
  # ...

  def request_takeoff(airplane)
    puts("#{airplane.code} send takeoff request!")
    
    notify_available_runway
  end

  def notify_available_runway
    available_runway.prepare_for_takeoff
  end

  private
  
  def available_runway
    runways.find do|runway|
      runway.status == 'Available'
    end || raise('There are no available runways!')
  end
end
After receiving the information, the Runway will have to change its status to Busy.

class Runway
  # ...

  def prepare_for_takeoff
    @status = 'Busy'
  end
end
Now we can use our code in action.

# Create airplanes
tupolev = Airplane.new(code: 'T154')
boeing = Airplane.new(code: 'B738')
airbus = Airplane.new(code: 'A318')

# Create runways
runway_a = Runway.new(code: 'RUNWAY_A')
runway_b = Runway.new(code: 'RUNWAY_B')

# Create tower
tower = ControlTower.new(code: 'TOWER_A')

# Add airplanes to tower
tower.add_airplane(tupolev)
tower.add_airplane(boeing)
tower.add_airplane(airbus)

# Add runways to tower
tower.add_runway(runway_a)
tower.add_runway(runway_b)

# Request takeoffs
tupolev.request_takeoff # => T154 send takeoff request!run
runway_a.status # => Busy

boeing.request_takeoff
runway_b.status # => Busy

# Raise an error when there are no available runways
airbus.request_takeoff # => RuntimeError: There are no available runways!
A pattern prepared in this way allows you to create a central place where communication between objects takes place. If you want to practice, you can add new features to my example, such as:
  • checking weather conditions on the runway before takeoff
  • adding communication between different towers
  • adding the runway departure function