Sunday, September 15, 2024

Command Pattern in Rust - absolutely driven by intrinsic motivation...

Rust is a multi paradigm language. It supports Object Oriented Programming model (traits and dynamic binding),  functional programming model (closure, etc) and at the same time straight forward procedural programming model.

It won't force a developer to follow a specific paradigm, rather the developers are free to choose and pick any paradigm they want.

As I have come from a pure Object Oriented world, (C++, Java, Python), my tryst with Rust is mainly through the design patterns of Gang of Four book.

Today, i developed a sample example of Command Pattern using Rust.

Key Components of the Command Pattern:

  1. Command Interface:

    • Defines a common interface with a method (usually called execute) that all concrete commands must implement. This allows different types of commands to be invoked in a uniform way.
  2. Concrete Command:

    • Implements the command interface and defines the actual behavior. It binds the Receiver (the object that knows how to carry out the operation) to an action. The execute method is used to invoke the receiver's method(s).
  3. Invoker:

    • The invoker (e.g., a Waiter in a restaurant analogy) is responsible for triggering the command by calling the execute method. It does not know or care about the specifics of the command, only that it can execute it.
  4. Receiver:

    • The receiver (e.g., a Chef) is the object that performs the actual work. The command will delegate the action to the receiver.

Example Scenario: Restaurant

  • Invoker: The Waiter takes an order from the customer and passes it to the chef.
  • Command: The FoodCommand tells the chef what food to prepare.
  • Receiver: The Chef is responsible for actually preparing the food.

The advantage of the Command Pattern is that the Waiter (invoker) doesn’t need to know the details of how food is prepared. It just knows how to pass the command. You could have different kinds of commands (e.g., DrinkCommand, FoodCommand, DessertCommand), and the invoker can execute them all the same way.

Command Pattern Flow:

  1. The Invoker issues a command.
  2. The Concrete Command executes the command by calling methods on the Receiver.
  3. The Receiver performs the action.
Here is the source code of the command pattern in Rust...

#enjoy...

I must admit - I am yet to achieve the flow in Rust - the learning curve is really steep...

trait Command {
fn execute(&self);
}

//Receiver
struct Chef;

impl Chef {
pub(crate) fn prepare_food(&self) {
println!("Chef is preparing the food...");
}
}

struct FoodCommand{
chef : Chef,
}

impl Command for FoodCommand {
fn execute(&self) {
self.chef.prepare_food();
}
}
//Invoker
struct Waiter {
command : Box<dyn Command>,
}


impl Waiter {
fn new () -> Waiter{
let waiter = Waiter {
command : Box::new (FoodCommand {chef : Chef}),
};
waiter
}

fn pass_command_to_chef(&self){
self.command.execute();
}
}

fn main() {
let chef = Chef;
let food_command = FoodCommand {chef};
let waiter = Waiter {command : Box::new(food_command),};
waiter.pass_command_to_chef();
}

No comments: