Command Pattern Characteristics
- Command
- Holds the instructions and references to things that it needs in order for it to be executed
- Receiver
- Command will execute Receiver
- Invoker
- Invoker will execute Command, and will also keep track of all executed commands
- Client
- Client decides which command to schedule for execution
A command contains all the data to process the request now or at a later time. This means we could execute the command right away once the client schedule that command, or we could schedule all the commands to be executed later on in the lifetime of our application.
Example: AddToCartCommand
- The product which should be added to the cart
- The shopping cart
- A way to check stock availability
ICommand Interface
Because we may need to implement different command, we should create a ICommand interface.
It contains three methods. Execute() will execute the command. CanExecute() will check if a command can be execute of not. Undo() will undo all commands we executed before (using a Stack to maintain all executed commands)
1 | public interface ICommand |
Next we need to implement CommandManager, which is the Invoker component. It contains a Stack Data Structure to maintain the Commands list. When the Client (UI Button) adds a command to the CommandManager, it will be added to the list. (We can also add extra feature like introduce a delay of executing commands or redo all commands later).
1 | public class CommandManager |
Next we start to implement a command AddToCartCommand
1 | public class AddToCartCommand : ICommand |
It takes a shoppingCartRepository object, a productRepository object and a product
1 | public AddToCartCommand(IShoppingCartRepository shoppingCartRepository, |
Just a reminder, the Repository is a pattern for abstracting data access. We could have access the data store from a SQL DB, a web service or a CSV file, but our application doesn’t need to know that.
In our case, the shoppingCartRepository and the productRepository are both just a local Dictionary data structure.
1 | public bool CanExecute() |
CanExecute() will check if our productRepository actually has the required product.
1 | public void Execute() |
Execute() will decrease the product quantity by one and add it to shoppingCartRepository
1 | public void Undo() |
Undo() will put the product from shoppingCartRepository back to the productRepository
1 | var shoppingCartRepository = new ShoppingCartRepository(); |
Finally we just need to compose all the necessary objects on app starts. And add the commands to CommandManager.
Command Pattern in WPF
1 | public interface ICommand |
WPF application has built in ICommand interface. If we want to use our Command implementation (RemoveAllFromCartCommand) with this ICommand interface. We could bind the method with a UI button, then create a RelayCommand Class, which will invoke RemoveAllFromCartCommand method.
- UI Button ->(bind)-> ICommand method ->(invoke)-> RelayCommand ->(invoke)-> RemoveAllFromCartCommand
1 | <Button Margin="0 5 5 0" Command="{Binding RemoveAllFromCartCommand}">Clear</Button> |
1 | public System.Windows.Input.ICommand RemoveAllFromCartCommand { get; private set; } |
1 | RemoveAllFromCartCommand = new RelayCommand( |
1 | public class RelayCommand : System.Windows.Input.ICommand |
Summary
Command Pattern converts the request from Client to an object(ICommand). And the children implementation of the ICommand (AddToCartCommand) will take the Receiver as one of its input parameters (ShoppingCartRepository, ProductRepository). And it will implement the Execute() method, decide what should the Receiver do in Execute() method. And the Receiver should have all the needed information about the request(Product)