Mediator Design Pattern in Rust

1. Definition

The Mediator Design Pattern defines an object that encapsulates how a set of objects interact. Instead of objects communicating directly with each other, they communicate through the mediator, reducing the dependencies between them.

2. Problem Statement

When there are multiple classes that interact with each other, it can lead to a system that's hard to maintain due to complex relationships and dependencies between these classes.

3. Solution

Introduce a mediator object that centralizes external communications. Instead of objects referring to each other directly, they use the mediator, thus decoupling the system's components and promoting single responsibility and open/closed principles.

4. Real-World Use Cases

1. Chat rooms where multiple users send messages, but the delivery of messages is handled centrally.

2. Air traffic control systems, where the mediator ensures that planes can take off and land without collisions.

3. GUI libraries where various widgets communicate through a central mediator.

5. Implementation Steps

1. Define the Mediator interface.

2. Create concrete classes that will use the Mediator.

3. Implement the Mediator to coordinate actions between the concrete classes.

6. Implementation in Rust Programming

// Define the Mediator trait
trait Mediator {
    fn notify(&self, sender: &str, event: &str);
}
// Concrete component: Button
struct Button {
    mediator: Box<dyn Mediator>,
}
impl Button {
    fn click(&self) {
        self.mediator.notify("Button", "click");
    }
}
// Concrete component: Label
struct Label {
    mediator: Box<dyn Mediator>,
}
impl Label {
    fn display(&self, text: &str) {
        println!("Label displays: {}", text);
    }
}
// Concrete Mediator: Dialog
struct Dialog {
    button: Option<Button>,
    label: Option<Label>,
}
impl Dialog {
    fn new() -> Self {
        Dialog {
            button: None,
            label: None,
        }
    }
    fn set_button(&mut self, button: Button) {
        self.button = Some(button);
    }
    fn set_label(&mut self, label: Label) {
        self.label = Some(label);
    }
}
impl Mediator for Dialog {
    fn notify(&self, sender: &str, event: &str) {
        if sender == "Button" && event == "click" {
            if let Some(label) = &self.label {
                label.display("Button was clicked!");
            }
        }
    }
}
// Client Code
fn main() {
    let mediator = Box::new(Dialog::new());
    let button = Button {
        mediator: mediator.clone(),
    };
    let label = Label {
        mediator: mediator.clone(),
    };
    let mut dialog = *mediator;
    dialog.set_button(button);
    dialog.set_label(label);
    dialog.button.as_ref().unwrap().click();
}

Output:

"Label displays: Button was clicked!"

Explanation:

1. We define a Mediator trait that will be the interface for all concrete mediators.

2. We create two components Button and Label. Button sends a notification when clicked, and Label displays text.

3. The Dialog struct acts as the concrete mediator, coordinating actions between Button and Label.

4. In the client code, when the button is clicked, the mediator ensures that the label displays the appropriate message.

The Mediator pattern centralizes external communications between components to simplify dependencies and make the system easier to maintain.

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 challenging because it communicates with many other objects.

3. You want to encapsulate the communication between objects in a separate mediator to make it easy to change independently.


Comments