Decorator Pattern in C# - 3 versions

Abstract

This article is a tutorial on the Decorator Pattern in C#. First, we show the Classic Decorator pattern, which is described in the literature and in GoF books. Then, we show 2 alternative versions that can be viewed as “modern-C#-enabled” versions of the pattern. The intended audience is Intermediate C# programmers and above.

Introduction

The decorator Pattern is one fascinating and very popular pattern. The pattern has the purpose of adding additional functionality to an existing object dynamically. Decorators provide an alternative to subclassing for extending functionality. While it is possible to add functionality to a whole class of objects by subclassing an object’s class, the Decorator pattern aims to add functionality to just a single object and leave other objects of that class unchanged.

It is sometimes commented that, by enhancing the existing type without modifying the original type, the pattern follows the Open-Close Principle.

What is very interesting is that the pattern achieves its goal by doing things the wrong way, against OO recommendations.

Composition vs. Inheritance


How to Choose?

A typical question in OOA/OOD courses is, for a given class/object A, when creating a new class/object B that is going to reuse class/object A, how and based on what criteria to choose between Composition and Inheritance? Recommendations are usually given: If you plan to reuse the Public interface of an existing class A, use Inheritance. If you plan to reuse just the functionality of an existing class A, go for Composition.

So, Decorator Pattern is doing exactly the opposite. It plans to reuse the Public Interface of an existing class but goes for Composition. That decision has some consequences regarding problems with extending the Public Interface, as we will see later.

Classic Decorator Pattern Version 1

Classic Decorator is a version of the pattern that was offered in the GoF book and is often mentioned in literature. Typically, you would have some interface IComponent abstracting some real class ComponentA. Our wish is to replace the object of class ComponentA with some object that will provide more/enhanced functionality, let’s call it DecoratorB, that implements the same interface IComponent. What we want it to replace

IComponent comp = new ComponentA();

With

IComponent comp = new DecoratorB(/*--some parameters--*/);

Where DecoratorB “improves” object ComponentA in a certain way.

In this pattern, DecoratorB goes for composition, it adds ComponentA as its component and reimplements interface IComponent from scratch, sometimes just passing methods calls to ComponentA methods.

So, let us assume we have this IComponent interface and ComponentA class implementations.

public interface IComponent  //(1)
{
    string StateI { get; set; }   //(2)
    string MethodI1(bool print = true);   //(3)
}

public class ComponentA : IComponent   //(4)
{
    public string StateI { get; set; }   //(5)
    public string StateA { get; set; }   //(6)

    public ComponentA()  //(7)
    { }

    public string MethodI1(bool print = true)   //(8)
    {
        string text = "ComponentA.MethodI1";
        if (print) Console.WriteLine(text);
        return text;
    }

    public string MethodA1()   //(9)
    {
        string text = "ComponentA.MethodA1";
        Console.WriteLine(text);
        return text;
    }
}

If you look at the IComponent interface, you see it exposes publicly one property (2) and one method (3).

Class ComponentA is our core component/functionality class. You can see that, of course, it implements the public interface of ICompnent (5), (8), but in addition, it has some additions to the class public interface at (6) and (9). When accessing class through interface IComponent, we are, of course, limited to properties/methods exposed by that interface, meaning that property (6) and method (9) will not be accessible.

Typical usage of the above classes would look like this.

Console.WriteLine("IComponent================================");
IComponent I = new ComponentA();  //(20)
I.StateI = "123";
//I.StateA = "123";   //not possible
string tmpI1 = I.MethodI1();  //(21)
//string tmpI2 = I.MethodA1();  //not possible  //(22)

Console.WriteLine("ComponentA================================");
ComponentA A = new ComponentA();  //(23)
A.StateI = "123";
A.StateA = "123";
string tmpA1 = A.MethodI1();
string tmpA2 = A.MethodA1();

Now, let us see what a sample decorator class would look like.

public class DecoratorB : IComponent   //(10)
{
    public string StateI { get; set; }   //(11)
    public string StateB { get; set; }   //(12)

    private IComponent component = null;    //(13)

