The Command Pattern encapsulates a request as an object, thereby letting you parameterize other objects with different requests, queue or log requests, and support undoable operations.
The formal definition of the pattern is quite general and it shows how the pattern is versatile and can be used in many scenarios but the real important thing however is the first sentence, the idea that you can define a command in order to encapsulate a request to do something and use that command as a normal object.
The Command Pattern allows you to encapsulate a method invocation.
The biggest benefit is separating the object making requests from the objects that receive and execute the requests.
In order to describe the pattern I will implement a very simple transactional installer.
Let's assume we have the following requirements:
- The installer allows you to execute an operation
- The installer is independent by the specific operation executed
- The installer is able to roll back an operation in case of errors
- The installer is able to execute multiple operations as a transaction that is if something goes wrong all previously executed operations will be rolled back
Let's start to define the command abstraction:
We can easily satisfy the first three requirements defining an installer that accept an instance of the command and calls its methods when requested.
As you can see, the Installer class is completely independent by the specific command that is provided in its constructor. In the same way, the command itself is also completely independent by the Installer.
You can define as many commands as you want:
You can easily create a program to execute some operations:
Super easy isn't it?
What about supporting transactions?
It is here that the real beauty of the pattern comes in. Because you treat a command as a normal object, you can aggregate them. Nobody stops you to define a kind of Meta Command that aggregate lots of commands together and in addition implement the support for transactions.
Consider that the TransactionInstallerCommand is still a command. This means that you can use the Installer class without any changes to run a set of commands in a transactional way.
Let's see an example of usage.
Is it not amazing?
In order to simulate an error I created a special command that throws an exception all the time is called.
This is the output of the program:
This example and the Command Pattern is a demonstration of many Object Oriented Principles
Encapsulate what varies
The implementation and the number of each operation is what varies and we encapsulated this creating the CommandInstaller abstraction.
Favor composition over inheritance
The Installer use a Command using composition. The same does the transaction command that compose many commands together in order to create an extremely flexible command.
Program to interfaces not implementations
Depend on abstractions. Don't depend on concrete classes
Strive for loosely coupled designs between objects that interact
InstallerCommand is the main interface introduced here that allows a very loosely coupled design with the Installer.
Classes should be open for extension but closed for modifications
It is very easy to create a new command and you don't need to change the TransactionInstallerCommand in order to use it. TransactionInstallerCommand satisfy the Open/Closed principle.
We didn't finish yet because I am sure you have a question.
In .NET we have delegates and lambda as a way to represent code as an object. At the end of the day, this is what the command pattern is about or not?
This is absolutely true!
.NET developers are now quite familiar with code like this:
If you think carefully the Where method is accepting a command as an input and it is independent by the specific command provided. In this case the command is a predicate that is a function that takes an argument and returns a boolean as the result.
In addition, the predicate requested is just a specific instance of the generic delegate called Func defined as follow:
How would you implement a method similar to Where without having delegates?
Using the Command Pattern of course!
Let's try to do this.
This is an example of usage:
What is wrong about this?
Nothing! However you can easily agree with me that the solution using lambda has a big advantage: you don't need to create a new class all the times you need a new command.
LINQ has been introduced in the language starting from the version 3.0 of C# and is the biggest implementation in the entire framework of the command pattern using lambdas.
LINQ together with some new features made the C# a better language enabling some of the most useful features available in so called functional languages.
Hi Andrea,
ReplyDeletein the Where extension method you collect data into a List but in this way you have break the enumerator's flow. Consider the use of "yield return" like this:
foreach(var element in source)
if (predicate.Predicate(element))
yield return element;
It was not the point of the discussion and I didn't pay much attention to it. You are right. Thanks for the catch
Delete