Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Decorator Pattern Variant

4.50/5 (2 votes)
26 Jul 2009CPOL2 min read 19.6K  
A variant of the Decorator Pattern with an excursion on Extension Methods.

Introduction

We have all heard about Design Patterns. They're known paths to resolve/improve common development scenarios. Patterns can be modified and extended to fit a wide range of needs.

Here is a variation of the Decorator Pattern using interfaces.

Background

The Decorator Pattern allows to extend the behaviour of a class that cannot be inherited or where the methods are not overridable. Holding a reference to a sealed class, the decorator class method, when invoked, will execute its code and call the referenced object. The result is an extension of the sealed class.

Using the code

Supposing we want to build a modular logging library, we can adapt the Decorator Pattern to our needs. One of the nice things is that each decorator class can be used alone or in a pile of other decorators.

Here is the shared interface between all the decorators:

C#
using System;

namespace George.Decorator.ConsoleApp
{
    public interface ILoggingProvider
    {
        string Message {get;set;}
        string Source {get;set;}
        void Log();        
    }
}

Now the decorators:

ConsoleDecorator:

C#
using System;

namespace George.Decorator.ConsoleApp
{
    public class ConsoleDecorator : ILoggingProvider
    {
        private ILoggingProvider _iLoggingProvider = null;

        private string _message, _source = null;
        //empty contructor for a traditional object
        public ConsoleDecorator(){}
        
        //init class members and decorated interface
        public ConsoleDecorator(ILoggingProvider iLoggingProvider)
        {
                this._iLoggingProvider = iLoggingProvider;
                Message = iLoggingProvider.Message;
                Source = iLoggingProvider.Source;
        }
        
        //both current and decorated inteface method are invoked
        public void Log()
        {
            if(_iLoggingProvider != null)
            {
                WriteToConsole(_iLoggingProvider.Source, 
                               _iLoggingProvider.Message);
                _iLoggingProvider.Log();
            }
            else
            {
                WriteToConsole(Source,Message);
            }
            
        }
        
        private void WriteToConsole(string source, string message)
        {
            Console.WriteLine("--------------------------------");
            Console.WriteLine(string.Format("Source: {0}",source));
            Console.WriteLine(string.Format("Message: {0}",message));    
            Console.WriteLine("--------------------------------");
            Console.Write(Environment.NewLine);    
        }
        
        public string Message {
            get {
                return _message;                
            }
            set {
                _message = value;
            }
        }
        
        public string Source {
            get {
                return _source;
            }
            set {
                _source = value;
            }
        }
    }
}

EventLogDecorator:

C#
using System;
using System.Diagnostics;

namespace George.Decorator.ConsoleApp
{
    public class EventLogDecorator: ILoggingProvider
    {        
        private ILoggingProvider _iLoggingProvider = null;
        
        private string _message, _source = null;
        
        public EventLogDecorator(){}
        
        public EventLogDecorator(ILoggingProvider iLoggingProvider)
        {
            this._iLoggingProvider = iLoggingProvider;
            Message = iLoggingProvider.Message;
            Source = iLoggingProvider.Source;
        }
        
        public void Log()
        {
            if(_iLoggingProvider!=null)
            {
                CheckIfSourceExist(_iLoggingProvider.Source);
                EventLog.WriteEntry(_iLoggingProvider.Source, 
                                    _iLoggingProvider.Message);
                _iLoggingProvider.Log();
                
            }
            else
            {
                CheckIfSourceExist(Source);
                EventLog.WriteEntry(Source, Message);
            }                    
        }
        
        private void CheckIfSourceExist(string source)
        {
            if(!EventLog.SourceExists(source))
                EventLog.CreateEventSource(source, "Application");
        }
        
        public string Message {
            get {
                return _message;                
            }
            set {
                _message = value;
            }
        }
        
        public string Source {
            get {
                return _source;
            }
            set {
                _source = value;
            }
        }
    }
}

WinformDecorator:

C#
using System;
using System.Windows.Forms;


