【问题标题】:The Decorator Design Pattern: What is meant by adding functionality?装饰者设计模式:添加功能意味着什么?
【发布时间】:2017-08-24 23:38:11
【问题描述】:

我刚刚开始学习使用 C# 的装饰器设计模式。

我做了一个例子,除了一件事之外,它的功能与我认为的一样。

我有点理解该模式的重点是动态地向对象添加功能。

所以当我创建这样的对象时:

Inventory a = new Ashbringer(new TravelersBagpack());
a.Execute();

然后我有点预料到,a 对象现在可以调用仅存在于Ashbringer 类中的Execute() 方法。从而向a 对象添加功能。

尽管这我不能不将Execute() 方法添加到Inventory 接口,这最终意味着我必须为所有实现Inventory 接口或抽象装饰器的类实现Execute() 方法.

也许有一两件事我不了解接口,或者我误解了装饰器设计模式的要点?

【问题讨论】:

标签: c# design-patterns decorator


【解决方案1】:

装饰器模式更多地是关于更改您正在装饰(包装)的对象的现有方法的行为,而不是添加新方法。虽然装饰器当然可以有自己的方法,但其目的是装饰器可以被视为它正在包装的对象。

考虑 .NET 中的 Stream 类。 Stream 是一个常见的抽象类,它简单地定义了一个用于读取和写入流的基本接口,但是像 FileStream 这样的具体子类提供了读取/写入文件的实现。但是如果你想为FileStream 类添加缓冲或压缩行为,你不会想要子类FileStream,因为这样你的子类就不能与NetworkStreamMemoryStream 一起工作,除非你也为他们。

这就是装饰器模式的用武之地。BufferedStreamCompressedStream 类可以“装饰”Stream 的另一个实例。您可以从BufferedStreamCompressedStream 读/写,就像对任何未修饰的流进行读/写一样。它通过覆盖Stream 类的方法来实现这一点,通过对流入或流出流的字节执行任何操作来添加其功能,然后将它们传递给包装对象,无论是FileStreamNetworkStream、甚至是另一个装饰器。

但是回到你的 WoW 示例,也许更好的示例是 EnchantedWeapon 类或 TransmogrifiedWeapon 类,它们将充当武器类的装饰器。然后你可以这样做:

Weapon w = new Ashbringer();
w = new TransmogrifiedWeapon(w, "Exodar Bastard Sword");
w = new EnchantedWeapon(w, "Scourgebane");
w.Render();

当然,这显然会抛出异常,因为每个人都知道灰烬使者无法幻化。 :)

【讨论】:

  • 哈哈!我现在好多了!当您尝试用 WoW 术语解释时,一切都变得简单多了 xD 所以 Weapon 有一个名为 Hit() 的方法,例如,Ashbringer 实现了这个方法来做 10 dmg,然后我们用 EnchantedWeapon 装饰 Ashbringer,这会增加 5 dmg hit() 方法。所以重点是给对象的方法增加功能,而不是给对象增加更多的方法。
  • 对。一个很好的指标表明您正在为装饰器寻找一份工作,即问题可以通过子类化解决,但这样做会限制其有用性或需要许多子类。 (即你可以制作一个 EnchantedAshbringer,但是你需要为每一种可附魔武器配备一个。)
  • 当你相信光线时,装饰者很容易:)
【解决方案2】:

装饰器模式的理念是扩展方法的功能。它适用于继承变得复杂并且功能需要在单独的类中的情况。

一个很好的例子是计算价格。想象一下,您想为更好的包裹、更快的运输、保险等特殊服务增加费用。

现在您的 Product 类具有 CalculatePrice 方法。

public class Product
{
    public double CalculatePrice()
    {
        // 10 is the products price
        return 10.0D;
    }
}

接下来,您可以创建一个ProductDecorator,它不调用他的基类方法,而是调用它所提供的产品之一。

public abstract class ProductDecorator : Product
{
    private readonly Product _product;

    public ProductDecorator(Product product)
    {
        _product = product;
    }

    public override double CalculatePrice()
    {
        return _product.CalculatePrice();
    }
}

现在,当您需要上述额外服务时,必须更改此价格。所以你可以为它们创建装饰器类。

public class ShippingDecorator : ProductDecorator
{
    public ShippingDecorator(Product product)
        : base(product)
    { }

    public override double CalculatePrice()
    {
        // shipping coasts 5
        return base.CalculatePrice() + 5;
    }
}

public class InsuranceDecorator : ProductDecorator
{
...

如果您想为此使用继承,它不会有用,因为它会很复杂。 (对许多班级)

现在你可以像这样使用它了。

var product = new Product();
product = new ShippingDecorator(product);
product = new InsuranceDecorator(product);
...

通常您还有一个创建对象的工厂,或者您可以使用构建器模式。

【讨论】:

    【解决方案3】:

    它不应该向界面添加任何新东西,它试图装饰现有的功能。

    所以你会以某种方式扩展 TravelersBagpack 的执行功能。

    【讨论】:

      【解决方案4】:

      在流的上下文中,假设您想写入文件。它看起来像这样:

      using(var fileStream = new FileStream(...))
      { ... }
      

      现在假设您希望对该文件进行加密。您可以使用装饰器模式对第二个流进行此操作:

      using(var fileStream = new FileStream(...))
      using(var cryptoStream = new CryptoStream(fileStream))
      { ... }
      

      现在假设您希望压缩该加密文件。装饰器又来救援了!

      using(var fileStream = new FileStream(...))
      using(var cryptoStream = new CryptoStream(fileStream))
      using(var zipStream = new ZipArchive(cryptoStream))
      { ... }
      

      ...你明白了。

      每个流都在“写入”流的过程中添加功能。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-06-01
        • 2021-11-24
        • 2017-12-18
        • 1970-01-01
        • 1970-01-01
        • 2023-03-10
        • 1970-01-01
        相关资源
        最近更新 更多