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

Dynamic Decorator Pattern

4.81/5 (13 votes)
30 Sep 2010CPOL3 min read 68.5K   911  
Extend functionality of an object without modifying its class or writing decoration code during design time

Introduction

This article describes a Dynamic Decorator pattern, which can be used to add extra functionality to an object dynamically. The Dynamic Decorator makes use of .NET remoting and reflection technologies. It gets rid of the design time work needed in the traditional Decorator pattern (one of GoF patterns). It is very flexible and has great advantages when new functionality needs to be added to object methods.

Background

While using the GoF Decorator pattern, we need to design decoration code at compile time. I found it difficulty to maintain the code while requirements evolve and new features are added. I thought that it would be nice to have an easier and maintenance free way to extend functionality of an object. It led me to have the idea of Dynamic Decorator.

Using the Code

At the core of Decorator pattern is that new functionality can be added to object methods without modifying the class from which the object is instantiated. Say, your software has a well tested class Employee, which implements an IEmployee interface as follows.

C#
public interface IEmployee
{
    System.Int32? EmployeeID { get; set; }
    System.String FirstName { get; set; }
    System.String LastName { get; set; }
    System.DateTime DateOfBirth { get; set; }
    System.String FullName();
    System.Single Salary();
}
C#
public class Employee : IEmployee
{
    #region Properties

    public System.Int32? EmployeeID { get; set; }
    public System.String FirstName { get; set; }
    public System.String LastName { get; set; }
    public System.DateTime DateOfBirth { get; set; }

    #endregion

    public Employee(
        System.Int32? employeeid
        , System.String firstname
        , System.String lastname
        , System.DateTime bDay
    )
    {
        this.EmployeeID = employeeid;
        this.FirstName = firstname;
        this.LastName = lastname;
        this.DateOfBirth = bDay;
    }

    public Employee() { }

    public System.String FullName()
    {
        System.String s = FirstName + " " + LastName;
        Console.WriteLine("Full Name: " + s);
        return s;
    }

    public System.Single Salary()
    {
        System.Single i = 10000.12f;
        Console.WriteLine("Salary: " + i);
        return i;
    }
}

After release of your software, some customers want security right check before calling the Salary method. And you think it would be useful to put some log information before and after method calls for debugging and support purposes.

There are several ways you can implement these new features and requirements. You can modify your Employee class directly to add these new features. Or you can use the GoF's Decorator pattern to create some decoration classes for objects of Employee. Or you can use the Dynamic Decorator. The source code of an implementation of Dynamic Decorator can be found in the zip file.

Here, I am going to demonstrate how the Dynamic Decorator can be used to achieve it at runtime without modifying Employee class or creating decoration classes. The following code is all you need to add security check and log to methods of an object of Employee.

C#
static void Main(string[] args)
{
    IEmployee em = new Employee(1, "John", "Smith", new DateTime(1990, 4, 1));
    IEmployee tpCheckRight = (IEmployee)ObjectProxyFactory.CreateProxy(
        em,
        new String[] { "Salary" },
        new Decoration(new DecorationDelegate(UserRightCheck), null), 
        null);

    IEmployee tpLogCheckRight = (IEmployee)ObjectProxyFactory.CreateProxy(
        tpCheckRight,
        new String[] { "Salary", "FullName" },
        null, 
        new Decoration(new DecorationDelegate(ExitLog), null));

    try
    {
        tpLogCheckRight.FullName();
        Console.WriteLine("");
        tpLogCheckRight.Salary();
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
    }
}

private static void UserRightCheck(object target, object[] parameters)
{
    Console.WriteLine("Do security check here");
}

private static void ExitLog(object target, object[] parameters)
{
    Console.WriteLine("Do exit log here");
}

In the above code, an instance of Employee, em, is created. Then, ObjectProxyFactory.CreateProxy is called with em object as the first parameter. The second parameter is a string array which includes names of the object methods to which you want additional functionality to be added, { "Salary" }, for this case. The third parameter is a Decoration instance. The logic implemented in its delegate will be executed prior to the method calls specified in the second parameter. For this case, the UserRightCheck method is passed to the delegate with no parameter, and it will be executed prior to Salary method call. The fourth parameter is the other Decoration instance. The logic implemented in its delegate will be executed after the method calls specified in the second parameter. For this case, there is no logic that needs to be executed after Salary method call.

The call of ObjectProxyFactory.CreateProxy returns a proxy of em object, tpCheckRight. From now on, you can use tpCheckRight just like the em object. For instance, if you call Salary method using tpCheckRight, the UserRightCheck will be executed, and then em's Salary method.

In the above code, we pass tpCheckRight in a second ObjectProxyFactory.CreateProxy call in order to add ExitLog logic after execution of Salary and FullName methods of the em object.

When execution of the above code, you will see the following output:

Full Name: John Smith
Do exit log here

Do security check here
Salary: 10000.12
Do exit log here

As you can see, the ExitLog logic is called after the actual method calls of the object while the UserRightCheck is called before the actual method call of the object.

Points of Interest

New features are added to object methods on the fly without changing the original class.

No groundwork at design time is needed comparing to the GoF's Decorator pattern.

Better design can be achieved by not mingling various features into a mighty class (separation of concerns).

What's Next

In the article, Add Aspects to Object Using Dynamic Decorator, I dive into more details on Dynamic Decorator's important features and discuss how to add aspects to object at runtime and enhance them using Dynamic Decorator.

License

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