Command Design Pattern

The Command Design Pattern is a behavioral design pattern that turns a request into a stand-alone object that contains all information about the request. This transformation allows for parameterization of methods with different requests, queuing of requests, and logging of the requests. It also provides support for undoable operations.

Components of Command Pattern

  1. Command: Declares an interface for executing an operation.
  2. ConcreteCommand: Implements the Command interface and defines a binding between a Receiver object and an action.
  3. Client: Creates a ConcreteCommand object and sets its receiver.
  4. Invoker: Asks the command to carry out the request.
  5. Receiver: Knows how to perform the operations associated with carrying out a request.

Here is an example of the Command Design Pattern:

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

// Command
pub trait Command {
    fn execute(&self);
}

// ConcreteCommand for turning on the light
pub struct TurnOnCommand {
    pub light: Rc<RefCell<Light>>,
}

impl Command for TurnOnCommand {
    fn execute(&self) {
        self.light.borrow_mut().turn_on();
    }
}

// ConcreteCommand for turning off the light
pub struct TurnOffCommand {
    pub light: Rc<RefCell<Light>>,
}

impl Command for TurnOffCommand {
    fn execute(&self) {
        self.light.borrow_mut().turn_off();
    }
}

// Invoker
pub struct RemoteControl{
    command: Option<Box<dyn Command>>,
}

impl RemoteControl {
    pub fn new() -> Self {
        RemoteControl { command: None }
    }

    pub fn set_command(&mut self, command: Box<dyn Command >) {
        self.command = Some(command);
    }

    pub fn press_button(&mut self) {
        if let Some(ref mut command) = self.command {
            command.execute();
        }
    }
}

// Receiver
pub struct Light {
    is_on: bool,
}

impl Light {
    pub fn new() -> Self {
        Light { is_on: false }
    }

    pub fn turn_on(&mut self) {
        self.is_on = true;
        println!("The light is on");
    }

    pub fn turn_off(&mut self) {
        self.is_on = false;
        println!("The light is off");
    }

    pub fn is_on(&self) -> bool {
        self.is_on
    }
}

// Client
fn main() {
    let light = Rc::new(RefCell::new(Light::new()));
    let turn_on_command = TurnOnCommand { light: Rc::clone(&light) };
    let turn_off_command = TurnOffCommand { light: Rc::clone(&light) };
    let mut remote = RemoteControl::new();


    remote.set_command(Box::new(turn_on_command));
    remote.press_button();
    println!("Light Status: {}", light.borrow().is_on()); // Borrow the Light instance


    remote.set_command(Box::new(turn_off_command));
    remote.press_button();
    println!("Light Status: {}", light.borrow().is_on()); // Borrow the Light instance

}

In this example:

  • Command is a trait that declares the execute method.
  • Light is the receiver that knows how to perform the operations.
  • TurnOnCommand and TurnOffCommand are concrete commands that implement the Command trait.
  • RemoteControl is the invoker that triggers the commands.

This pattern decouples the object that invokes the operation from the one that knows how to perform it.