【问题标题】:Decorator pattern for classes with many properties具有许多属性的类的装饰器模式
【发布时间】:2012-12-08 11:26:33
【问题描述】:

我有这个简单的课程:

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

然后我想为它创建一个装饰器,它会添加一堆它自己的属性。问题是提供对装饰属性的访问的最简洁和优雅的方式是什么?

到目前为止,我有两个选择:或者我为装饰器中的四个装饰属性中的每一个提供一个 get-set 对(这看起来很乏味和拗口,基本上这是我想要避免的)或者我从 @987654324 继承 DataBag @ 然后以某种方式设法使用 TryGetMember 方法(这是动态的,似乎不是在 C# 中做事的正确方法)获得修饰属性。

有什么建议吗?

【问题讨论】:

  • 通常,装饰器都继承自一个通用接口。在这种情况下,该接口是什么?如果它是动态的,那么装饰器可能不合适。
  • 你考虑过从DataBag获得装饰器inherit吗?唯一的问题是你不能从装饰器向下转换到基本类型。
  • @dbaseman 我一开始使用继承,但很快就遇到了需要许多属性和方法略有不同的类的情况。
  • @dtryon 到目前为止根本没有接口。我知道古典装饰器需要实现一个接口,但我相信这不是重点。
  • @Anton:如果您提供一个计划如何使用装饰器和 DataBag 的示例,这可能会有所帮助......子类化是否足够?您不能子类化有什么原因吗?装饰器是否需要适用于多种类型?您是否需要同时考虑多个装饰器?

标签: c# properties decorator


【解决方案1】:

在实现装饰器时,我通常会执行以下操作。首先 - 提取装饰对象的接口并使装饰对象实现该接口:

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

下一步 - 创建一个装饰器,它将所有调用委托给装饰对象(所有装饰器都将从这个装饰器继承):

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
}

最后 - 创建装饰器:

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
}

用法:

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

【讨论】:

    【解决方案2】:

    这是最简单的装饰方式:

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

    如果您想创建一个外观并隐藏一些 DataBag 属性而不是仅仅添加属性,那么您可以使 DataBag 的成员受到保护。

    您可以使用接口:

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

    【讨论】:

      【解决方案3】:

      更新:@JeremyDWill 正确地指出,您不能从其参数之一派生泛型类型...

      如果您将装饰器视为“添加新属性的子类”,您可以执行以下操作:

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

      然后您可以创建MyDecorator&lt;DataBag&gt;MyDecorator&lt;OtherClass&gt; 等的实例。现有属性是可访问的,因为MyDecorator&lt;&gt; 特定于泛型参数的类型,并且派生自该类。

      您可以创建一个包含装饰对象的包装器:

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

      优点是访问装饰属性很容易:myObj.MyDecoratorProperty1。缺点是您现在必须通过 DecoratedObject 成员才能访问基础对象:

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

      如果你不能从装饰子类中继承(比如说你需要同时支持多个装饰器),你将不得不做一个“附加属性”之类的事情......你的装饰器类必须保留一个原始对象和修饰属性的字典。使用一些扩展方法,您可以使这些属性“看起来”像被修饰类的本地成员,只要您知道预先修饰的类型(或愿意修饰 any 对象):

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

      您的代码可能如下所示:

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

      【讨论】:

      • 与简单继承MyDataBagDecorator : DataBag有什么区别?通常也为一个装饰对象创建多个装饰器,而不是为许多不同类型的对象创建一个装饰器。
      • @lazyberezovsky:装饰器通常不特定于单个类。如果是,它们只是一个子类,而不是装饰器。至于拥有多个装饰器,这完全取决于上下文,这就是为什么那一段开始“如果你不能从被装饰者继承......”。幸运的是,在您添加评论的同时,我正在编写我的“附加装饰器”解决方案!
      • 在这种情况下,装饰器对 T 的成员没有任何了解,因此无法访问它们。我认为这个解决方案是最好的,但应该强制 T 实现一个接口。
      • 当前编写的第一个代码块无法编译;您不能从泛型类型参数继承。例如,请参阅stackoverflow.com/questions/5890516/…
      • @JeremyDWill: 嗯...我可以发誓我过去做过这件事(而不仅仅是使用 C++ 模板)。我会看看我是否可以解决它。
      猜你喜欢
      • 1970-01-01
      • 2016-10-14
      • 2011-05-30
      • 2018-01-16
      • 1970-01-01
      • 2018-08-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多