Skip to content

Latest commit

 

History

History
124 lines (94 loc) · 8.97 KB

3E. Mediator.md

File metadata and controls

124 lines (94 loc) · 8.97 KB

Mediator

The Mediator pattern is used to simplify the communication between objects in a system by introducing a mediator object that encapsulates how these objects interact with each other.

image

// Mediator Interface
interface ControlUnit {
    void registerCombatVehicle(CombatVehicle combatVehicle);
    void sendMessage(String message, CombatVehicle combatVehicle);
}

// Concrete Mediator
class GroundControlUnit implements ControlUnit {
    private List<CombatVehicle> groundVehicles;

    public GroundControlUnit() {
        groundVehicles = new ArrayList<>();
    }

    public void registerCombatVehicle(CombatVehicle combatVehicle) {
        groundVehicles.add(combatVehicle);
        combatVehicle.setControlUnit(this);
    }

    @Override
    public void sendMessage(String message, CombatVehicle combatVehicle) {
        for (CombatVehicle a : groundVehicles) {
            if (a != combatVehicle) {
                a.receiveMessage(message);
            }
        }
    }
}

// Colleague Interface
interface CombatVehicle {
    void setControlUnit(ControlUnit controlUnit);
    void sendMessage(String message);
    void receiveMessage(String message);
}

// Concrete Colleague
class Tank implements CombatVehicle {

    private String name;
    private ControlUnit controlUnit;

    public Tank(String name) {
        this.name = name;
    }

    @Override
    public void setControlUnit(ControlUnit controlUnit) {
        this.controlUnit = controlUnit;
    }

    @Override
    public void sendMessage(String message) {
        controlUnit.sendMessage(message, this);
    }

    @Override
    public void receiveMessage(String message) {
        System.out.println(getName() + " received message: " + message);
    }

    public String getName() {
        return this.name;
    }
}

// Client code
public class Main {
    public static void main(String[] args) {
        ControlUnit groundControlUnit = new GroundControlUnit();

        CombatVehicle tank1 = new Tank("Tank 1");
        CombatVehicle tank2 = new Tank("Tank 2");
        CombatVehicle tank3 = new Tank("Tank 3");

        groundControlUnit.registerCombatVehicle(tank1);
        groundControlUnit.registerCombatVehicle(tank2);
        groundControlUnit.registerCombatVehicle(tank3);

        tank1.sendMessage("Hello everyone!");
        // Output:
        // Tank 2 received message: Hello everyone!
        // Tank 3 received message: Hello everyone!
    }
}

In this example, We define the ControlUnit interface. This interface declares the methods registerCombatVehicle() and sendMessage(). These methods define the communication protocol between the mediator and the vehicles. We implement the GroundControlUnit class, which serves as the concrete mediator. It maintains a list of registered combat vehicles and implements the mediator methods. The registerCombatVehicle() method adds a vehicle to the list, sendMessage() send messages to all other combat vehicles within a GroundControlUnit.

The CombatVehicle interface represents the colleagues (Combat Vehicle) that communicate through the mediator. It declares the methods setControlUnit(), sendMessage() and receiveMessage().

We implement the Tank class, which is a concrete colleague. It has a name and a reference to the mediator (ControlUnit). The setControlUnit() method sets the mediator reference, the sendMessage() directs the mediator to deliver the message to other CombatVehicle within a ControlUnit on behalf of the Tank instance.

Finally, in the Main class, we create a GroundControlUnit instnace and create multiple instances of Tank. We set the mediator for each Tank and register them with the according ControlUnit. Then, we simulate the CombatVehicle sending messages to other CombatVehicle via the mediator ControlUnit

The mediator pattern allows vehicles (colleagues) to communicate through the mediator (traffic control tower) without knowing each other directly. The tower handles the coordination and communication between the vehicles, enabling a decoupled and flexible system.

Mediator Design Pattern Summary

Use the Mediator pattern when it’s hard to change some of the classes because they are tightly coupled to a bunch of other classes.

  • The pattern lets you extract all the relationships between classes into a separate class, isolating any changes to a specific component from the rest of the components.
    • Assuming that without the mediator, each Tank instances directly interact with each other to coordinate their actions. This would lead to tight coupling, where each combat vehicle needs to know about the other combat vehicle and their specific behaviors. Any change in one combat vehicle class may require corresponding changes in other classes, making the system less flexible and harder to maintain.
    • By introducing the mediator, the combat vehicle only need to know about the mediator interface and communicate with it. They don't need to be aware of other combat vehicle classes or their specific implementations. The mediator encapsulates the interaction logic and coordination between the combat vehicle, reducing the direct dependencies between them.
      • This design choice provides flexibility because now the combat vehicle can focus on their own responsibilities without being tightly coupled to other classes. If there is a need to introduce new types of combat vehicle or change the coordination logic, we can modify the mediator implementation (say GroundControlUnit (E.g: LogisticsVehicle can only talk to each others within a ControlUnit, but cannot send messages to Tank)) without impacting the combat vehicle classes. This separation of concerns makes it easier to extend and modify the system.

Use the pattern when you can’t reuse a component in a different program because it’s too dependent on other components.

  • After you apply the Mediator, individual components become unaware of the other components. They could still communicate with each other, albeit indirectly, through a mediator object. To reuse a component in a different app, you need to provide it with a new mediator class.
    • Say if LogisticsVehicle and Tank classes have direct dependencies on each other, without the Mediator pattern, if we wanted to reuse the LogisticsVehicle and Tank classes in a different program, we would need to carry over their dependencies and tightly coupled interactions with each other. This tight coupling makes it difficult to reuse these components independently, as they rely on the specific behavior and presence of other components.
    • By introducing the ControlUnit interface and GroundControlUnit class as a mediator, we separate the dependencies and interactions between combat vehicles. The mediator encapsulates the communication and coordination logic, allowing each combat vehicle to interact indirectly through the mediator interface.
      • With this approach, the LogisticsVehicle and Tank classes become more self-contained and less dependent on each other. They can be reused in different programs or scenarios where the specific behavior or presence of other components may differ. The mediator acts as an intermediary, handling the communication and coordination between the vehicles, which promotes reusability of the individual components.

Use the Mediator when you find yourself creating tons of component subclasses just to reuse some basic behavior in various contexts.

  • Say we have some common behavior, such as notifying a ground control unit, that needs to be performed by both LogisticsVehicle and Tank in different situations. One approach to achieve this behavior reuse could be to create subclasses for each variation. For example, we could create subclasses like LogisticsVehicleInCity, LogisticsVehicleInCombat, TankInCity, TankInCombat, and so on, each representing a specific context where the common behavior is needed. However, this approach can quickly lead to a proliferation of subclasses. As the number of variations and contexts increases, we end up with an explosion of subclasses, resulting in a larger codebase.

  • Furthermore, adding new variations or modifying the behavior for a specific context requires creating additional subclasses, making the codebase even more complex. This tightly couples the behavior with the specific subclasses and creates a lot of dependencies, making it difficult to make changes or introduce new behavior without affecting the existing code.

  • By introducing a mediator, such as the GroundControlUnit, all relations between components will be contained within the mediator, it’d be easier to define entirely new ways for these components to collaborate by introducing new mediator classes, without having to change the components themselves. Since the mediator encapsulates the behavior and provides a consistent interface for the vehicles to interact with. This eliminates the need for creating numerous subclasses and promotes code reuse.