2012-12-20 12 views
9

Ich habe diese einfache Klasse:Dekorierermuster für Klassen mit vielen Eigenschaften

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

dann mag ich einen Dekorateur für sie schaffen, die ein Bündel von seiner eigenen Eigenschaften hinzufügen würden. Frage ist, was ist der prägnanteste und eleganteste Weg, um dekorierte Eigenschaften zugänglich zu machen?

Bisher habe ich zwei Möglichkeiten: Entweder ich bieten ein get-set Paar für jeden von vier eingerichteten Wohnungen in Dekorateur (Wich scheint tideous und Bissen und im Grunde ist es, was ich vermeiden wollen) oder ich erben DataBag von DynamicObject und dann irgendwie verwalten dekorierte Eigenschaften mit der Methode TryGetMember (die dynamisch ist und nicht der richtige Weg ist, Dinge in C# zu tun).

Irgendwelche Ratschläge?

+0

normalerweise erben Dekoratoren alle von einer gemeinsamen Schnittstelle. Was wäre in diesem Fall die Schnittstelle? Wenn es dynamisch ist, dann ist vielleicht Dekorateur nicht die richtige Lösung. –

+0

Haben Sie darüber nachgedacht, den Dekorator * von 'DataBag' erben zu lassen? Das einzige Problem besteht dann darin, dass Sie vom Dekorator nicht den Basistyp abwandeln können. – McGarnagle

+0

@dbaseman Ich habe zuerst die Vererbung benutzt, bin aber sehr schnell in eine Situation geraten, in der viele Klassen mit leicht unterschiedlichen Eigenschaften und Methoden benötigt wurden. – Anton

Antwort

7

Wenn Dekorateur Umsetzung Ich normalerweise folgende.Erste - extrahieren Schnittstelle von dekorierten Objekt und machen dekoriert Objekt diese Schnittstelle implementieren:

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

Next - ein Dekorateur schaffen, die Delegierten alle Anrufe eingerichtet Objekt (alle Dekorateure von diesem Dekorateur erben):

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 - Dekorateure erstellen:

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 
} 

Verbrauch:

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

Update: @JeremyDWill Recht darauf hingewiesen, dass Sie nicht einen generischen Typ von einem seiner Parameter ...

Wenn Sie denken, der Dekorateur als „Unterklasse, die neue Eigenschaften hinzufügt“ ableiten können, Sie etwas tun könnte wie:

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

Dann können Sie Instanzen von MyDecorator<DataBag> erstellen, und MyDecorator<OtherClass> usw. die vorhandenen Eigenschaften zugänglich sind, weil die MyDecorator<> auf die Art des generischen Argument spezifisch ist, und leitet sich von dieser Klasse.

Sie können einen Wrapper erstellen, die das eingerichtete Objekt enthält:

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

Der Vorteil ist, dass auf den geschmückten Eigenschaften immer einfach ist: myObj.MyDecoratorProperty1. Der Nachteil ist, dass man jetzt durch das DecoratedObject Mitglied gehen, um das Basis-Objekt zu erhalten:

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

Wenn Sie nicht aus der Unterklasse können deco (Sie müssen mehrere Dekorateure zu einer Zeit, unterstützen, sagen), Sie müssen etwas wie eine "angefügte Eigenschaft" tun ... Ihre Dekoratorklasse würde ein Wörterbuch der ursprünglichen Gegenstände und der verzierten Eigenschaften behalten müssen. Mit wenigen Erweiterungsmethoden, können Sie diese Eigenschaften „Look“ wie native Mitglieder der dekorierten Klasse machen, solange Sie die Typen wissen immer im Voraus eingerichtet (oder bereit sind, jedes Objekt zu dekorieren):

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); 
    } 
    // ... 
} 

Code könnte dann wie folgt aussehen:

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

Wie unterscheidet es sich von der einfachen Vererbung 'MyDataBagDecorator: DataBag'? In der Regel sind viele Dekorateure für ein dekoriertes Objekt erstellt worden, nicht ein Dekorator für viele Objekte unterschiedlichen Typs. –

+0

@lazyberezovsky: Dekoratoren sind normalerweise nicht spezifisch für eine einzelne Klasse. Wenn das so ist, sind sie nur eine Unterklasse, kein Dekorateur. Da es mehrere Dekoratoren gibt, hängt alles vom Kontext ab, weshalb der eine Absatz beginnt "Wenn Sie keine Unterklasse von der Dekoration ..." können. Wie es der Zufall wollte, schrieb ich meine "Attached Decorator" -Lösung zur gleichen Zeit, als Sie Ihren Kommentar hinzufügten! – JaredReisinger

+0

In diesem Fall hätte der Dekorator kein Wissen über Mitglieder von T, so dass er nicht darauf zugreifen konnte. Ich denke, dass diese Lösung am besten ist, aber T sollte gezwungen sein, eine Schnittstelle zu implementieren. –

1

Dies ist die einfachste Art und Weise zu dekorieren:

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

Wenn Sie möchten, cr Wenn Sie eine Fassade bevorzugen und einige der DataBag-Eigenschaften ausblenden, anstatt nur Eigenschaften hinzuzufügen, können Sie die Mitglieder von DataBag schützen.

Mit Schnittstellen könnten Sie tun:

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