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

时间:2014-05-19 08:46:26

标签: 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;
  }
}

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

1 个答案:

答案 0 :(得分:3)

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

您简化了实现,为您的抽象删除了“不必要的”。但是虽然您可能认为它们是不必要的,但是当您的应用程序增长并且获得几十个组件和装饰器时,它们将在未来变得非常有用。目前是谁,很容易迷路。

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

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

更新:支持抽象:

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

现在让我们分析一下Decorator模式的实现,而不需要为装饰器提取抽象基类。比较MovableContainerLidContainer类的实现 - 你看到类似的东西吗?实际上我几乎没有看到任何差异。好的,让我们找到常见的事情:

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

所有这些逻辑都应该提取到基类。您已经非常需要为两个装饰器提供基类。

让我们走得更远。让我们想象每个可能的容器的每个可能的装饰器。从Decorator模式定义可以看出,很明显,所有装饰器都有一些逻辑:它们都存储对装饰对象的引用。这是否足以提取基类(单个属性 - 这不难复制 - 粘贴到你拥有的两个装饰器)?绝对够了!

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

最后,我提供了重构代码的方式(我一开始不想这样做,让你自己解决这个问题):

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")
    {
    }
}

此类代码不仅可以改善核心重用,还可以防止其他人以错误的方式使用装饰器,因为容器和装饰器之间的边界丢失了。现在声明无参数装饰器要困难得多,而且几乎不可能使用它。你不能用另一个没有意义的容器“装饰”一个容器,但是当你的实现中有些新的开发人员在不知道你的初始意图的情况下创建自己的容器时。 现在做错事变得复杂得多。