namespace George.Decorator.ConsoleApp
{
    public class WinFormDecorator: ILoggingProvider
    {
        private ILoggingProvider _iLoggingProvider = null;
        
        private string _message, _source = null;
        
        public WinFormDecorator(){}
        
        public WinFormDecorator(ILoggingProvider iLoggingProvider)
        {
            this._iLoggingProvider = iLoggingProvider;
            Message = iLoggingProvider.Message;
            Source = iLoggingProvider.Source;
        }
        
        public string Message {
            get {
                return _message;
            }
            set {
                _message = value;
            }
        }
        
        public string Source {
            get {
                return _source;
            }
            set {
                _source = value;
            }
        }
        
        public void Log()
        {
            if(_iLoggingProvider != null)
            {
                ShowMessageBox(_iLoggingProvider.Message, 
                               _iLoggingProvider.Source);
                _iLoggingProvider.Log();
            }
            else
            {
                ShowMessageBox(Message,Source);
            }
        }
        
        private void ShowMessageBox(string message, string source)
        {
            MessageBox.Show(message,source);
        }
    }
}

We can now use the decorator objects in a standalone mode:

C#
class Program
{
    public static void Main(string[] args)
    {
        ConsoleDecorator consoleDecorator = new ConsoleDecorator();
        EventLogDecorator eventLogDecorator = new EventLogDecorator();
        WinFormDecorator windowsFormDecorator = new WinFormDecorator();
        
        //in case of an information it will be enough the console windows
        
        consoleDecorator.Message = "Hi, here's the console";
        consoleDecorator.Source = "Console";
        consoleDecorator.Log();
        
        //if we want to log the message also on the eventviewer
        eventLogDecorator.Message = "Hi, here's the event viewer";
        eventLogDecorator.Source = "Event viewer";
        eventLogDecorator.Log();
        
        //or if we want to log the message also on the messagebox
        windowsFormDecorator.Message = "Hi, here's the windows form";
        windowsFormDecorator.Source = "Message Box";
        windowsFormDecorator.Log();

Or we can compose a block of logging providers by using the Decorator Pattern:

C#
//decorator pattern variant
consoleDecorator = new ConsoleDecorator();
consoleDecorator.Message = "Hi, here's the console again";
consoleDecorator.Source = "Console";

eventLogDecorator = new EventLogDecorator(consoleDecorator);
windowsFormDecorator = new WinFormDecorator(eventLogDecorator);

//print out the output using all the providers
windowsFormDecorator.Log();

Let's now see why we can only get the benefits by programming using interfaces. In this case, we can use any object implementing the ILoggingProvider interface to build up the logging, and in other cases, like for instance the use of extensions methods:

C#
using System;
using System.Windows.Forms;
using System.Diagnostics;

namespace George.Decorator.ConsoleApp
{
    public static class ConsoleDecoratorExtensions
    {
        
        //Adding event viewer logging
        public static void LogWithEventViewer(this ILoggingProvider consoleDecorator)
        {
            if(!EventLog.SourceExists(consoleDecorator.Source))
                   EventLog.CreateEventSource(consoleDecorator.Source, "Application");
            EventLog.WriteEntry(consoleDecorator.Source,consoleDecorator.Message);
            consoleDecorator.Log();
        }
        
        //adding messagebox alert
        public static void LogWithMessageBox(this ConsoleDecorator consoleDecorator)
        {
             MessageBox.Show(consoleDecorator.Message,consoleDecorator.Source);
             consoleDecorator.Log();
        }
        
    }
}

The extra value of the interface is that now that we have the extension method available for all the classes implementing it, extension methods targeting classes will be available only to the class.

C#
//using extension methods
consoleDecorator.LogWithEventViewer();
consoleDecorator.LogWithMessageBox();

//interface extension method
windowsFormDecorator.LogWithEventViewer();

Points of interest

Design Patterns are a fascinating argument, they provide elegant solutions to solve daily problems. In this case, a variation of the Decorator Pattern helps to build up objects that can be independent (usually decorators are used only to extend objects) or related with a kind of inheritance relationship.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)