    public DecoratorB(IComponent comp)   //(14)
    {
        component = comp;
    }

    public string MethodI1(bool print = true)  //(15)
    {
        string baseTmp = component.MethodI1(false);
        string text = baseTmp + " " + "DecoratorB.MethodI1";
        if (print) Console.WriteLine(text);
        return (text);
    }

    public string MethodB1()  //(16)
    {
        string text = "DecoratorB.MethodB1";
        Console.WriteLine(text);
        return text;
    }
}

The key thing to notice in the Decorator class is how the constructor (14) passed IComponent is assigned to a private attribute (13). That is the “composition” we were talking about, and from there, all the magic of the decorator comes.

Decorator (10) now has the ability to completely reuse the functionality of the contained object in (13) and expose its own interface, in which it is free to reuse or modify the behavior of the object (13).

In (15) method MethodI1, we see how DecoratorB provides its own implementation of the interface (10), and in that process, it reuses MethodI1 of the contained object (13).

In addition to the public interface, it is inherited in (10), DecoratorB can add its own properties (12) and methods (16) that add some new functionality to the class.

Typical usage of DecoratorB would look like this.


DecoratorB B = new DecoratorB(new ComponentA()); //(24)
B.StateI = "123";
//B.StateA = "123";      //not possible
B.StateB = "123";
string tmpB1 = B.MethodI1(); //(25)
//string tmpB2 = B.MethodA1();   //not possible //(26)
string tmpB3 = B.MethodB1();

Look at call (25). That is the real reason for the usage of and strength of the Decorator pattern. That is a call to MethodI1 of class DecoratorB, which is using and enhancing the call to MethodI1 of class ComponentA. Since MethodI1 is part of the interface IComponent, even if the resulting object (24) is accessed through reference to IComponent, the call will be available, and it will work. Consumers of reference to IComponent need not even be aware if he is using the original ComponentA or the decorated version. The whole point of why we are doing this Decorator pattern lies in the power of this call. Of course, this is tutorial-level code, in real life, there will be more methods MethodI1, MethodI2, MethodI3, etc. that will all have that power.

We can see that in (26) it is not possible to access the method from ComponentA that is not exposed through interface IComponent. That is a problem because we might have several Components, let’s say ComponentA, ComponentA2, ComponentA3, etc., and each might have its own specific setters to configure itself. The main reason why this is happening is that, again, we do not inherit from ComponentA; we contain an object of class ComponentA, and we access it only through the interface IComponent.

This is one serious deficiency of the Decorator pattern since ComponentA might need some configuring/setup before it is ready for reuse by DecoratorB. It is often bypassed in 2 ways: 1) ComponentA is configured before it is passed to DecoratorB in (24); 2) ComponentA constructor would have parameters to configure itself.

Let us create one more Decorator class.

public class DecoratorC : IComponent  //(40)
{
    public string StateI { get; set; }
    public string StateC { get; set; }  //(41)

    private IComponent component = null;

    public DecoratorC(IComponent comp)
    {
        component = comp;
    }

    public string MethodI1(bool print = true)
    {
        string baseTmp = component.MethodI1(false);
        string text = baseTmp + " " + "DecoratorC.MethodI1";
        if (print) Console.WriteLine(text);
        return text;
    }

    public string MethodC1()  //(42)
    {
        string text = "DecoratorC.MethodC1";
        Console.WriteLine(text);
        return text;
    }
}

As you can see, DecoratorC is almost the same as DecoratorB. That was done deliberately for the purpose of this article.

Notice that in (41) and (42), DecoratorC is defining some public properties/methods that are specific to itself.

Here is what the usage of DecoratorC looks like.

DecoratorC C = new DecoratorC(new DecoratorB(new ComponentA()));  //(27)
C.StateI = "123";
//C.StateA = "123";      //not possible
//C.StateB = "123";      //not possible
C.StateC = "123";
string tmpC1 = C.MethodI1();   //(28)
//string tmpC2 = C.MethodA1();   //not possible  //(29)
//string tmpC3 = C.MethodB1();   //not possible  //(30)
string tmpC4 = C.MethodC1();

You can see in (27) how multiple decorators can be applied to a component. Let us say immediately, that nothing from a code design point prevents us from applying decorators in a different order, instead of first DecoratorB then DecoratorC, we can apply them in a different order. However, the resulting functionality might differ, so the order is relevant.

Look at call (28). Again, that is the real reason for the usage of and strength of the Decorator pattern. That is a call to MethodI1 of class DecoratorC, which is using and enhancing a call to MethodI1 of class DecoratorB, and that one is using and enhancing a call to MethodI1 of class ComponentA. Again, if we assign (27) to reference to IComponent, the consumer of reference to IComponent does not need to be aware if he is using original components or decorated versions. The whole point of why we are doing this Decorator pattern lies in the power of this call. Again, in real life, there will be more methods, MethodI1, MethodI2, MethodI3, etc., that will all have that power.

You can see in (29) and (30) that access to some public methods of ComponentA and DecoratorB is not possible. As mentioned above, that is a serious deficiency of the Decorator pattern.

Here is the class diagram and code of the whole project Classic Decorator.

Classic Decorator

public interface IComponent  //(1)
{
    string StateI { get; set; }   //(2)
    string MethodI1(bool print = true);   //(3)
}

public class ComponentA : IComponent   //(4)
{
    public string StateI { get; set; }   //(5)
    public string StateA { get; set; }   //(6)

    public ComponentA()  //(7)
    { }

    public string MethodI1(bool print = true)   //(8)
    {
        string text = "ComponentA.MethodI1";
        if (print) Console.WriteLine(text);
        return text;
    }

    public string MethodA1()   //(9)
    {
        string text = "ComponentA.MethodA1";
        Console.WriteLine(text);
        return text;
    }
}

public class DecoratorB : IComponent   //(10)
{
    public string StateI { get; set; }   //(11)
    public string StateB { get; set; }   //(12)

    private IComponent component = null;    //(13)

    public DecoratorB(IComponent comp)   //(14)
    {
        component = comp;
    }

    public string MethodI1(bool print = true)  //(15)
    {
        string baseTmp = component.MethodI1(false);
        string text = baseTmp + " " + "DecoratorB.MethodI1";
        if (print) Console.WriteLine(text);
        return (text);
    }

    public string MethodB1()  //(16)
    {
        string text = "DecoratorB.MethodB1";
        Console.WriteLine(text);
        return text;
    }
}

public class DecoratorC : IComponent  //(40)
{
    public string StateI { get; set; }
    public string StateC { get; set; }  //(41)

    private IComponent component = null;

    public DecoratorC(IComponent comp)
    {
        component = comp;
    }

    public string MethodI1(bool print = true)
    {
        string baseTmp = component.MethodI1(false);
        string text = baseTmp + " " + "DecoratorC.MethodI1";
        if (print) Console.WriteLine(text);
        return text;
    }

    public string MethodC1()  //(42)
    {
        string text = "DecoratorC.MethodC1";
        Console.WriteLine(text);
        return text;
    }
}

class Client
{
    static void Main(string[] args)
    {
        Console.WriteLine("IComponent================================");
        IComponent I = new ComponentA();  //(20)
        I.StateI = "123";
        //I.StateA = "123";   //not possible
        string tmpI1 = I.MethodI1();  //(21)
        //string tmpI2 = I.MethodA1();  //not possible  //(22)

        Console.WriteLine("ComponentA================================");
        ComponentA A = new ComponentA();  //(23)
        A.StateI = "123";
        A.StateA = "123";
        string tmpA1 = A.MethodI1();
        string tmpA2 = A.MethodA1();

        Console.WriteLine("DecoratorB================================");
        DecoratorB B = new DecoratorB(new ComponentA()); //(24)
        B.StateI = "123";
        //B.StateA = "123";      //not possible
        B.StateB = "123";
        string tmpB1 = B.MethodI1(); //(25)
        //string tmpB2 = B.MethodA1();   //not possible //(26)
        string tmpB3 = B.MethodB1();

        Console.WriteLine("DecoratorC================================");
        DecoratorC C = new DecoratorC(new DecoratorB(new ComponentA()));  //(27)
        C.StateI = "123";
        //C.StateA = "123";      //not possible
        //C.StateB = "123";      //not possible
        C.StateC = "123";
        string tmpC1 = C.MethodI1();   //(28)
        //string tmpC2 = C.MethodA1();   //not possible  //(29)
        //string tmpC3 = C.MethodB1();   //not possible  //(30)
        string tmpC4 = C.MethodC1();

        Console.WriteLine("Collection================================");
        List<IComponent> list = new List<IComponent>();
        list.Add(new ComponentA());
        list.Add(new DecoratorB(new ComponentA()));
        list.Add(new DecoratorC(new DecoratorB(new ComponentA())));

        foreach (IComponent iComp in list)
        {
            iComp.StateI = "123";
            string tmpII1 = iComp.MethodI1();
        }

        Console.ReadLine();
    }
}

Here is the result of the sample execution.

Sample execution

Dynamically selecting a type of Component

What the Decorator pattern makes possible, is to dynamically at runtime select which type of component application will use. Let us look at this code.

ComponentA compA = new ComponentA();
compA.StateA = "123"; // some configuring

IComponent comp = null;
int selection = GetSelectionFromGui();

switch (selection)
{
    case 1:
        comp = new DecoratorB(compA);
        break;
    case 2:
        comp = new DecoratorC(compA);
        break;
    default:
        comp = compA;
        break;
}

string result = comp.MethodI1();

You can see that user input will, at runtime decide on component type.

Decorator Cycles

Nothing in the code is preventing us from applying the same decorator more than once. For example, look at the code.

IComponent cc = new DecoratorB(new DecoratorC(new DecoratorB(new ComponentA())));

In literature [1], there can be found articles discussing how to detect Decorator cycles, and possibly forbid them. We will not be discussing that here.

Problem with Fat Classic Decorator

We said that the Decorator pattern is about composability, that is reused class is not inherited but composed into the decorator class. One side-effect of that is that the decorator does not inherit the public interface of the reused class but needs to explicitly implement each and every method that needs to be reused/exposed publicly. Creating and maintaining all those methods requires some effort if the number is bigger.

Let us see how the class diagram looks in the case of a fat classic observer that has 5 public methods to expose.

Class diagram

It can be seen that the number of methods in solution grows significantly. Most of the methods might just be passing calls to contained objects, as can be seen in this example implementation of DecoratorB.

public class DecoratorB : IComponent
{
    public string StateI { get; set; }
    public string StateB { get; set; }

    private IComponent component = null;

    public DecoratorB(IComponent comp)
    {
        component = comp;
    }

    public string MethodI1(bool print = true)
    {
        return component.MethodI1();
    }

    public string MethodI2(bool print = true)
    {
        return component.MethodI2();
    }

    public string MethodI3(bool print = true)
    {
        return component.MethodI3();
    }

    public string MethodI4(bool print = true)
    {
        return component.MethodI4();
    }

    public string MethodI5(bool print = true)
    {
        return component.MethodI5();
    }

    public string MethodB1()
    {
        string text = "DecoratorB.MethodB1";
        Console.WriteLine(text);
        return text;
    }
}

More realistically, in practice, there will be 20+ methods per class that need to be reused/publicly exposed. There are some developer tools like ReSharper that can assist the developer in creating and maintaining automatically all those methods, but the responsibility still lies on the developer/implementer.

Generics Decorator Pattern Version 2

The Generics Decorator pattern version can be found in recent literature. The idea of the Decorator being based on composability stays the same, just the method of passing type information is different. From the point of the purpose of the pattern and main design ideas, many things are similar. I got the idea for this version of the Decorator pattern and core code from [2].

The main trick is to pass class/type information via generics parameter types. The creation of the contained component object is passed to the default constructor of the type parameter class. So, the analogous code sample to our previous code will now look like this.

IComponent C = new DecoratorC<DecoratorB<ComponentA>>();

Since many things regarding usage are similar to the previous version, we will go immediately to the class diagram and code.

public interface IComponent
{
    string StateI { get; set; }
    string MethodI1(bool print = true);
}

