Command
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:
- Separation of concerns, e.g. separate UI from the business logic. A button UI can be reused for other use cases.
- Decoupled code.
- Highly extensible, e.g. add new commands.
- 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.