Here is a simple application that demonstrates using of decorator pattern in C#.
Sample solution can be downloaded from here https://github.com/mchudinov/LoggingDecorator. Solution is compatible with Visual Studio 2012, MonoDevelop 5, and Xamarin Studio 5.
Application is based on the answer found at Stackoverflow and this related article “Meanwhile… on the command side of my architecture“. Decorated functionality in this application is logging. This is probably not the best practice to use decorator for cross-cutting functionality such as logging but it is just for example purpose. Actually I prefer to use AOP for cross-cutting functionality such as logging in production and here is my another post about it “Logging in .NET using AOP with PostSharp“.
Here is a trivial method that should be decorated with logging
public class EchoCommand { public virtual void Echo(string str) { Console.WriteLine(str); throw new Exception("except me!"); } }
Including the logging (or any other additional functionality) into Echo method is a violation of the SOLID principles:
- The Single Responsibility Principle. If a class does logging together with main functionality then it has multiple responsibilities. We should extract the logging into its own class.
- The Open-Closed principle. The behaviour of the system should be able to be altered without changing any existing line of code. For instance when we need logging and also need exception handling (a third responsibility) we must alter the
Echo
method, which is a violation of the Open-Closed principle.
1. Simple decorator
We extract logging into its own class – the decorator – and allow that class to wrap the original EchoCommand class. Note that method that will be decorated must be virtual public virtual void Echo(string str)
because it will be overridden in decorator.
public class EchoCommandLogDecorator : EchoCommand { private readonly EchoCommand decorated; static readonly ILog log = LogManager.GetLogger(typeof(Program)); public EchoCommandLogDecorator(EchoCommand decorated) { this.decorated = decorated; } public override void Echo(string str) { log.Info("Entering method"); try { this.decorated.Echo(str); } catch (Exception e) { log.ErrorFormat("Exception {0}", e.Message); } } }
By wrapping the decorator around the real instance, we can now add this logging behaviour to the class, without any other part of the system to change:
EchoCommand command = new EchoCommandLogDecorator(new EchoCommand()); command.Echo("Echo Me");
When writing the code as shown above, we will have to define decorators for every operation that needs logging in a program. This leads to a lot of duplicate decorator code. This is a violation of the DRY principle. What’s missing here is a common abstraction over all use cases that need to be decorated.
2. General decorator
What’s missing in the previous section is a common interface what every class that implements it executes a command which can be decorated. Let it be called command handler ICommandHandler<TCommand>
interface. Let’s define it:
interface ICommandHandler<TCommand> { void Execute(TCommand command); }
Now we need to pass parameters to command through command handler. Let’s store the method arguments of the Echo(string str)
in a separate class. Thus we split data and behaviour.
class EchoCommandData { public string Str { get; set; } }
And let’s put the behaviour of the Echo()
method in a class that itself implements ICommandHandler<EchoCommand>
:
class EchoCommandHandler : ICommandHandler<EchoCommandData> { public void Execute(EchoCommandData data) { Echo(data.Str); } void Echo(string str) { Console.WriteLine(str); throw new Exception("except me!"); } }
First of all this gives us possibility to instance EchoCommandHandler once (with a factory or mock it in a unit test) and then feed it with data we need. Then since we have a common interface for all commands we can stack them together and execute in a kind of common manner.
Let’s write now a general abstraction for log decoration that will also be a command handler ICommandHandler
:
class LogDecorator<TCommand> : ICommandHandler<TCommand> { static readonly ILog log = LogManager.GetLogger(typeof(Program)); ICommandHandler<TCommand> decorated; public LogDecorator(ICommandHandler<TCommand> decorated) { this.decorated = decorated; } public void Execute(TCommand command) { log.Info("Entering method"); try { this.decorated.Execute(command); } catch (Exception e) { log.ErrorFormat("Exception {0}", e.Message); } } }
This LogDecorator class class can be reused for all command handlers in the system. But every method that will use this decoration must be wrapped into a command handler. Use of it:
ICommandHandler<EchoCommandData> handler = new LogDecorator<EchoCommandData>(new EchoCommandHandler()); handler.Execute(new EchoCommandData { Str = "Echo Me #3" });
3. More decorators
If the are more then one decorator in the program then the common part of functionality of them can be extracted into a parent class:
abstract class Decorator<TCommand> { protected ICommandHandler<TCommand> decorated; public Decorator(ICommandHandler<TCommand> decorated) { this.decorated = decorated; } }
Let’s add a new decorator that will beep a sound after a method call:
class BeepDecorator<TCommand> : Decorator<TCommand>, ICommandHandler<TCommand> { public BeepDecorator(ICommandHandler<TCommand> decorated) : base(decorated){} public void Execute(TCommand command) { this.decorated.Execute(command); Console.Beep(); } }
Let’s apply both BeepDecorator and LogDecorator to our EchoCommand:
ICommandHandler<EchoCommandData> handler = new BeepDecorator<EchoCommandData>( new LogDecorator<EchoCommandData>( new EchoCommandHandler() ) ); handler.Execute(new EchoCommandData { Str = "Echo Me #3" });
4. Conclusion
Decoration pattern in C# needs quite a lot of code just to wrap the main functionality into a command handler. That can be quite much coding for the whole application to decorate cross-cutting functionality such as logging for every class needs it. So as I mentioned in the beginning I do not like to use decorators for cross-cutting functionality. AOP solves this problem much more elegant. However decoration can be quite useful for additional functionality for some classes, not the whole app.
Sample solution can be downloaded from here https://github.com/mchudinov/LoggingDecorator. Solution is compatible with Visual Studio 2012, MonoDevelop 5, and Xamarin Studio 5.