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
The Command Pattern is the best way to solve this problem.
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.
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:
This Func object is actually the declaration of a generic command.
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,in 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
Sharing the source code might help 🙂
You are right, it would be useful. Unfortunately, I wrote this article a while back and I don’t have the code
Hi Andrea, I hope this message find you well with everything. I know this post is a bit old but clean code is clean neat always. I wanted to know your opinion on how would you combine this pattern with event aggregator and how would you presist commands?
Imagine you have steps like 1. Create database 2. Populate it. 3 deploy files 4. Create services etc.
How would we raise events and or pickup from the last step that was successful? Any thoughts?
Struggled for years, though I have 10 yrs writing code in C#,I always felt I understood command pattern but never was like 100% and use to feel i am missing something to understand. this article explanation delegates.. cleared my mind after struggle for a long period , over years. The word thank you is not enough for you. Thank you so much !!