The Command Pattern in .NET
Andrea Angella
Technical Lead @ Redgate Software | ?Former Six-Times MVP on C# | ?? Grow Software Engineering Leaders | ?? Trainer, Mentor and Coach @ productivecsharp.com | ?? Host of The Productive C# Podcast | ?? Books Reader
The post was originally written on the Productive C# blog in 2012.
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 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.
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:
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.
Azure Cloud & Microsoft Technical Architect
1 年Good article showing a common useful pattern, even without latest language improvements. The only thing that I would change is the example in this post: creating a list for this ienumerable extension method could not be a good suggestion ??
?? Architecte technique senior chez VISEO
1 年PredicateCommand<TSource, bool> instead of PredicateCommand<string, bool>. ??