public class ComponentA : IComponent
{
    public string StateI { get; set; }
    public string StateA { get; set; }

    public ComponentA()
    { }

    public string MethodI1(bool print = true)
    {
        string text = "ComponentA.MethodI1";
        if (print) Console.WriteLine(text);
        return text;
    }

    public string MethodA1()
    {
        string text = "ComponentA.MethodA1";
        Console.WriteLine(text);
        return text;
    }
}

public class DecoratorB<T> : IComponent  //(50)
        where T : IComponent, new()
{
    public string StateI { get; set; }
    public string StateB { get; set; }

    private T component = new T();   //(51)

    public DecoratorB()
    {
    }

    public string MethodI1(bool print = true)
    {
        string baseTmp = component.MethodI1(false);
        string text = baseTmp + " " + "DecoratorB.MethodI1";
        if (print) Console.WriteLine(text);
        return (text);
    }

    public string MethodB1()
    {
        string text = "DecoratorB.MethodB1";
        Console.WriteLine(text);
        return text;
    }
}

public class DecoratorC<T> : IComponent
        where T : IComponent, new()
{
    public string StateI { get; set; }
    public string StateC { get; set; }

    private T component = new T();

    public DecoratorC()
    {
    }

    public string MethodI1(bool print = true)
    {
        string baseTmp = component.MethodI1(false);
        string text = baseTmp + " " + "DecoratorC.MethodI1";
        if (print) Console.WriteLine(text);
        return text;
    }

    public string MethodC1()
    {
        string text = "DecoratorC.MethodC1";
        Console.WriteLine(text);
        return text;
    }
}

public class Client
{
    static void Main(string[] args)
    {
        Console.WriteLine("ComponentA================================");
        ComponentA A = new ComponentA();
        A.StateI = "123";
        A.StateA = "123";
        string tmpA1 = A.MethodI1();
        string tmpA2 = A.MethodA1();

        Console.WriteLine("DecoratorB================================");
        DecoratorB<ComponentA> B = new DecoratorB<ComponentA>();
        B.StateI = "123";
        //B.StateA = "123";      //not possible
        B.StateB = "123";
        string tmpB1 = B.MethodI1();
        //string tmpB2 = B.MethodA1();   //not possible
        string tmpB3 = B.MethodB1();

        Console.WriteLine("DecoratorC================================");
        DecoratorC<DecoratorB<ComponentA>> C = new DecoratorC<DecoratorB<ComponentA>>();
        C.StateI = "123";
        //B.StateA = "123";      //not possible
        //C.StateB = "123";      //not possible
        C.StateC = "123";
        string tmpC1 = C.MethodI1();
        //string tmpC2 = C.MethodA1();   //not possible
        //string tmpC3 = C.MethodB1();   //not possible
        string tmpC4 = C.MethodC1();

        Console.WriteLine("Collection================================");
        List<IComponent> list = new List<IComponent>();
        list.Add(new ComponentA());
        list.Add(new DecoratorB<ComponentA>());
        list.Add(new DecoratorC<DecoratorB<ComponentA>>());

        foreach (IComponent comp in list)
        {
            comp.StateI = "123";
            comp.MethodI1();
        }

        Console.ReadLine();
    }
}

And here is the result of the execution.

Execution

The main new thing to be noticed is how the Decorator class is defined in (50) as a generics class with a type parameter representing a reused (decorated) class/object. Also notice in (51) how a component, that needs to have a default constructor, is created.

The rest of the code, particularly the Decorator's usage, is the same.

Note in the class diagram (and code) that we didn’t touch or change the IComponent and ComponentA classes, but just the DecoratorB and DecoratorC classes.

This version of the Decorator pattern does not offer much new. It is even limiting since it relies strictly on the default constructor for the decorated (reused) class. Academically, it is attractive to show a version of Decorator using Generics, but practically, there is no gain in new/better features.

Problem with Dynamically selecting a type of Component

Generics Decorator has the problem that class type is resolved statically in compile time. So, that makes it a problem to create code similar to what was previously shown for Classic Decorator, which would select the type of component in runtime. It would maybe be possible with a big switch statement, enumerating statically different possible combinations, but it is not as natural and easy as in Classic Decorator.

