Thursday, September 19, 2024

My exploration of Rust continues... Mediator Design Pattern in Rust...

 This design pattern lets you reduce chaotic dependencies between objects. The pattern restricts direct communications between the objects and forces them to collaborate only via a mediator object.

The Mediator interface declares a method used by components to notify the mediator about various events. The Mediator may react to these events and triggers specific components to execute different yet specific tasks.

The Base Component provides the basic functionality of storing a mediator's reference inside the component.

Concrete Components implement various functionality. They don't depend on other components.

Let me explain it to you with an example.

Suppose two students Ridit and Ishan take a lot of responsibility in a class. But something bad happens between them, and hence they don't talk to each other. So, here comes the ClassMonitor - as a Mediator between Ishan and Ridit - if Ridit does anything that needs Ishan to do another task, Ridit notifies the Class Monitor, and then the Class Monitor asks Ishan to execute the necessary task.


There are many uses for this design pattern. This design pattern can be found in most of the widget message-passing systems - individual widgets don't pass or communicate with each other - but they communicate through the container window.

For Android engineers - you remember how two fragments talk to each other through the container window.

Enjoy.

Happy learning.

Here's the source code for Mediator Pattern implemented through Rust.

use std::cell::RefCell;
use std::rc::Rc;

trait BaseStudent {
fn do_a(&self);
fn do_b(&self);
fn do_c(&self);
fn do_d(&self);
fn set_class_monitor(&mut self, monitor: Rc<RefCell<dyn ClassMonitar>>);
}

struct Ridit {
monitor: Option<Rc<RefCell<dyn ClassMonitar>>>,
}

struct Ishan {
monitor: Option<Rc<RefCell<dyn ClassMonitar>>>,
}

impl BaseStudent for Ridit {
fn do_a(&self) {
println!("Ridit does task A");
if let Some(ref monitor) = self.monitor {
monitor.borrow().notify("A".to_string());
}
}

fn do_b(&self) {
println!("Ridit does task B");
}

fn do_c(&self) {
println!("Ridit does task C");
}

fn do_d(&self) {
println!("Ridit does task D");
}

fn set_class_monitor(&mut self, monitor: Rc<RefCell<dyn ClassMonitar>>) {
self.monitor = Some(monitor);
}
}

impl BaseStudent for Ishan {
fn do_a(&self) {
println!("Ishan does task A");
}

fn do_b(&self) {
println!("Ishan does task B");
if let Some(ref monitor) = self.monitor {
monitor.borrow().notify("B".to_string());
}
}

fn do_c(&self) {
println!("Ishan does task C");
}

fn do_d(&self) {
println!("Ishan does task D");
}

fn set_class_monitor(&mut self, monitor: Rc<RefCell<dyn ClassMonitar>>) {
self.monitor = Some(monitor);
}
}

trait ClassMonitar {
fn notify(&self, event: String);
}

#[derive(Clone)]
struct ConcreteClassMonitor {
ridit: Option<Rc<RefCell<dyn BaseStudent>>>,
ishan: Option<Rc<RefCell<dyn BaseStudent>>>,
}

impl ClassMonitar for ConcreteClassMonitor {
fn notify(&self, event: String) {
if event == "A" {
println!("ClassMonitor reacting to event A and asking Ishan to do task C");
if let Some(ref ishan) = self.ishan {
ishan.borrow().do_c();
}
}

if event == "B" {
println!("ClassMonitor reacting to event B and asking Ridit to do task D");
if let Some(ref ridit) = self.ridit {
ridit.borrow().do_d();
}
}
}
}

fn main() {
// Create instances of students as trait objects
let ridit: Rc<RefCell<dyn BaseStudent>> = Rc::new(RefCell::new(Ridit { monitor: None }));
let ishan: Rc<RefCell<dyn BaseStudent>> = Rc::new(RefCell::new(Ishan { monitor: None }));

// Create ConcreteClassMonitor as trait object (ClassMonitar)
let class_monitor: Rc<RefCell<dyn ClassMonitar>> = Rc::new(RefCell::new(ConcreteClassMonitor {
ridit: Some(Rc::clone(&ridit)),
ishan: Some(Rc::clone(&ishan)),
}));

// Set the monitor in the students
ridit.borrow_mut().set_class_monitor(Rc::clone(&class_monitor));
ishan.borrow_mut().set_class_monitor(Rc::clone(&class_monitor));

// Test the functionality
ridit.borrow().do_a(); // This will notify the monitor and trigger Ishan to do task C
ishan.borrow().do_b(); // This will notify the monitor and trigger Ridit to do task D
}

No comments: