我刚刚使用C#研究装饰器设计模式。
我已经做了一个例子,除了一件事之外我认为它的功能。
我有点明白,模式的重点是动态地为对象添加功能。
所以当我创建这样的对象时:
Inventory a = new Ashbringer(new TravelersBagpack());
a.Execute();
然后我有点希望关键是a
对象现在能够调用仅存在于Execute()
类中的Ashbringer
方法。从而为a
对象添加功能。
虽然我不能将Execute()
方法添加到Inventory
接口,这最终意味着我必须对所有实现的类实现Execute()
方法Inventory
接口或抽象装饰器。
也许有一两件事我不了解界面或者我误解了装饰设计模式的重点?
答案 0 :(得分:2)
Decorator模式更多的是改变您正在装饰(包装)的对象的现有方法的行为,而不是添加新方法。虽然装饰器当然可以拥有自己的方法,但目的是装饰器可以被视为它正在包裹的对象。
考虑.NET中的Stream
类。 Stream
是一个常见的抽象类,它只是定义了一个用于读取和写入流的基本接口,但是像FileStream
这样的具体子类提供了读/写文件的实现。但是,如果要向FileStream
类添加缓冲或压缩行为,则不希望继承FileStream
,因为那时您的子类不能与NetworkStream
或{{1一起使用除非你也为它们创建了子类。
这就是Decorator模式的用武之地。MemoryStream
或BufferedStream
类可以“装饰”CompressedStream
的另一个实例。您可以从Stream
或BufferedStream
读取/写入与读取/写入任何未修饰的流相同的内容。它通过覆盖CompressedStream
类的方法来实现这一点,通过对流入或流出的字节做任何事情来添加其功能,然后将它们传递到包装对象,无论它是Stream
, FileStream
,甚至是另一位装饰者。
但回到你的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))
{ ... }
...你明白了。
每个流都会在“编写”流的过程中添加功能。