Dynamic Decorator Pattern Version 3

Dynamic Decorator pattern exploits C# technology of dynamic objects and Reflection to achieve more than Classic Decorator. This version is enabled specifically by C# technology and might not be possible in some other OO languages that do not support similar technology.

I do not know if anyone independently published a version like this one, but I got the idea for it when I was reading about Dynamic Proxy. It came to my mind that similar technology can be used to overcome the limitations of the Classic Decorator pattern.

The key idea is to intercept calls to the unknown method and redirect calls to the contained object for processing. The contained object will then if it can not resolve the method call, recursively pass the method call for a resolution to its contained object, etc. We want to emphasize that the main trick here is the passing method calls down the contentment hierarchy, not the inheritance hierarchy.

The code shown here is demo-the-concept level quality, and for usage in production, it might need some refinement.

Let us show the class diagram and code first, then we will discuss

Dynamic object

public interface IComponent
{
    string StateI { get; set; }
    string MethodI1(bool print = true);
}

public class ComponentA : IComponent
{
    public string StateI { get; set; }
    public string StateA { get; set; }

    public ComponentA()
    { }

    public string MethodI1(bool print = true)
    {
        string text = "ComponentA.MethodI1";
        if (print) Console.WriteLine(text);
        return text;
    }

    public string MethodA1()
    {
        string text = "ComponentA.MethodA1";
        Console.WriteLine(text);
        return text;
    }
}

public class DecoratorB : DynamicObject, IComponent  //(60)
{
    public string StateI { get; set; }
    public string StateB { get; set; }

    private dynamic component;

    public DecoratorB(IComponent comp)
    {
        component = comp;
    }

    public override bool TryInvokeMember(  //(61) 
       InvokeMemberBinder binder, object[] args, out object result)
    {
        try
        {
            result = null;
            MethodInfo mInfo =
                component.GetType().GetMethod(binder.Name);  //(62)

            if (mInfo != null)
            {
                result = mInfo.Invoke(component, args);  //(63)
            }
            else
            {
                if (component is DynamicObject)
                {
                    component.TryInvokeMember(  //(64)
                        binder, args, out result);
                }
            }
            return true;
        }
        catch
        {
            result = null;
            return false;
        }
    }

    public override bool TrySetMember(   //(65)
                SetMemberBinder binder, object value)
    {
        try
        {
            PropertyInfo prop = component.GetType().GetProperty(
                binder.Name, BindingFlags.Public | BindingFlags.Instance);

            if (prop != null && prop.CanWrite)
            {
                prop.SetValue(component, value, null);
            }
            else
            {
                if (component is DynamicObject)
                {
                    component.TrySetMember(binder, value);
                }
            }
            return true;
        }
        catch
        {
            return false;
        }
    }

    public override bool TryGetMember(  //(66)
                GetMemberBinder binder, out object result)
    {
        try
        {
            result = null;
            PropertyInfo prop = component.GetType().GetProperty(
                binder.Name, BindingFlags.Public | BindingFlags.Instance);

            if (prop != null && prop.CanWrite)
            {
                result = prop.GetValue(component, null);
            }
            else
            {
                if (component is DynamicObject)
                {
                    component.TryGetMember(binder, out result);
                }
            }
            return true;
        }
        catch
        {
            result = null;
            return false;
        }
    }


    public string MethodI1(bool print = true)
    {
        string baseTmp = component.MethodI1(false);
        string text = baseTmp + " " + "DecoratorB.MethodI1";
        if (print) Console.WriteLine(text);
        return (text);
    }

    public string MethodB1()
    {
        string text = "DecoratorB.MethodB1";
        Console.WriteLine(text);
        return text;
    }
}

public class DecoratorC : DynamicObject, IComponent
{
    //similar to code DecoratorB
    //removed for brevity
}

