5

我正在阅读这篇文章:

http://www.codeproject.com/Articles/479635/UnderstandingplusandplusImplementingplusDecoratorp

我正在考虑在学校项目中实施这种模式。这不是一个要求,所以我可以半途而废。但是,我只是认为这将是扩展我的知识和专业知识的好机会。

学校的项目是这样的:创建一个披萨订购应用程序,员工可以在其中输入客户的订单。所以一个披萨,它可以有任意数量的配料。

上面的文章(以及Head First:Design Patterns书中的描述)似乎与我的应用程序完美匹配。

这是我的问题:这似乎不是一个好的模式,原因如下:

每当“披萨店”在他们的菜单上添加新的配料时......他们将不得不添加一个全新的类,并重新编译他们的订购系统并重新分配它们?

我认为问题可能在于我在谷歌上搜索的所有示例都必须处理食物和某种配料。

  1. 我是否只是为这种模式找到了错误类型的示例?
  2. 有哪些更好的例子可以实现这种模式?
  3. 食品行业是其中之一吗,只是实施起来很糟糕?
  4. 这是那些已经存在但在实际生产代码中几乎没有使用过的模式之一吗?
4

4 回答 4

5

实际上,装饰器允许您在不重新编译源代码的情况下添加一些行为。您可以在您的披萨域中声明IPizza接口,并在您的应用程序中使用此抽象。然后您可以添加另一个程序集,如果IPizza装饰器将具有其他实现,并使用依赖注入将这些实现注入您的应用程序。因此,您既不需要重新编译应用程序,也不需要重新编译域。

顺便说一句,添加新类比修改现有类更好。你总是可以打破在你修改之前工作的东西。这就是引入单一职责原则的原因。

另一个你的问题:

  1. 不,您正在找到使用装饰器模式的好例子(尤其是 Head First 书中的一个)。还可以看看 IO Stream 类和它的装饰器以获得灵感。
  2. 模式应该解决问题。如果你没有问题,那是针对哪个模式,而不是模式的错误使用。
  3. 模式不拘泥于行业。他们坚持你的代码问题(通常是关于代码重复)
  4. 不,这是很好的模式。再想想 .Net 中的 Streams。是生产代码吗?
于 2013-01-25T16:58:38.037 回答
1

通常在现实世界的应用程序中,您将处理更抽象的对象(即不是比萨饼和咖啡之类的东西 :))

装饰器模式的一个真实示例是 JavaBufferedReader类。例如,它为 a 添加了附加功能FileReader

好处是您可以在运行时更改行为,并且您不会被许多不同的对象所束缚。

在您的示例中,如果我有四个对象:

Pizza
Tomatoes
Cheese
Mushrooms

然后我可以用四种成分的任意组合制作披萨。否则我将不得不有大量的类来允许这种行为,例如PizzaWithTomatoesAndCheesePizzaWithTomatoesAndMushrooms

于 2013-01-25T16:56:37.940 回答
0

我能想到的关于装饰器模式的一个“缺点”是设计是僵化的。

当您装饰一个对象时,您通常不太了解该对象,因为您正在实现一个精简的通用接口。当您第一次实现该接口时,该接口可能适合您的算法,但更改可能会出现问题。

考虑这个关于绘画程序的例子。我喜欢它,因为它说明了我在该模式中看到的两个问题。

装饰器之一是填充形状。如果您对形状唯一了解的是通用界面,您打算如何填充形状?至少如果它是一个只绘制矩形的程序,那么可以在保持相同的类设计的情况下完成一些不太难看的事情,但是绘制矩形似乎是一个人为的例子......

其次,画一个形状可以做多少事情?基本上只是不同类型的边框和不同类型的填充。也许阴影和文字。关键是,如果只有一种或两种可能的装饰,Decorator 可能是一个过度设计的解决方案。

我认为,一个更重要的问题是每个包装器加上对象都执行其操作(抽象装饰器类中目标方法的实现是delegate.operation())。因此,当需求发生变化时会发生什么,我们不应该一个接一个地执行,而是一个应该取代其他的,或者包装器的行为应该取决于被包装对象的某些值。

正如您所说,我怀疑该模式几乎从未使用过,因为您需要稳定的需求,几种类型的装饰器来证明开销和真正的包装行为,其中被包装者的价值总是无关紧要的。

事实上,我能找到的唯一合理示例是 Java 核心库中的示例,例如 InputStream。

另一个荒谬的例子出现在你提到的 Head First 书中,他们用它来计算咖啡的价格,在那里他们创建了一连串的类来计算咖啡的价格(每个类专门用于添加一个新的总成本!)

对我来说,这表明很难在野外找到这种模式的合法用途。

于 2019-08-23T19:36:23.710 回答
0

我同意使用装饰器模式仅出于一个原因:

  • 代码更改管理方案中的 SRP。请参阅《有效使用遗留代码》第 72 页上的出色示例。当您希望在单元测试上下文中划分您的更改时(尤其是对于脆弱的遗留代码),Decorator 非常棒。它非常适合分离关注点,但就像所有这些变化一样,它是有代价的(见下文)。

我不同意使用装饰器模式有几个原因,在更一般的情况下:

  • 它可能很慢,尤其是在对性能至关重要的代码部分中使用时。许多装饰器的重复嵌套会导致JMP生成的程序集中出现重复的 s,这可能会产生严重的性能影响。
  • 当您必须读取/解析多重嵌套的装饰器并确定其执行顺序时,就像剥洋葱层一样两者的)。
  • This idea of "wow, you can assemble any set of permutations you like using decorators!" is solvable in other ways, such as favouring composition over inheritance, where we exhaustively compose structs or classes in such a way that they can accommodate all possibilities of composition required for that class; e.g. a player character in a game may have a gun and / or a vehicle they are driving; so to know what these are, we provide fields for both (these may be either nested structs or pointers / references to other structs classes, allocated separately), whether used or not. This is common in entity-component systems for games.
  • A proliferation of classes is avoided, which may or may not be a good thing in your case.

归根结底,组合是非常专业的并且忽略了 SRP,但归根结底,大多数代码都是为专门目的而编写的,而不是为了无休止的重用。

归根结底,我很乐意在积极开发/原型设计功能期间使用装饰器,但我的目标是在功能集确定后将其切换为更具体的硬编码解决方案。

于 2020-01-28T18:19:07.247 回答