After covering a few creational and structural design patterns, we have started behavioral patterns now. You can see the first article in this category of the strategy pattern. With creational patterns, we learned how we should create our objects, and from structural patterns we learned how we should structure our classes and objects to help build a better application.
In this article, we will be going through the command design pattern. As the name says, in this pattern we will be dealing with executing various commands. Let me take a word about this pattern from Wikipedia:
The command pattern is a behavioral design pattern in which an object is used to represent and encapsulate all the information needed to call a method at a later time. This information includes the method name, the object that owns the method and values for the method parameters.
Basically a pattern has numerous elements involved, which are as below. In the next section, we will be exploring each element with a code example. I will be taking the example of radio actions—very basic actions would be turning the radio on or off. So let's dive into each element.
Receiver
This element contains the actual implementation (knows how to carry out requested command) of any commands. This also maintains the history of executed commands, but it is not part of the actual command pattern. We will see that part in the Memento Design Pattern.
// Receiver class radioControl { public function turnOn() { // Turning On Radio echo "Turning On Radio"; } public function turnOff() { // Turning Off Radio echo "Turning Off Radio"; } }
Command
This element contains information about the necessary action to be taken. It calls the required method from receiver
.
// Command interface radioCommand { public function execute(); } class turnOnRadio implements radioCommand { private $radioControl; public function __construct(radioControl $radioControl) { $this->radioControl = $radioControl; } public function execute() { $this->radioControl->turnOn (); } } class turnOffRadio implements radioCommand { private $radioControl; public function __construct(radioControl $radioControl) { $this->radioControl = $radioControl; } public function execute() { $this->radioControl->turnOff (); } }
Client
This element really behaves like a client (deciding what to do). Its job is to determine which command to execute, without knowing who will execute it and how it will be executed. In this example I have taken command as a hard-coded value, but it can be grabbed from anywhere like a value from a URL and/or post variable as well.
// Client $in = 'turnOffRadio';
Invoker
This element initiates the whole process. It takes arguments from the client and invokes the process to call the required command.
// Invoker if (class_exists ( $in )) { $command = new $in ( new radioControl () ); } else { throw new Exception ( '..Command Not Found..' ); } $command->execute ();
Once we execute this command it will show "Turning Off Radio" as an output, because the client has invoked that command. Additional commands can be executed in the same way. If our action was set as turnOffRadio
, then it should show "Turning On Radio". Simple enough, isn't it?
Putting It All Together
Let's wrap up all the elements here to see how it works.
// Receiver class radioControl { public function turnOn() { // Turning On Radio echo "Turning On Radio"; } public function turnOff() { // Turning Off Radio echo "Turning Off Radio"; } } // Command interface radioCommand { public function execute(); } class turnOnRadio implements radioCommand { private $radioControl; public function __construct(radioControl $radioControl) { $this->radioControl = $radioControl; } public function execute() { $this->radioControl->turnOn (); } } class turnOffRadio implements radioCommand { private $radioControl; public function __construct(radioControl $radioControl) { $this->radioControl = $radioControl; } public function execute() { $this->radioControl->turnOff (); } } // Client $in = 'turnOffRadio'; // Invoker if (class_exists ( $in )) { $command = new $in ( new radioControl () ); } else { throw new Exception ( '..Command Not Found..' ); } $command->execute ();
Adding New Commands
Earlier we had only one frequency to play on the radio, so we had only two methods, which are to turn On or Off radio. But imagine that as time goes we have multiple frequencies to play, forcing us to add two new methods: tuneUp
and tuneDown
. Let's see how we can add those.
When we use design patterns, it enables us to make changes or extend functionality without making any changes in the client code. This applies here as well. We are going to add two more commands, but our client and invoker code will remain as they are.
Adding new commands requires changes in two places. The first is to create new commands which implement our radioCommand
interface, and next is to define the actual implementation of those commands in the receiver.
New Commands
class tuneUpRadio implements radioCommand { private $radioControl; public function __construct(radioControl $radioControl) { $this->radioControl = $radioControl; } public function execute() { $this->radioControl->tuneUp (); } } class tuneDownRadio implements radioCommand { private $radioControl; public function __construct(radioControl $radioControl) { $this->radioControl = $radioControl; } public function execute() { $this->radioControl->tuneDown (); } }
Updated Receiver Code
We will be adding two new methods in our receiver class.
// Receiver class radioControl { public function turnOn() { // Turning On Radio echo "Turning On Radio"; } public function turnOff() { // Turning Off Radio echo "Turning Off Radio"; } public function tuneUp() { // Tuning Up Radio echo "Tuning Up Radio"; } public function tuneDown() { // Tuning Down Radio echo "Tuning Down Radio"; } }
Conclusion
So we should go with the Command design pattern when we have multiple commands to execute, and it does not matter if those commands are related to each other or not. I have tried my best to elaborate on this design pattern, and you can post your feedback in the comment section.
Comments