Command

·

3 min read

A pattern that encapsulate a request as a stand-alone object. This object contains all information about request. As a result, it is possible to pass the requests as a method argument, delay or queue or group (think of bank execution) a request’s execution, and support undoable operations.

With this design pattern, we can achieve:

  1. Separation of concerns, e.g. separate UI from the business logic. A button UI can be reused for other use cases.
  2. Decoupled code.
  3. Highly extensible, e.g. add new commands.
  4. Testable and maintainable code.

    Structure

It consists of 5 components:

  • Command . An interface for executing an operation

  • ConcreteCommand . Connects receiver with an action and implements Execute

  • Client . Creates and schedules ConcreteCommands .

  • Invoker. Runs the commands, typically using callback. For example when a button is pressed.

  • Receiver . Performs the operation requested by the command.

Implementation

Classic Example

class Command {
public:
    virtual void Execute() const = 0;
    virtual ~Command() = default;
};

class BookVenueCommand: public Command {
    Venue* _venue;
    int _remainingSeats, _numberOfSeatstToBook;
    ...
    public:
        BookVenueCommand(Venue* venue) : _venue(venue) {}

        virtual void Execute() override {
                _remainingSeats = _venue->BookSeats(_numberOfSeatsToBook);
        }

        BookVenueCommand(Venue* venue, int numberSeatsToBook, TicketType ticketType) {
            ...
        }

        int GetNumberOfRemainingSeats() { return _remainingSeats; }
}

class ReverseTicketCommand: publicCommand {
    PriceHandler* _priceHandler;
    VneueType _venueType;
    ...
    public:
        virtual void Execute() override {
            ...
            double ticketPrice = _priceHandler->handlePrice(ticket);
            _tickets->push_back(ticket);

            std::cout << fmt::format("You reserved a ticket for {0} ...", ticket.getNumberOfSeats();
        }
}

int main() {
    BookVenueCommand* bookHugeTheather = new BookVenueCommand(_hugeTheatre, 10, ticketType);
    bookHugeTheather->Execute();

}

Macro Command

class MacroCommand : public Command {

    std::list<Command*>* _commands;

    public:
        MacroCommand() { _commands = new std::list<Command*>(); }

        virtual void Add(Command* command) {
            _commands->push_back(command);
        }

        virtual void Remove(Command* command) {
            _commands->remove(command);
        }

        virtual void Execute() {
            std::list<Command&>::iterator iterator;

            for(iterator=_commands->begin(); iterator != _commands->end(); ++iterator)
            {
                (*iterator)->Execute();
            }
        }    
}

int main()
{
    MacroCommand* bookingMacroCommand = new MacroCommand;

    BookVenueCommand* command1 = new BookVenueCommand(...);
    bookingMacroCommand->Add(command1);
    BookVenueCommand* command2 = new BookVenueCommand(...);
    bookingMacroCommand->Add(command2);

    bookingMacroCommand->Execute();

}

Undo Command

// Simply adding a new method in the command class
virtual void Undo() = 0;

// Save the commands into history.

// Can be combined with Memento design pattern and/or Prototype to handle history

Notes

Chain Responsibility passes a request sequentially along a dynamic chain of potential receivers until one of them handles it.

Handlers in Chain Responsibility can be implemented as Commands. Execute different operations over the same context object/ request.

Requests in Chain Responsibility can be implemented as Commands. Execute the same operation in a series of different contexts linked into a chain.

Command establishes unidirectional connection between senders and receivers.

Mediator eliminates direct connections between senders and receivers

Observers lets receivers to dynamically subscribe to and unsubscribe from receiving requests.

Credits

Command in C++ / Design Patterns (refactoring.guru)

Did you find this article valuable?

Support Random Notes by becoming a sponsor. Any amount is appreciated!