Advertisment

Command Pattern

author-image
CIOL Bureau
Updated On
New Update

Avijeet Dash and Satyabrata

Dash

Advertisment

Intent

Advertisment

Encapsulate a request as an object, thereby letting you parameterize clients

with different requests, queue or log requests, and support undoable operations.

In 30 Seconds

Advertisment

The catch here is "Encapsulate a request as an object". This

encapsulation helps in decoupling of sender from receiver of the request. A sender

is an object that invokes an operation, and a receiver is an object

that receives the request to execute a certain operation. With decoupling,

the sender has no knowledge of the receiver's interface. The term request

here refers to the command that is to be executed. The Command Pattern also

allows us to vary when and how a request is fulfilled. Therefore, a Command

Pattern provides us flexibility as well as extensibility.

In programming languages like C, function pointers are used to

eliminate giant switch statements. The object-oriented languages can use the

Command pattern to implement callbacks. The Command pattern lets invoker make

requests of unspecified application objects by turning the request itself to an

object. The object can be stored and passed around like other objects. The key

to this is pattern is Command interface, which declares the operations. In the

simplest form this interface includes an abstract Execute operation. Concrete

Command subclasses specify a receiver-action pair by storing the receiver as an

instance variable and by implementing Execute to invoke the request. The

receiver has the knowledge required to carry out the request.

Advertisment

Motivation

Let’s say we want to model the time evolution of a stock-trading program.



We need information on

  • What needs to be done, e.g. queued requests, alarms, conditions for

    action.
  • What is being done, e.g. which parts of a composite or distributed action

    have been completed.
  • What has been done, e.g. a log of undoable operations.
Advertisment

In other words, we want a kind of Reflection where what is being described is

workflow, and not just instantaneous data.

Forces

The number and implementation of program operations can vary. Operations

should be undoable, e.g. if the request was mistaken or a failure occurred in

the middle of a composite action.



Additional functionality related to an operation, such as logging, should be
decoupled from the operation itself.

Advertisment

Solution

Explicitly represent units of work as Command objects. Units of work depend

on the application and may range from reading and writing individual variables

to reformatting a text document. The interface of a Command object can be as

simple as a DoIt() method. Extra methods can be used to support undo and redo.

Commands can be persistent and globally accessible, just like normal objects.

Commands are parameterized by the variable to be written, word to delete,

place to move a figure to, etc. These parameters should be abstract, to avoid

embedded references which would hinder logging and communicating a Command. For

example, variables should be referred to by name, not pointer, and words should

be referred to by position in the text.

Advertisment

For the Command to perform its own undo, it needs to store information

destroyed by the operation. For example, the old value of the variable, the

contents of the word being deleted, and the old position of the figure need to

be stored. The UndoIt() method can use this to back out of the operation. For

safety, the Command could also store whether it has been done or not.

Commands are sent to a Command Processor, which may queue, prioritize, and

distribute them among machines or threads. The Command Processor can take care

of logging the Commands for future undo. It can also schedule a Command to occur

at a particular time or under specific conditions.

When all Commands are logged, storing the program state is redundant, because

the program state at any moment in time can be reconstructed by replaying the

log up to that point. Thus program state is essentially a cache of the

computation stored the log. This offers a simple way to implement undo: blow

away the current state and reconstruct the state at a previous time. To speed up

this operation, the state can be periodically written, or checkpointed,

to the log. Starting with the last checkpoint and replaying the Commands since

then can then reconstruct state.

We can apply Command Pattern when we want to:

  • Since Commands are encapsulated, their number and implementations can

    vary.
  • Commands have independent lifetimes. Thus they can be queued, stored, or

    transmitted.
  • Commands can be logged to support undo and redo, either because of user

    error or system crash. Commands can encapsulate their own undo procedure, or

    the log can be replayed to simulate undo.
  • Commands can be assembled into macros and scripts, which are also

    Commands. This is one way to represent atomic transactions. The composite

    Command registers its start and end in the log. If an error or failure

    occurs in the middle, all Commands are undone up to the start marker.
  • A Command can play the role of a Strategy or callback, decoupling the

    issuer of the Command from the Command's implementation. For example, a

    button can initiate an arbitrary operation depending on what its Command

    Strategy was. The same Command can come from several different sources.

