我正在读这篇文章:
http://www.codeproject.com/Articles/479635/UnderstandingplusandplusImplementingplusDecoratorp
我正在考虑在学校项目中实施这种模式。这不是一个要求,所以我可以减半。但是,我只是认为这是一个扩展我的知识和专业知识的好机会。
学校项目是这样的:创建一个比萨订购应用程序,员工输入客户的订单。所以披萨,它可以有任意数量的浇头。
以上文章(以及 Head First:Design Patterns 一书中的描述)似乎与我的应用程序完美匹配。
这是我的问题:这似乎不是一个好的模式,这就是为什么:
每当“披萨店”在其菜单中添加新的顶部时......他们将不得不添加一个全新的类,并重新编译他们的订购系统并重新分发它们?
我想也许问题是我谷歌的所有例子都必须处理某种食物和配料。
答案 0 :(得分:3)
实际上,Decorator允许您添加一些行为而无需重新编译源代码。您可以在披萨域中声明IPizza
接口,并在您的应用程序中使用此抽象。然后,您可以添加另一个程序集,如果IPizza
装饰器将具有其他实现,并使用依赖项注入将这些实现注入到您的应用程序中。因此,您无需重新编译应用程序或域。
BTW添加新类比修改现有类更好。在修改之前,你总是可以破坏正常工作的东西。这就是为什么引入单一责任原则的原因。
您的另一个问题:
答案 1 :(得分:1)
通常在现实世界的应用程序中,您将处理更抽象的对象(即不是比萨饼和咖啡的东西:))
Decorator模式的真实示例是Java BufferedReader
类。例如,它为FileReader
添加了附加功能。
好处是你可以在运行时更改行为,而不是让你拥有许多不同的对象。
在您的示例中,如果我有四个对象:
Pizza
Tomatoes
Cheese
Mushrooms
然后我可以用这四种成分的任意组合制作披萨。否则我将不得不有大量的类来允许这种行为,例如PizzaWithTomatoesAndCheese
,PizzaWithTomatoesAndMushrooms
答案 2 :(得分:0)
我可以想到的有关装饰器模式的“缺点”是设计是刚性的。
装饰对象时,由于要实现精简的通用接口,因此您通常对对象不了解很多。初次实现该接口时,对于您的算法而言可能是合适的,但是更改可能会出现问题。
考虑this example有关绘画程序。我喜欢它,因为它说明了我在模式中看到的两个问题。
装饰器之一是填充形状。如果您唯一了解形状是通用界面,您打算如何计划填充形状?至少如果它是一个仅绘制矩形的程序,则可以在保持相同类设计的情况下完成一些不太丑陋的事情,但是绘制矩形似乎是一个人为的例子……
第二,绘画形状时可以做几件事?基本上只是不同类型的边框和不同类型的填充。也许阴影和文字。关键是,如果可能的装饰只有一种或两种,那么Decorator可能是过度设计的解决方案。
我认为,一个更重要的问题是每个包装器和对象都执行其动作(抽象Decorator类中target方法的实现是delegate.operation()
)。因此,当需求发生变化并且我们不应该再一个接一个地执行另一个需求,而另一个应该取代另一个需求时,或者包装器的行为应取决于包装对象的某些值时,会发生什么情况。
正如您所说,我怀疑几乎没有使用过这种模式,因为您需要稳定的需求,几种类型的装饰器来证明开销和真正的包装行为(其中被包装者的价值始终无关紧要)。
实际上,我能找到的唯一明智的示例是Java核心库中的示例,例如InputStream。
您提到的《 Head First》一书中出现了另一个荒谬的例子,他们用它来计算咖啡的价格,在那里他们创建了一系列的类来计算咖啡的价格(每个类都专门使用在总费用中添加新费用!)
对我来说,这表明很难在野外找到这种模式的合法用途。
答案 3 :(得分:0)
我仅出于一个原因同意使用Decorator模式:
在更一般的情况下,出于多种原因,我不同意使用Decorator模式:
JMP
,这可能会对性能产生严重影响。struct
或类,使它们可以容纳该类所需的所有组合可能性;例如游戏中的玩家角色可能拥有自己驾驶的枪支和/或车辆;为了知道它们是什么,我们为这两个字段提供了字段(它们可以是嵌套的struct
或指向其他struct
类的指针/引用,分别分配),无论是否使用。这在游戏的实体组件系统中很常见。最终,合成是相当专业的,并且忽略了SRP,但是到最后,大多数代码都是出于专门目的而不是无休止的重用而编写的。
最终,我很乐意在主动开发/原型开发功能时使用Decorator,但目的是在功能集建立之后再将其切换为更具体的硬编码解决方案。