Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

Decorator Pattern

The other day I created a screencast that showed off the Unity Interception Extension and RemotingPolicyInjector to show AOP development in .NET applications:



and then followed that up with a post using PostSharp 1.0 that showed the same functionality without using the Unity Interception Extension:



 


 


Decorator Pattern


Continuing with this idea of showing alternatives using the same example, I thought it might be interesting to show the Decorator Pattern which can be used when the Policy Injection Application Block and PostSharp may be overkill for your application.


In the two examples mentioned above I created a Stopwatch Attribute that timed the ILogger.Write Method used in our console application:


 



public interface ILogger


{


    void Write(string message);


}


 


public class ConsoleLogger : ILogger


{


    public void Write(string message)


    {


        Console.WriteLine();


        Console.WriteLine(“*** In Logger ***”);


        Console.WriteLine(string.Format


            (“message : {0}”, message));


    }


}


 


Creating these attributes is pretty simple for use in the Policy Injection Application Block and PostSharp. However, we can avoid the use of these 3rd party libraries by creating a StopwatchLoggerDecorator Class that will achieve the same results:


 



public class StopwatchLoggerDecorator : ILogger


{


    private readonly ILogger _inner;


 


    public StopwatchLoggerDecorator(ILogger inner)


    {


        _inner = inner;


    }


 


    public void Write(string message)


    {


        Stopwatch sw = new Stopwatch();


        sw.Start();


 


        _inner.Write(message);


 


        sw.Stop();


 


        Console.WriteLine();


        Console.WriteLine(string.Format(“Time Elapsed: {0} ms”, sw.ElapsedMilliseconds));


    }


}


 


The Decorator Class implements the same ILogger Interface and takes the “real” ILogger in its constructor, ConsoleLogger. Inevitably it passes the Write Call onto the real logger, but before doing so starts a Stopwatch. After the call to ConsoleLogger is finished ( _inner.Write ), it stops the Stopwatch and displays the elapsed time in the console.


In keeping with the examples mentioned above, one can register the ILogger in your IoC Container / Dependency Injection Tool of choice. Here is an example using Unity:


 



IUnityContainer container = new UnityContainer();


 


 


// Register Logger


ILogger consoleLogger = new ConsoleLogger();


 


ILogger logger = new StopwatchLoggerDecorator(consoleLogger);


 


container.RegisterInstance<ILogger>(logger,


    new ContainerControlledLifetimeManager());


 


 


// Use Logger


var registeredLogger = container.Resolve<ILogger>();


 


registeredLogger.Write(“Hello!”);


 


Console.ReadLine();


 


The results are as follows:


 


Decorator Pattern


 


Although a simple example, you can use this with all sorts or cross-cutting concerns or anytime you want to decorate existing classes without modifying them. Note how easy it is to remove the functionality without modifying any of the classes. You simply just change the configuration code to no longer use the decorator.


 



IUnityContainer container = new UnityContainer();


 


 


// Register Logger


container.RegisterType<ILogger, ConsoleLogger>(new ContainerControlledLifetimeManager());


 


 


// Use Logger


var registeredLogger = container.Resolve<ILogger>();


 


registeredLogger.Write(“Hello!”);


 


Console.ReadLine();


 


Although not an example of the Open-Closed Principle ( OCP ) per se, the Decorator Pattern supports the idea that we don’t want to change the ConsoleLogger to add new behavior and possibly introduce bugs, but instead decorate it with another class that can offer that additional behavior. This also touches upon the Single-Responsibility Principle ( SRP ) in that the Console Logger keeps to its single responsibility of logging to the Console as opposed to adding “profiling” responsibilities to it.


 


 


Decorator Pattern at Runtime


Often you use the Decorator Pattern when it is decided at runtime whether or not you want to add the additional functionality offered by a Decorator Class.


Let’s re-write the code above to encapsulate the registration of our Infrastructure Services and introduce a debug setting that decides at runtime whether or not we wish to decorate the ConsoleLogger with the StopwatchDecoratorLogger Class:


 



public class InfrastructureExtension : UnityContainerExtension


{


    private readonly bool _debug;


 


    public InfrastructureExtension(bool debug)


    {


        _debug = debug;


    }


 


    protected override void Initialize()


    {


        ILogger logger;


 


        if (_debug)


            logger = new StopwatchLoggerDecorator(new ConsoleLogger());


        else


            logger = new ConsoleLogger();


 


        Container.RegisterInstance<ILogger>(logger, new ContainerControlledLifetimeManager());


    }


}


 


and now we set-up the Unity Container as follows:


 



IUnityContainer container = new UnityContainer();


container.AddExtension(new InfrastructureExtension(false));


 


// Use Logger


var registeredLogger = container.Resolve<ILogger>();


 


registeredLogger.Write(“Hello!”);


 


Console.ReadLine();


 


We can easily get the value of debug from a configuration setting, but in this case we just pass it in directly in the code above:


 



container.AddExtension(new InfrastructureExtension(false));


 


This will allow us to decide at runtime whether or not to decorate the ConsoleLogger with the StopwatchLoggerDecorator


 


 


Conclusion


If you prefer not to use the 3rd party AOP style libraries, you can always get back to the basics and use the Decorator Pattern. Note that at some point the 3rd party libraries, like Policy Injection Application Block and PostSharp, may begin to offer you much more gains in developer productivity and application maintenance if you find yourself creating a lot of decorators.


 


Hope this helps,


David Hayden


 


Related Posts:



 

This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

4 Responses to Decorator Pattern

  1. David Hayden says:

    Gael,

    You are absolutely right. That wasy my intent – to show the manual way of achieving the same results as done by various other IoC Containers and specific AOP Tools. I think this helps convey the concepts when leveraging the automated ways of achieving the end results using the tools.

    Regards,

    Dave

  2. David Hayden says:

    Hi Victor,

    I could have shown that, but the use of Unity and Lifetime Management was not really the intent of the post. I was attempting to focus on the Decorator Pattern as a manual way of AOP compared to other alternatives like the Policy Injection Application Block and PostSharp.

    However, you make an excellent point. When using an IoC Container, I should just register and resolve the other classes using the IoC Container as opposed to creating that tight coupling by using New. Good catch!

    Dave

  3. David,

    What you show there is basically a hand-based version of what Spring.NET or Castle do automatically: generate a proxy implementing the same interface, but calling some instrumentation code before and after calling the intercepted implementation.

    Just my 2c :).

    -gael

  4. I like how you show different approaches to achieve same goal. But it would be more helpful to show a more real-world scenario for decorator with IoC tool. Our ConsoleLogger may have dependecies of it’s own, it may have different lifestyles. So, just newing it up and passing to decorator logger won’t work. This requires the container to support it, i.e. register both logger implementations and container.Resolve() should return decorator with it’s inner impl fully builded up with their deps.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>