Skip to content

Latest commit

 

History

History

Command

  • Also known as: Action, Transaction

  • Four terms always associated with the command pattern are command, receiver, invoker(Sender) and client.

Problem

Suppose you are building a home automation system. There is a programmable remote which can be used to turn on and off various items in your home like lights, stereo, AC etc. It looks something like this. You can do it with simple if-else statements

if (buttonPressed == button1)
     lights.on()

But we need to keep in mind that turning on some devices like stereo comprises of many steps like setting cd, volume etc. Also we can reassign a button to do something else. By using simple if-else we are coding to implementation rather than interface. Also there is tight coupling.

So what we want to achieve is a design that provides

  1. loose coupling
  2. remote control should not have much information about a particular device.

Sections

Definitions

geeksforgeeks:

  • encapsulates a request as an object,

  • thereby letting us parameterize other objects with different (requests, queue or log requests),

  • and support undoable operations.

  • Parameterizing other objects with different requests: means that the button used to turn on the lights can later be used to turn on stereo or maybe open the garage door.

  • queue or log requests, and support undoable operations: means

    • Command’s Execute operation can store state for reversing its effects in the Command itself.
    • The Command may have an added unExecute operation that reverses the effects of a previous call to execute.
    • It may also support logging changes so that they can be reapplied in case of a system crash
  • example: command_remote_control_example.dart

tutorialspoint

  • Command pattern is a data driven design pattern and falls under behavioral pattern category.
  • A request is wrapped under an object as command and passed to invoker (Sender) object.
  • Invoker object looks for the appropriate object which can handle this command and passes the command to the corresponding object which executes the command.

When to use


Examples

import 'dart:developer';

/// We have created an interface Order which is acting as a command.
/// We have created a Stock class which acts as a request.
/// We have concrete command classes BuyStock and SellStock implementing Order interface
///     which will do actual command processing.
/// A class Broker is created which acts as an invoker object.
///     It can take and place orders.

/// Broker object uses command pattern to identify which object will execute which command based on the type of command.
///  CommandPatternDemo, our demo class, will use Broker class to demonstrate command pattern.

/// Step 1
/// Create a command interface.
abstract interface class Order {
  void execute();
}
/// Step 2
/// Create a request class.
class Stock {
  String _name = "ABC";
  int _quantity = 10;

  void buy() => log("Stock [ Name: $_name, Quantity: $_quantity  bought");

  void sell() => log("Stock [ Name: $_name, Quantity: $_quantity  sold");
}
/// Step 3
/// Create concrete classes implementing the Order interface.
class BuyStock implements Order {
  Stock _stock;

  BuyStock(this._stock);

  void execute() => _stock.buy();
}

class SellStock implements Order {
  Stock _stock;

  SellStock(this._stock);

  void execute() => _stock.sell();
}
/// Step 4
/// Create command invoker (Sender) class.
class Broker {
  List<Order> _orderList = [];

  void takeOrder(Order order) => _orderList.add(order);

  void placeOrders() {
    for (Order order in _orderList) {
      order.execute();
    }
    _orderList.clear();
  }
}
/// Step 5
/// Use the Broker class to take and execute commands.
void main() {
  Stock abcStock = Stock();

  /// Commands
  BuyStock buyStockOrder = BuyStock(abcStock);
  SellStock sellStockOrder = SellStock(abcStock);

  /// Invoker -> Sender
  Broker broker = Broker();
  broker.takeOrder(buyStockOrder);
  broker.takeOrder(sellStockOrder);

  broker.placeOrders();
}

/// Step 6: Verify the output.
/// Stock [ Name: ABC, Quantity: 10 ] bought
/// Stock [ Name: ABC, Quantity: 10 ] sold

Summery


How To Implement

  1. Declare the command interface with a single execution method.

  2. Start extracting requests into concrete command classes that implement the command interface.

    • Each class must have a set of fields for storing the request arguments along with a reference to the actual receiver object.
    • All these values must be initialized via the command’s constructor.
  3. Identify classes that will act as senders.

    • Add the fields for storing commands into these classes.
    • Senders should communicate with their commands only via the command interface.
    • Senders usually don’t create command objects on their own, but rather get them from the client code.
  4. Change the senders so they execute the command instead of sending a request to the receiver directly.

  5. The client should initialize objects in the following order:

    1. Create receivers.
    2. Create commands, and associate them with receivers if needed.
    3. Create senders, and associate them with specific commands

Advantages:

Disadvantages:

Real Life Uses:

Sources

  1. https://www.geeksforgeeks.org/command-pattern/
  2. https://www.tutorialspoint.com/design_pattern/command_pattern.htm
  3. https://github.com/scottt2/design-patterns-in-dart/tree/master/command
  4. https://refactoring.guru/design-patterns/command

(back to top)