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

本文关键字:模式 有效 一个 | 更新日期: 2023-09-27 18:04:12

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

我想知道这个例子是装饰器模式的有效例子吗? 如果不是呢
?此处是否需要更正或更改,请建议。

我们有一个代表容器的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();
}

接口代码:

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;
  }
}
根据我的理解

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

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

虽然你的实现不能理想地反映结构或装饰器模式,但从我的角度来看,它解决了装饰器旨在解决的相同问题。您的解决方案对于未来的修改并不像"理想的"装饰器实现那样严格和安全。

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

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

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

更新:支持抽象的观点:

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

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

  • 两者都有构造函数,接收组件进行装饰。
  • 两家商店都参考了装饰IContainer
  • 两者都在 getFeatures(( 方法中检索组件的功能
  • 两者都检查组件的功能是否为空,如果不是 - 附加一些字符串(唯一的区别是字符串本身(

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

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

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

最后,我

提供了重构您的代码的方法(我不想在开始时这样做,让您自己完成此操作(:

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

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