|
| 1 | +<div id="top"></div> |
| 2 | + |
| 3 | +# Command |
| 4 | + |
| 5 | +- Also known as: Action, Transaction |
| 6 | + |
| 7 | +- **Four terms always associated with the command pattern are `command`, `receiver`, `invoker(Sender)` and `client`.** |
| 8 | + |
| 9 | +## Problem |
| 10 | + |
| 11 | +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_ |
| 12 | + |
| 13 | +``` |
| 14 | +if (buttonPressed == button1) |
| 15 | + lights.on() |
| 16 | +``` |
| 17 | + |
| 18 | +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. |
| 19 | + |
| 20 | +So what we want to achieve is a design that provides |
| 21 | + |
| 22 | +1. loose coupling |
| 23 | +2. remote control should not have much information about a particular device. |
| 24 | + |
| 25 | +--- |
| 26 | + |
| 27 | +<img style="background-color:#554777" align="center" src = "assets/structure_command.png"> |
| 28 | + |
| 29 | +<details> |
| 30 | + <summary> <h2 style="display: inline;"> Sections</h2> </summary> |
| 31 | + |
| 32 | +- [Definitions](#Definitions) |
| 33 | +- <a href="#when-to-use">When to use</a> |
| 34 | +- [What problems can it solve](#What-problems-can-it-solve) |
| 35 | +- <p><a href="#Examples">Examples</a></p> |
| 36 | +- <p><a href="#Summery">Summery</a></p> |
| 37 | +- <p><a href="#How_To_Implement">How_To_Implement</a></p> |
| 38 | +- [Sources](#Sources) |
| 39 | +</details> |
| 40 | + |
| 41 | +## Definitions |
| 42 | + |
| 43 | +### geeksforgeeks: |
| 44 | + |
| 45 | +- encapsulates a request as an object, |
| 46 | +- thereby letting us parameterize other objects with different (`requests`, `queue` or `log requests`), |
| 47 | +- and support `undoable operations`. |
| 48 | +- 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. |
| 49 | +- queue or log requests, and support undoable operations: means |
| 50 | + |
| 51 | + - Command’s Execute operation can store state for reversing its effects in the Command itself. |
| 52 | + - The Command may have an added unExecute operation that reverses the effects of a previous call to execute. |
| 53 | + - It may also support logging changes so that they can be reapplied in case of a system crash |
| 54 | + |
| 55 | +- example: command_remote_control_example.dart |
| 56 | + |
| 57 | +### tutorialspoint |
| 58 | + |
| 59 | +- `Command pattern` is a data driven design pattern and falls under behavioral pattern category. |
| 60 | +- A request is wrapped under an object as `command` and passed to `invoker` (Sender) object. |
| 61 | +- Invoker object looks for the appropriate object which can handle this command and passes the command to the corresponding object which executes the command. |
| 62 | + |
| 63 | +<h2 id="when-to-use" >When to use</h2> |
| 64 | + |
| 65 | +--- |
| 66 | + |
| 67 | +<h2 id="Examples"> Examples</h2> |
| 68 | + |
| 69 | +<img style="background-color:#554777" src = "assets/command_pattern_uml_diagram_stock_example.jpg"> |
| 70 | + |
| 71 | +- <a href="command_stock_order_example.dart/"> command_stock_order_example</a> |
| 72 | + |
| 73 | +```dart |
| 74 | +
|
| 75 | +import 'dart:developer'; |
| 76 | +
|
| 77 | +/// We have created an interface Order which is acting as a command. |
| 78 | +/// We have created a Stock class which acts as a request. |
| 79 | +/// We have concrete command classes BuyStock and SellStock implementing Order interface |
| 80 | +/// which will do actual command processing. |
| 81 | +/// A class Broker is created which acts as an invoker object. |
| 82 | +/// It can take and place orders. |
| 83 | +
|
| 84 | +/// Broker object uses command pattern to identify which object will execute which command based on the type of command. |
| 85 | +/// CommandPatternDemo, our demo class, will use Broker class to demonstrate command pattern. |
| 86 | +
|
| 87 | +/// Step 1 |
| 88 | +/// Create a command interface. |
| 89 | +abstract interface class Order { |
| 90 | + void execute(); |
| 91 | +} |
| 92 | +``` |
| 93 | + |
| 94 | +```dart |
| 95 | +/// Step 2 |
| 96 | +/// Create a request class. |
| 97 | +class Stock { |
| 98 | + String _name = "ABC"; |
| 99 | + int _quantity = 10; |
| 100 | +
|
| 101 | + void buy() => log("Stock [ Name: $_name, Quantity: $_quantity bought"); |
| 102 | +
|
| 103 | + void sell() => log("Stock [ Name: $_name, Quantity: $_quantity sold"); |
| 104 | +} |
| 105 | +``` |
| 106 | + |
| 107 | +```dart |
| 108 | +/// Step 3 |
| 109 | +/// Create concrete classes implementing the Order interface. |
| 110 | +class BuyStock implements Order { |
| 111 | + Stock _stock; |
| 112 | +
|
| 113 | + BuyStock(this._stock); |
| 114 | +
|
| 115 | + void execute() => _stock.buy(); |
| 116 | +} |
| 117 | +
|
| 118 | +class SellStock implements Order { |
| 119 | + Stock _stock; |
| 120 | +
|
| 121 | + SellStock(this._stock); |
| 122 | +
|
| 123 | + void execute() => _stock.sell(); |
| 124 | +} |
| 125 | +``` |
| 126 | + |
| 127 | +```dart |
| 128 | +/// Step 4 |
| 129 | +/// Create command invoker (Sender) class. |
| 130 | +class Broker { |
| 131 | + List<Order> _orderList = []; |
| 132 | +
|
| 133 | + void takeOrder(Order order) => _orderList.add(order); |
| 134 | +
|
| 135 | + void placeOrders() { |
| 136 | + for (Order order in _orderList) { |
| 137 | + order.execute(); |
| 138 | + } |
| 139 | + _orderList.clear(); |
| 140 | + } |
| 141 | +} |
| 142 | +``` |
| 143 | + |
| 144 | +```dart |
| 145 | +/// Step 5 |
| 146 | +/// Use the Broker class to take and execute commands. |
| 147 | +void main() { |
| 148 | + Stock abcStock = Stock(); |
| 149 | +
|
| 150 | + /// Commands |
| 151 | + BuyStock buyStockOrder = BuyStock(abcStock); |
| 152 | + SellStock sellStockOrder = SellStock(abcStock); |
| 153 | +
|
| 154 | + /// Invoker -> Sender |
| 155 | + Broker broker = Broker(); |
| 156 | + broker.takeOrder(buyStockOrder); |
| 157 | + broker.takeOrder(sellStockOrder); |
| 158 | +
|
| 159 | + broker.placeOrders(); |
| 160 | +} |
| 161 | +
|
| 162 | +/// Step 6: Verify the output. |
| 163 | +/// Stock [ Name: ABC, Quantity: 10 ] bought |
| 164 | +/// Stock [ Name: ABC, Quantity: 10 ] sold |
| 165 | +
|
| 166 | +``` |
| 167 | + |
| 168 | +--- |
| 169 | + |
| 170 | +- <a href="command_terminal_example_with_history.dart.dart/"> command_terminal_example_with_history.dart </a> |
| 171 | + |
| 172 | +- <a href="command_remote_control_example.dart/"> command_remote_control_example </a> |
| 173 | +- <a href="command_remote_control_easy_example.dart/"> command_remote_control_easy_example </a> |
| 174 | + |
| 175 | +<h2 id="Summery"> Summery</h2> |
| 176 | + |
| 177 | +--- |
| 178 | + |
| 179 | +<h2 id="How_To_Implement"> How To Implement </h2> |
| 180 | + |
| 181 | +1. Declare the command interface with a single execution method. |
| 182 | + |
| 183 | +2. Start extracting requests into concrete command classes that implement the command interface. |
| 184 | + |
| 185 | + - Each class must have a set of fields for storing the request arguments along with a reference to the actual receiver object. |
| 186 | + - All these values must be initialized via the command’s constructor. |
| 187 | + |
| 188 | +3. Identify classes that will act as senders. |
| 189 | + |
| 190 | + - Add the fields for storing commands into these classes. |
| 191 | + - Senders should communicate with their commands only via the command interface. |
| 192 | + - Senders usually don’t create command objects on their own, but rather get them from the client code. |
| 193 | + |
| 194 | +4. Change the senders so they execute the command instead of sending a request to the receiver directly. |
| 195 | + |
| 196 | +5. The client should initialize objects in the following order: |
| 197 | + |
| 198 | + 1. Create receivers. |
| 199 | + 2. Create commands, and associate them with receivers if needed. |
| 200 | + 3. Create senders, and associate them with specific commands |
| 201 | + |
| 202 | +--- |
| 203 | + |
| 204 | +<h3 id="Advantages:"> Advantages:</h3> |
| 205 | + |
| 206 | +<h3 id=" Disadvantages:"> Disadvantages:</h3> |
| 207 | + |
| 208 | +<h3 id=" Real-Life-Uses">Real Life Uses:</h3> |
| 209 | + |
| 210 | +## Sources |
| 211 | + |
| 212 | +1. https://www.geeksforgeeks.org/command-pattern/ |
| 213 | +2. https://www.tutorialspoint.com/design_pattern/command_pattern.htm |
| 214 | +3. https://github.com/scottt2/design-patterns-in-dart/tree/master/command |
| 215 | +4. https://refactoring.guru/design-patterns/command |
| 216 | + |
| 217 | +<img src = "assets/structure_command.png"> |
| 218 | + |
| 219 | +<p align="right">(<a href="#top">back to top</a>)</p> |
0 commit comments