13

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?

Jason Aller
  • 3,541
  • 28
  • 38
  • 38
src091
  • 2,807
  • 7
  • 44
  • 74
  • typically, decorators all inherit from a common interface. In this case, what would that interface be? If it is dynamic, then maybe decorator is not the right fit. – Davin Tryon Dec 20 '12 at 21:46
  • Have you considered having the decorator *inherit* from `DataBag`? The only problem is then you can't cast from the decorator down the the base type. – McGarnagle Dec 20 '12 at 21:50
  • @dbaseman I used inheritance at first but very soon got to a situation where a lot of classes with slightly different properties and methods were needed. – src091 Dec 20 '12 at 21:53
  • @dtryon So far no interface at all. I understand that classical decorator needs to implement an interface but I believe this is not a point here. – src091 Dec 20 '12 at 21:58
  • @Anton: It might help if you provide an example of how you plan to use the decorator and DataBag... Is subclassing sufficient? Is there a reason you *can't* subclass? Does the decorator need to apply to multiple types? Do you need to account for more than one decorator at a time? – JaredReisinger Dec 20 '12 at 23:26

3 Answers3

9

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());
Sergey Berezovskiy
  • 232,247
  • 41
  • 429
  • 459
1

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());
JaredReisinger
  • 6,955
  • 1
  • 22
  • 21
  • 1
    How does it differs from simple inheritance `MyDataBagDecorator : DataBag`? Also usually many decorators created for one decorated object, not one decorator for many objects of different types. – Sergey Berezovskiy Dec 20 '12 at 21:52
  • @lazyberezovsky: Decorators usually aren't specific to a single class. If the are, they're just a subclass, not a decorator. As to having multiple decorators, it all depends on context, which is why that one paragraph starts "If you can't subclass from the decoratee...". As luck would have it, I was writing up my "attached decorator" solution at the same time you added your comment! – JaredReisinger Dec 20 '12 at 22:00
  • In this case the decorator wouldn't have any knowledge about members of T, so it could not access them. I think this solution is best but T should be forced to implement an interface. – Nick Bray Dec 20 '12 at 22:10
  • 1
    Your first code block, as currently written, does not compile; you cannot inherit from a generic type parameter. See http://stackoverflow.com/questions/5890516/in-c-sharp-4-0-is-it-possible-to-derive-a-class-from-a-generic-type-parameter for example. – JeremyDWill Dec 20 '12 at 22:36
  • @JeremyDWill: huh... I could have sworn I'd done this in the past (and not just using C++ templates). I'll see if I can work around it. – JaredReisinger Dec 20 '12 at 23:11
1

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; }
    }
Nick Bray
  • 1,953
  • 12
  • 18