2012-12-20 19 views
9

mam tę prostą klasę:Dekorator wzorzec dla klas z wielu właściwości

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 wtedy chciałby stworzyć dekorator na to, że będzie dodać kilka To własnych właściwości. Pytanie: jaki jest najbardziej zwięzły i elegancki sposób na zapewnienie dostępu do dekorowanych właściwości?

tej pory mam dwie opcje: albo ja dostarczenie get-set parę dla każdego z czterech zdobionych właściwości w dekoratora (wich wydaje tideous i łyk i w zasadzie to, co chcę uniknąć) lub I dziedziczą DataBag z DynamicObject a potem jakoś uzyskać zdobione właściwości za pomocą metody TryGetMember (która jest dynamiczna i nie wydaje się być właściwą metodą robienia rzeczy w języku C#).

Każda rada?

+0

Zazwyczaj wszyscy dekoratorzy dziedziczą ze wspólnego interfejsu. W takim przypadku, jaki byłby ten interfejs? Jeśli jest dynamiczny, to może dekorator nie jest odpowiedni. –

+0

Czy uważasz, że dekorator * może odziedziczyć * z 'DataBag'? Jedyny problem polega na tym, że nie można odlewać od dekoratora w dół od typu podstawowego. – McGarnagle

+0

@dbaseman Z początku korzystałem z dziedziczenia, ale bardzo szybko doszło do sytuacji, w której potrzebnych było wiele klas o nieco innych właściwościach i metodach. – Anton

Odpowiedz

7

Przy wdrażaniu dekorator zwykle zrobić po.Po pierwsze - wyodrębnić interfejsu zdobione obiektu i dokonać urządzony obiekt wdrożenia tego interfejsu:

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

Następny - tworzenie dekorator, który przekazuje wszystkie połączenia do zdobione obiektu (wszystkie dekoratorzy będą dziedziczyć z tego dekoratora):

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 
} 

Ostatnio - tworzenie dekoratorów:

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 
} 

Zastosowanie:

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

Aktualizacja: @JeremyDWill słusznie wskazał, że nie można wywieść typu rodzajowego od jednego z jego parametrów ...

Jeśli myślisz o dekoratora jako „podklasy, który dodaje nowe właściwości”, ty mógłby zrobić coś takiego:

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

Następnie można utworzyć instancji MyDecorator<DataBag> i MyDecorator<OtherClass> itp istniejące właściwości są dostępne, ponieważ MyDecorator<> jest specyficzny dla typu rodzajowego argumentu, a wywodzi się z tej klasy.

Można utworzyć opakowanie, które zawiera urządzony obiekt:

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; } 
} 

Zaletą jest to, że dotarcie do zdobionych właściwości jest proste: myObj.MyDecoratorProperty1. Minusem jest to, że teraz trzeba przejść przez członka DecoratedObject dostać się do obiektu bazowego:

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

Jeśli nie można podklasy z Dekoracje (trzeba obsługiwać wiele dekoratorów w czasie, powiedzmy), będziesz musiał zrobić coś w rodzaju "przywiązanej własności" ... Twoja klasa dekoratorów będzie musiała przechowywać słownik oryginalnych przedmiotów i dekorowanych właściwości. Za pomocą kilku metod rozszerzenie, można zrobić te właściwości „wyglądają” jak rodzimych członków zdobione klasy tak długo, jak wiesz typy coraz urządzone z góry (lub są skłonni do dekoracji żadnego 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); 
    } 
    // ... 
} 

Twój kod to może wyglądać następująco:

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

Jak to się różni od prostego dziedziczenia 'MyDataBagDecorator: DataBag'? Zwykle jest też wiele dekoratorów stworzonych dla jednego zdobionego obiektu, a nie jednego dekoratora dla wielu obiektów różnych typów. –

+0

@lazyberezovsky: Dekoratorzy zwykle nie są specyficzni dla jednej klasy. Jeśli są, to tylko podklasa, a nie dekorator. Jeśli chodzi o posiadanie wielu dekoratorów, wszystko zależy od kontekstu, dlatego ten akapit zaczyna się "Jeśli nie możesz podklasy od dekoranta ...". Przy odrobinie szczęścia napisałem moje rozwiązanie "dołączonego dekoratora" w tym samym czasie, gdy dodałeś swój komentarz! – JaredReisinger

+0

W tym przypadku dekorator nie miałby żadnej wiedzy na temat członków T, więc nie mógł uzyskać do nich dostępu. Myślę, że to rozwiązanie jest najlepsze, ale T powinno być zmuszone do wdrożenia interfejsu. –

1

jest to najprostszy sposób, aby ozdobić:

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

Jeśli chcesz do Cr stwórz elewację i ukryj niektóre właściwości DataBag zamiast tylko dodawać właściwości, a następnie możesz zabezpieczać członków DataBag.

z interfejsami można zrobić:

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; } 
    } 
Powiązane problemy