Skip to content

Commit 9f6f43f

Browse files
committed
Create Command Design Pattern
1 parent 22a39f1 commit 9f6f43f

8 files changed

+713
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ Some patterns can be rendered unnecessary in languages that have built-in suppor
9191

9292
- <strong><a href="behavioral_design_patterns/chain_of_responsibility/" target="_blank">Chain Of Responsibility</a></strong>
9393
- <strong><a href="behavioral_design_patterns/observer/" target="_blank">Observer</a></strong>
94+
- <strong><a href="behavioral_design_patterns/command/" target="_blank">Command</a></strong>
9495

9596
---
9697

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
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>
Loading
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/// src: https://github.com/scottt2/design-patterns-in-dart/blob/master/command/command.dart
2+
3+
abstract class Receiver {
4+
Set<String> get actions;
5+
}
6+
7+
abstract class Command {
8+
late Receiver receiver;
9+
late String name;
10+
11+
Command(this.receiver);
12+
13+
@override
14+
String toString() => this.name;
15+
16+
void execute();
17+
}
18+
19+
class Invoker {
20+
List<String> history = [];
21+
void execute(Command cmd) {
22+
cmd.execute();
23+
history.add("[${DateTime.now()}] Executed $cmd");
24+
}
25+
26+
@override
27+
String toString() =>
28+
history.fold("", (events, event) => events + "$event\r\n");
29+
}
30+
31+
class TurnOffCommand extends Command {
32+
String name = "Turn off";
33+
TurnOffCommand(Light light) : super(light);
34+
void execute() {
35+
(receiver as Light).turnOff();
36+
}
37+
}
38+
39+
class TurnOnCommand extends Command {
40+
String name = "Turn on";
41+
TurnOnCommand(Light light) : super(light);
42+
void execute() {
43+
(receiver as Light).turnOn();
44+
}
45+
}
46+
47+
class Light implements Receiver {
48+
void turnOff() => print("Light off!");
49+
void turnOn() => print("Light on!");
50+
Set<String> get actions => Set.from(["off", "on"]);
51+
}
52+
53+
class LightSwitch {
54+
Invoker _switch = Invoker();
55+
Light light;
56+
57+
LightSwitch(this.light);
58+
59+
String get history => _switch.toString();
60+
61+
void perform(String action) {
62+
if (!light.actions.contains(action)) {
63+
return print("Uh...wait, wut?");
64+
}
65+
switch (action) {
66+
case "on":
67+
return _switch.execute(TurnOnCommand(light));
68+
case "off":
69+
return _switch.execute(TurnOffCommand(light));
70+
}
71+
}
72+
}
73+
74+
void main() {
75+
var myFavoriteLamp = Light();
76+
var iotLightSwitch = LightSwitch(myFavoriteLamp);
77+
78+
iotLightSwitch.perform("on");
79+
iotLightSwitch.perform("off");
80+
iotLightSwitch.perform("blink");
81+
iotLightSwitch.perform("on");
82+
83+
print("\r\n*** Fancy IoT Switch Logs ***\r\n${iotLightSwitch.history}");
84+
85+
/*
86+
Light on!
87+
Light off!
88+
Uh...wait, wut?
89+
Light on!
90+
91+
*** Fancy IoT Switch Logs ***
92+
[2019-06-20 08:00:38.880050] Executed Turn on
93+
[2019-06-20 08:00:38.883495] Executed Turn off
94+
[2019-06-20 08:00:38.883702] Executed Turn on
95+
*/
96+
}

0 commit comments

Comments
 (0)