C#:"装饰器设计模式:添加功能是什么意思?"

时间:2017-03-30 20:55:06

标签: c# function design-patterns

我刚刚使用C#研究装饰器设计模式。

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

我有点明白,模式的重点是动态地为对象添加功能。

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

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

然后我有点希望关键是a对象现在能够调用仅存在于Execute()类中的Ashbringer方法。从而为a对象添加功能。

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

也许有一两件事我不了解界面或者我误解了装饰设计模式的重点?

4 个答案:

答案 0 :(得分:2)

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

考虑.NET中的Stream类。 Stream是一个常见的抽象类,它只是定义了一个用于读取和写入流的基本接口,但是像FileStream这样的具体子类提供了读/写文件的实现。但是,如果要向FileStream类添加缓冲或压缩行为,则不希望继承FileStream,因为那时您的子类不能与NetworkStream或{{1一起使用除非你也为它们创建了子类。

这就是Decorator模式的用武之地。MemoryStreamBufferedStream类可以“装饰”CompressedStream的另一个实例。您可以从StreamBufferedStream读取/写入与读取/写入任何未修饰的流相同的内容。它通过覆盖CompressedStream类的方法来实现这一点,通过对流入或流出的字节做任何事情来添加其功能,然后将它们传递到包装对象,无论它是StreamFileStream,甚至是另一位装饰者。

但回到你的WoW示例,也许更好的例子是NetworkStream类或EnchantedWeapon类,它们将充当Weapon类的装饰器。然后你可以做类似的事情:

TransmogrifiedWeapon

当然,这显然会引发例外,因为每个人都知道Ashbringer不能被转变。 :)

答案 1 :(得分:1)

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

因此,您可以扩展TravelBagpack的执行功能。

答案 2 :(得分:1)

装饰器模式中的想法是扩展方法的功能。这是因为继承变得复杂并且功能需要在分离的类中。

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

现在,您的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 :(得分:1)

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

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

...你明白了。

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