Structure

Sample Code

The participants in this sample code are

  • Command Interface

It declares an interface for executing an operation.

  • BuyStockCommand and SellStockCommand

These concrete command classes define a binding between a Receiver

object(StockTrade class) and implement execute() by invoking the corresponding

operation(s) on Receiver.

  • Invoker

Asks the Command to carry out the request

  • Client

Creates a ConcreteCommand object and sets its receiver.

 // Command.java. 

public interface Command {
        public abstract void execute ( );
}
// Receiver class. StockTrade.java
class StockTrade {
        public void buy() {
                System.out.println("You want to buy stocks");
        }
public void sell() {
                System.out.println("You want to sell stocks ");
        }
}
// Invoker. Invoker.java
class Invoker {
        private Command buyCommand, sellCommand; 

        public Invoker( Command bc, Command sc) {
                buyCommand=bc;
sellCommand=sc;
        }
        void buy( ) {
                        buyCommand. execute( ) ;
        }
        void sell( ) {
                        sellCommand . execute( );
        }
}
//ConcreteCommand Class. BuyStockCommand.java

class BuyStockCommand implements Command { 

        private StockTrade stock; 

        public BuyStockCommand ( StockTrade st) {
                stock = st;
        }
        public void execute( ) {
                stock . buy( );
        } 

}
//ConcreteCommand Class. SellStockCommand.java

class SellStockCommand implements Command { 

        private StockTrade stock; 

        public SellStockCommand ( StockTrade st) {
                stock = st;
        }
        public void execute( ) {
                stock . sell( );
        } 

} 

// Client
public class Client {
                public static void main(String<> args) {
                        StockTrade stock=new StockTrade();
                        BuyStockCommand bsc = new BuyStockCommand(stock);
                        SellStockCommand ssc = new SellStockCommand(stock); 

Invoker testInvoker = new Invoker( bsc,ssc);
testInvoker.buy(); // Buy Shares
testInvoker.sell(); // Sell Shares
}
}

How Command pattern makes a difference in this sample code?

An alternate to command pattern here is we can directly invoke the operations

on the Receiver object and use switch() block to decide the appropriate

operations. But using switch() block is a bad design and it’s practice is

always discouraged.





Complexities simplified

  • Programs do many things, so there may be an excessive number of different

    Commands. This can be avoided by defining all operations in terms of a small

    number of kernel Commands, a so-called bottleneck design. The graphical

    interface, command-line interface, and network interface all create the same

    kernel Commands. Bottlenecks are commonly used in programming languages, where

    most syntax is defined in terms of a smaller, kernel language (the extraneous

    syntax being called "sugar"). Frameworks like ET++ use bottlenecks

    to minimize the number of methods that need to be overridden in a subclass.

    Some OpenDoc components create a bottleneck by sending OSA events to

    themselves rather than directly invoking methods. This makes it easy to log

    and override the actions of the component.
  • The undo operation can be a direct message to the Command Processor or can

    be a Command of its own. In the former case, undo do not enjoy the advantages

    of regular Commands, and have no audit trail. In the latter case, only a

    single-level of undo is possible, because a second undo will actually be a

    redo.
  • The above problems with undo can be avoided by using anti-Commands,

    which are Commands that happen to undo previous Commands. For example, x--

    is the anti-Command to x++.

    A Command should be able to produce its own anti-Command. Anti-Commands allow

    non-sequential undo, i.e. undoing a Command which wasn't the most recently

    done.
  • Commands can be passed along a Chain of Responsibility for consumption.
  • As Ralph Johnson points out in Transactions

    and Accounts
    (PLoP'95),

    sometimes the implementation of a Command needs to be changed or an operation

    needs to be associated with a different Command. Such changes can also be

    modeled as Commands, allowing them to be recorded and undone.

Related Patterns

The Factory Method pattern is sometimes used to provide a layer of

indirection between a user interface and command classes.



Memento can be used to keep state the command requires to undo its effect.

References:

tech-news