装饰者模式:增强设计?

Decorator pattern: enhanced design?

我正在阅读 "Head first design patterns book" 中提供的有关装饰图案的示例。

我注意到两件事:

  1. 如果你需要从包装装饰器的堆栈中移除一个装饰器,你将不得不通过组件引用一个一个地迭代,这是 O(n) 复杂度。
  2. 从概念上讲,我发现将基本组件包装(封装)到装饰器对象中是错误的。应该反过来;组件对象应该封装装饰对象。

我是设计模式的新手,我很可能是错的。请向我解释一下我认为的方式有什么特别错误,以便我学习。

我创建了一个不同的设计,它解决了我提到的问题。也许他们增加了新问题;请随时指出问题。

这是建议的 UML 图:

基本上我所做的是在组件 class 中创建了一个字典,它保存了添加了哪些装饰器,并使装饰器抽象 class 尚未从组件继承自接口(所以组件抽象class)。

这样一来,我们可以去掉任何我们想要的装饰,复杂度为O(1),而且更符合逻辑的是组件包裹装饰器的方式构造,而不是反过来

我明白了,也许我没有注意到原始Decorator模式设计的一些优点。请多多指教

这是我的代码url

编辑:

客户何时需要移除装饰器的示例:

比如说客户在选择调味品,他在加鞭子,去掉焦糖,根据客户选择添加的装饰品,看看每次总价如何变化。

  1. 为什么需要 "remove a decorator from the stack of the warped decorators"?这是什么意思?我认为您混淆了两个不同的概念,即装饰器模式和堆栈。前者是面向对象编程中的设计模式,后者是数据结构

  2. 装饰器模式的存在使得新功能可以添加到基础组件,而无需重新定义 use/are 依赖它的组件。这就是装饰器组件 "encapsulates" 基础组件的原因,这样它就可以使用它包含的任何功能,同时添加所需的其他功能。如果基础组件封装了装饰器组件,您将如何引用其中任何给定组件中存在的功能?按照你的例子,假设我调用 Mocca.GetCost()。如果它没有被覆盖或重新定义,将调用 CondimentDecorated.GetCost(),我想,考虑到您要执行的操作,这反过来将调用 Beverage.GetCost()。这个方法会做什么?遍历字典以查找调用哪个装饰器方法?这没有意义,因为在调用 CondimentDecorated.GetCost() 时,您只会再次调用 Beverage.GetCost()。如果你可以,正如你所说,从装饰器字典中 "remove any decoration you want" ,这一切将如何工作?那么,当您调用 Mocca.GetCost()?

  3. 时,行为会怎样

这并不是说您尝试做的事情是不可能的,而且质疑为什么事情是这样是很好的。但是这里有很多 OOP 误解和违规行为。意思是,不仅要质疑如何让事情变得更好,还要质疑为什么他们会按照现在的方式完成。

  1. if you will need to remove a decorator from the stack of the wrapped decorators, you will have to iterate one by one through the component reference, which is O(n) complexity.

的确,当装饰器被包装时,从理论上讲,移除装饰器会更复杂。但是,您需要考虑什么是可能的 n。我将猜测提议的装饰器模式,可能有一个小的 (max(n) == 20) 迭代器。迭代这么多不会是一个实际问题。

  1. Conceptually I find it wrong to wrap (encapsulate) the base component in to the decorator object. It should be reversed; the component object should encapsulate the decorating objects.

Decorator 模式试图在不修改组件的情况下添加功能(通过具体的装饰器)class。在某些情况下,无法更改组件(例如,它来自标准库)。使用您建议的方法,必须修改组件以封装其装饰器。这不是Decorator的本意。

original GoF book 中,设计模式对其解决的问题 有明确的定义,后果(并非都是积极的!)的设计模式。根据您的复杂点(删除装饰器),作者提到了这个后果:

  1. Lots of little objects. A design that uses Decorator often results in systems composed of lots of little objects that all look alike. The objects differ only in the way they are interconnected, not in their class or in the value of their variables. Although these systems are easy to customize by those who understand them, they can be hard to learn and debug.