class Client
{
    static void Main(string[] args)
    {
        Console.WriteLine("ComponentA================================");
        ComponentA A = new ComponentA();
        A.StateI = "III";
        Console.WriteLine(A.StateI);
        A.StateA = "AAA";      // not possible in Classic Decorator
        Console.WriteLine(A.StateA);
        string tmpA1 = A.MethodI1();
        string tmpA2 = A.MethodA1();

        Console.WriteLine("DecoratorB================================");
        dynamic B = new DecoratorB(new ComponentA());
        B.StateI = "III";
        Console.WriteLine(B.StateI);
        B.StateA = "AAA";      // not possible in Classic Decorator
        Console.WriteLine(B.StateA);
        B.StateB = "BBB";
        Console.WriteLine(B.StateB);
        B.StateXXX = "XXX";  // property does not exist, but no exception
        string tmpB1 = B.MethodI1();
        string tmpB2 = B.MethodA1();
        string tmpB3 = B.MethodB1();
        B.MethodXXX();  // method does not exist, but no exception


        Console.WriteLine("DecoratorC================================");
        dynamic C = new DecoratorC(new DecoratorB(new ComponentA())); //(70)
        C.StateI = "III";
        C.StateA = "AAA";  // not possible in Classic Decorator
        Console.WriteLine(C.StateA);
        C.StateB = "BBB";  // not possible in Classic Decorator
        Console.WriteLine(C.StateB);
        C.StateC = "CCC";
        Console.WriteLine(C.StateC);
        C.StateXXX = "XXX";  // property does not exist, but no exception
        string tmpC1 = C.MethodI1();
        string tmpC2 = C.MethodA1();   //(71)
        string tmpC3 = C.MethodB1();   //(72)
        string tmpC4 = C.MethodC1();
        C.MethodXXX();  // method does not exist, but no exception //(73)

        Console.WriteLine("Collection================================");
        List<IComponent> list = new List<IComponent>();
        list.Add(new ComponentA());
        list.Add(new DecoratorB(new ComponentA()));
        list.Add(new DecoratorC(new DecoratorB(new ComponentA())));

        foreach (IComponent iComp in list)
        {
            iComp.StateI = "III";
            Console.WriteLine(iComp.StateI);
            string tmpI1 = iComp.MethodI1();
        }

        Console.ReadLine();
    }
}

Here is the result of the sample execution.

Result

First, notice that the class diagram and code classes IComponent and ComponentA didn’t change. Only the code for decorators changed. That shows that this pattern can replace, if needed, the Classic Decorator pattern, without changes to the components code.

Let us look at DecoratorB class (60). DecoratorB now inherits from DynamicObject, which brings us the magic of creating methods like (61). In (67), we saved/contained our component as the dynamic object itself, so we can invoke any method on it, which we need in (64).

We will discuss method (61) only since in (65) and (66), similar logic applies. Method (61) is called when no appropriate method name is found in DecoratorB. In (62), with the help of a bit of Reflection magic, we want to see if our component has a method of such a name, and if it has we invoke it in (63). If not, then we see if our component is itself a DynamicObject, and if it is, we pass the call to its component (64).

This is a bit of a clever design, with recursively passing method calls to the contained component. I leave it to the reader to study it a bit.

The practical result is seen in (70). Calls that were previously not possible will now work. In particular, in (71) and (72) we make calls down the containment hierarchy, which was not possible with Classic Decorator. The interesting thing is (73) when a call to a method that does not exist is simply ignored.

The Dynamic Decorator pattern is a powerful alternative to the Classic Decorator design. But, of course, the usage of Dynamic objects brings performance costs to the application.

Conclusion

The Decorator Pattern is a very popular and important pattern. It is frequently used for file and IO streams. Just to mention, C#'s own IO Streams library is built around the Decorator Pattern.

First, we looked at the Classic Decorator pattern, which is described in the literature and in the GoF book. We discussed the problems it has, especially the need to manually implement all methods required to pass functionality through the containment hierarchy. Then we looked at the Generics Decorator pattern version, which is academically interesting but does not bring much new to the table.

In the end, we looked at the Dynamic Decorator pattern, which is the most potent version of the pattern. It is made possible by the C# feature DynamicObject and solves many problems that the Classic Decorator pattern has.


Similar Articles