Monday, September 16, 2024

My tryst with Rust - through the subject of Design Pattern - implementation of Chain of Responsibility...

 I remember in my earlier phase as a software engineer, how I mapped the MFC's command routing algorithm to Chain of Responsibility - and when finally it became clear, there was immense joy for me.

I always say to my students that we should approach a problem not only from how's point of view but also from why's point of views.

Curiosity is one of the greatest traits for a software engineer - nurture that, grow that - if you want to extract joy from your day-to-day journey as a software engineer.

So here we go.

My today's contribution to the learning community - Chain of Responsibility design pattern using Rust.

The Chain of Responsibility design pattern is a behavioural design pattern that allows a request to pass through a series of handlers. Each handler can either handle the request or pass it to the next handler in the chain. This design pattern promotes loose coupling between the sender and receiver of a request, allowing more flexibility in handling specific actions.

Key Features of the Chain of Responsibility Pattern:

Decoupling: 

The sender of the request doesn't need to know which handler will process the request.

Dynamic Chains: 

You can change the chain of handlers at runtime or configure it based on the application's needs.

Multiple Handlers: 

A request can be processed by a series of handlers in sequence until one of them handles the request, or it can propagate through all of them.

Here's the source code...

#[derive(Clone)]
struct Request{
request_type : String,
}
impl Request{
fn new(request_type : String) -> Self{
Request{
request_type : request_type,
}
}
}

trait Employee {
fn set_successor(&mut self, successor:Box<dyn Employee>);
fn handle_request(&mut self, request: Request) ;
}
struct ProjectManager {
successor: Option<Box<dyn Employee>>,
}

struct TeamLeader {
successor: Option<Box<dyn Employee>>,
}

struct GeneralManager{
successor: Option<Box<dyn Employee>>,
}

struct CTO {
successor: Option<Box<dyn Employee>>,
}

impl Employee for CTO{

fn set_successor(&mut self, successor: Box<dyn Employee>) {
//end of the chain... no successor
}
fn handle_request(&mut self, request : Request) {
println!("I am the CTO... I must handle the request as this is the end of the chain");
println!("The request is handled at the CTO level...");
}
}

impl Employee for GeneralManager {
fn set_successor(&mut self, successor: Box<dyn Employee>) {
self.successor = Some(successor);
}

fn handle_request(&mut self, request : Request) {
if request.request_type == "generalmanager_level" {
println!("Handling the request at the General Manager level");
}
else if let Some(ref mut successor) = self.successor {
println!("I am the general manager...Forwarding the request to CTO...");
successor.handle_request(request);
}
}
}

impl Employee for ProjectManager {
fn set_successor(&mut self, successor: Box<dyn Employee>) {
self.successor = Some(successor);
}

fn handle_request(&mut self, request: Request) {
if request.request_type == "projectmanager_level" {
println!("Handling the request at the Project Manager level");
} else if let Some(ref mut successor) = self.successor {
println!("I am the Project Manager...Forwarding the request to General Manager");
successor.handle_request(request);
}
}
}

impl Employee for TeamLeader {
fn set_successor(&mut self, successor: Box<dyn Employee>) {
self.successor = Some(successor);
}

fn handle_request(&mut self, request : Request) {
if request.request_type == "teamleader_level" {
println!("Handling the request at the team_leader level");
}
else if let Some(ref mut successor) = self.successor {
println!("I am the teamleader....Forwarding the request to Project Manager");
successor.handle_request(request);
}
}
}

fn main() {
let request1 = Request::new("teamleader_level".to_string());
let request2 = Request::new("generalmanager_level".to_string());
let request3 = Request::new("cto_level".to_string());
let mut cto = CTO{successor: None};
let mut gm = GeneralManager{successor: None};
let mut manager = ProjectManager {successor: None};
let mut teamLeader = TeamLeader {successor: None};
gm.set_successor(Box::new(cto));
manager.set_successor(Box::new(gm));
teamLeader.set_successor(Box::new(manager));
teamLeader.handle_request(request3);
}

Enjoy...

No comments: