Decorator pattern for classes with many properties

10,314

Solution 1

When implementing decorator I usually do following. First - extract interface of decorated object and make decorated object implement that interface:

public interface IDataBag
{
    string UserControl { get; set; }
    string LoadMethod { get; set; }
    dynamic Params { get; set; }
    int Height { get; set; }
}

Next - create a decorator, which delegates all calls to decorated object (all decorators will inherit from this decorator):

public class DataBagDecorator : IDataBag
{
    private IDataBag _dataBag;

    public DataBagDecorator(IDataBag dataBag)
    {
        _dataBag = dataBag;
    }

    public virtual string UserControl
    {
        get { return _dataBag.UserControl; }
        set { _dataBag.UserControl = value; }
    }

    // other members
}

Last - creating decorators:

public class FooDataBag : DataBagDecorator
{
    public FooDataBag(IDataBag dataBag) 
        : base(dataBag) { }

    public override string UserControl
    {
        // added behavior
        get { return "Foo" + base.UserControl; }
        set { base.UserControl = value; }
    }

    // you don't need to override other members
}

Usage:

IDataBag dataBag = new FooDataBag(new DataBag());

Solution 2

Update: @JeremyDWill rightly pointed out that you can't derive a generic type from one of its parameters...

If you think of the decorator as a "subclass that adds new properties", you could do something like:

public class MyDecorator<T> : T
{
    public int MyDecoratorProperty1 { get; set; }
    public int MyDecoratorProperty2 { get; set; }
}

Then you can create instances of MyDecorator<DataBag>, and MyDecorator<OtherClass>, etc. The existing properties are accessible because the MyDecorator<> is specific to the type of the generic argument, and derives from that class.

You can create a wrapper which contains the decorated object:

public class MyDecorator<T>
{
    public MyDecorator(T decoratedObject)
    {
        this.DecoratedObject = decoratedObject;
    }

    public T DecoratedObject { get; private set; }

    public int MyDecoratorProperty1 { get; set; }
    public int MyDecoratorProperty2 { get; set; }
}

The advantage is that getting to the decorated properties is easy: myObj.MyDecoratorProperty1. The downside is that you now have to go through the DecoratedObject member to get to the base object:

DataBag bag = new DataBag("", null, null);
MyDecorator<DataBag> deco = new MyDecorator<DataBag>(bag);
deco.DecoratedObject.Height = 2;

If you can't subclass from the decorate (you need to support multiple decorators at a time, say), you'll have to do something like an "attached property"... your decorator class would have to keep a dictionary of original objects and the decorated properties. With a few extension methods, you could make these properties "look" like native members of the decorated class as long as you know the types getting decorated in advance (or are willing to decorate any object):

public static class AttachedDecorator
{
    private class Properties
    {
        public int MyDecoratorProperty1 { get; set; }
        public int MyDecoratorProperty2 { get; set; }
    }

    private static Dictionary<object, Properties> map = new Dictionary<object, Properties>();

    public static int GetMyDecoratorProperty1(object obj)
    {
        Properties props;
        if (map.TryGetValue(obj, out props))
        {
            return props.MyDecoratorProperty1;
        }

        return -1; // or some value that makes sense if the object has no decorated property set
    }

    public static int GetMyDecoratorProperty2(object obj) { /* ... */ }

    public static void SetMyDecoratorProperty1(object obj, int value)
    {
        Properties props;
        if (!map.TryGetValue(obj, out props))
        {
            props = new Properties();
            map.Add(obj, props);
        }

        props.MyDecoratorProperty1 = value;

    }

    public static void SetMyDecoratorProperty2(object obj, int value) { /* ... */ }
}

public static class DecoratorExtensions
{
    private static int GetMyDecoratorProperty1(this object obj)
    {
        return AttachedDecorator.GetMyDecoratorProperty1(obj);
    }

    private static void SetMyDecoratorProperty1(this object obj, int value)
    {
        return AttachedDecorator.GetMyDecoratorProperty1(obj, value);
    }
    // ...
}

Your code then might look like:

DataBag myData = new DataBag();
myData.SetMyDecoratorProperty1(7);
Console.WriteLine("prop1: {0}", myData.GetMyDecoratorProperty1());

Solution 3

This is the simplest way to decorate:

public class PrettyBag : DataBag
{
    public int Decoration1 { get; set; }
    public int Decoration2 { get; set; }
}

If you want to create a facade and hide some of the DataBag properties instead of just adding properties then you can make the members of DataBag protected.

With interfaces you could do:

    public interface IDataBag
    {
       ...
    }
    public class DataBag : IDataBag
    {
       ...
    }
    public interface IPrettyBag : IDataBag
    {
        int Decoration1 { get; set; }
        int Decoration2 { get; set; }
    }
    public class BigBag : IPrettyBag
    {
        public int Decoration1 { get; set; }
        public int Decoration2 { get; set; }
    }
    public interface SmallBag : IPrettyBag
    {
        public int Decoration1 { get; set; }
        public int Decoration2 { get; set; }
    }
Share:
10,314
src091
Author by

src091

Updated on June 03, 2022

Comments

  • src091
    src091 about 2 years

    I have this simple class:

    public class DataBag
    {
        public string UserControl { get; set; }
        public string LoadMethod { get; set; }
        public dynamic Params { get; set; }
        public int Height { get; set; }
    
        public DataBag(string Control, 
            object vars, string lm)
        {
            UserControl = Control;
            LoadMethod = lm;
            Params = vars;
            Height = 0;
        }
    }
    

    I then would like to create a decorator for it that would add a bunch of it's own properties. Question is what's the most concise and elegant way to provide access to decorated properties?

    So far I have two options: either I provide a get-set pair for every of four decorated properties in decorator (which seems tedious and mouthful and basically it's what I want to avoid) or I inherit DataBag from DynamicObject and then somehow manage to get decorated properties using TryGetMember method (which is dynamic and does not seem to be the right way to do things in C#).

    Any advice?