c# - 这是一个有效的Decorator模式示例

标签 c#

这是装饰器模式的有效示例吗?

我想知道此示例是装饰器模式的有效示例吗?如果不是
需要在此处进行更正或更改,请提出建议。

我们有一个Container类代表容器,为此,我想在
    运行。在示例中使用了wheellid之类的功能。

客户代码:

private void button2_Click(object sender, EventArgs e)
{
  IContainer container = new MovableConatiner(new Container());
  string features = container.getFeatures();
  container = new LidConatiner(container);
  features = container.getFeatures();
}


API代码:

public interface IContainer
{
  string getFeatures();
}

public class Container : IContainer
{      
  public string getFeatures()
  {
    return "Container";
  }
}

// a decorator to add wheels
public class MovableConatiner : IContainer
{
  protected IContainer container;

  public MovableConatiner(IContainer container)
  {
    this.container = container;
  }

  public string getFeatures()
  {
    string features = container.getFeatures();

   features = features != string.Empty ? string.Format("{0} , four wheels", features) :       
   features;
   return features;
  }
}

// a decorator to add lid to contaner
public class LidConatiner : IContainer
{
  protected IContainer container;

  public LidConatiner(IContainer container)
  {
    this.container = container;
  }

  public string getFeatures()
  {
    string features = container.getFeatures();

    features = features != string.Empty ? string.Format("{0} , Lid", features) : 
    features;
    return features;
  }
}


根据我的理解,所以我想验证一下我的理解。

最佳答案

尽管您的实现不能理想地反映结构或Decorator模式,但从我的角度来看,它解决了Decorator要解决的相同问题。您的解决方案不如"ideal" Decorator implementation那样严格和安全地用于将来的修改。

您简化了实现,为您删除了“不必要的”抽象。但是,尽管您可能认为它们现在不是必需的,但是当您的应用程序扩展并拥有几十个组件和装饰器时,它们在将来会变得非常有用。谁是谁,很容易迷路。

在我提供的链接中,非常简单地描述了Decorator模式的主要参与者,并且有一个示例代码,与您的代码非常相似,但是很完整。我不会在此处复制粘贴它来向您展示正确的实现。

我只想强调一下,如果您不了解设计模式中某些抽象的需求,则最好保留它们,或者再次阅读如何使用它,而不是仅仅删除它们。

更新:赞成抽象:

所有通用逻辑必须在一个地方实现并重用。代码重复非常非常糟糕。我认为每个人都同意这是组织良好的代码的基本原则。

现在,让我们分析装饰器模式的实现,而无需提取装饰器的抽象基类。比较您的MovableContainerLidContainer类的实现-您看到类似的东西吗?实际上,我几乎看不出任何区别。好的,让我们找到常见的地方:


两者都有构造函数,接收要装饰的组件。
两者都存储对装饰的IContainer的引用
都在getFeatures()方法中检索组件的功能
都检查组件的功能是否为空,如果不是,则附加一些字符串(唯一的区别是字符串本身)


所有这些逻辑都应提取到基类中。您已经应该了解两个装饰器的基类是必需的。

让我们走得更远。让我们想象每个可能的容器的每个可能的装饰器。正如Decorator模式定义所隐含的那样,很明显,所有装饰器都有一些共同的逻辑:它们都存储对装饰对象的引用。是否足以提取基类(单个属性-将其复制粘贴到您拥有的两个装饰器中并不难)?绝对足够!

如果您喜欢真实的例子,就在这里。您实现了很少的装饰器,比方说100(2个仍然是enouth,100只是消除了这个问题)。而且您会意识到有些减速器不知道如何正确使用它们-他们只是将NULL传递给您的装饰器。他们的代码运行良好,然后将创建的装饰器传递到其他地方,或存储到DB等。然后,在某些神奇的地方,您的代码稍后在各个地方失败。很难每次都找到NULL的来源,应用程序的哪个部分创建了这种对象。您决定在构造函数中添加NULL检查,以禁止传递NULL,并使初始代码无法立即解决问题。哎呀,我们需要修复所有100个构造函数!并将您的更改与其他10个开发人员的更改合并,这些开发人员正在每个人的装饰器上工作。那不是一个好的观点。
如果此示例不能使您信服,并且您仍然准备复制粘贴代码100次,请想象您正在开发一个可重用的库,并且其他公司的其他开发人员也实现了从您的IContainer派生的装饰器。您无法修复其装饰器的构造函数,并确保它们不会在内部为您提供无效的对象containsint NULL。相反,如果您有Decorator的基类,则只需要对其进行修复-您和第三方的所有实现都将获得该功能。如果您认为自己没有实现可重用的库,请考虑将其他开发人员作为第3方在团队中工作-它总是很有用,并且没有什么不同-不需要他们更改代码,因为您需要一些修复。

最后,我提供了重构您的代码的方式(我不想在一开始就这样做,而是让您自己来做):

public interface IContainer
{
    string getFeatures();
}

public class Container : IContainer
{
    public string getFeatures()
    {
        return "Container";
    }
}

public abstract class ContainerDecorator : IContainer
{
    protected IContainer container;

    protected ContainerDecorator(IContainer container)
    {
        if (container == null)
        {
            throw new ArgumentNullException("container");
        }
        this.container = container;
    }

    public abstract string getFeatures();
}

public class StringFormatDecorator : ContainerDecorator
{
    private readonly string _format;

    public StringFormatDecorator(IContainer container, string format) : base(container)
    {
        _format = format;
    }

    public override string getFeatures()
    {
        string features = container.getFeatures();

        features = features != string.Empty ? string.Format(_format, features) : features;
        return features;
    }
}

// a decorator to add wheels
public class MovableConatiner : StringFormatDecorator
{
    public MovableConatiner(IContainer container) : base(container, "{0} , four wheels")
    {
    }
}

// a decorator to add lid to contaner
public class LidConatiner : StringFormatDecorator
{
    public LidConatiner(IContainer container) : base(container, "{0} , Lid")
    {
    }
}


这样的代码不仅可以提高核心重用性,而且还可以防止其他人由于容器和装饰器之间的边界丢失而以错误的方式使用装饰器。现在要声明无参数装饰器要困难得多,几乎无法使用它。您不能用一个没有意义的容器来“装饰”一个容器,但是在您的实现中,当一些新开发人员在不知道您的初衷的情况下创建自己的容器时,这是可能的。
现在做错了事情变得更加复杂。

关于c# - 这是一个有效的Decorator模式示例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23733403/

相关文章:

c# - Windows窗体C#如何制作图表正方形的网格线?

C# 如何使属性赋值在循环中独立

c# - 在 C# 中重启 Windows 服务

c# - 指定的转换无效。 C# 网页浏览器

c# - 在 C# 源代码中断开长字符串的最佳方法

c# - .net/C# 中的网络

c# - 使用 C# 通过更改源代码有效地从网页中提取数据?

c# - ASP.Net:回发后如何维护文本框状态

c# - 如何在代码中绑定(bind)GridViewColumn的DisplayMemberBinding

c# - XNA:调整正交窗口大小而不拉伸(stretch)