装饰模式问题

时间:2016-06-05 16:31:33

标签: java design-patterns decorator

最近我读到了装饰设计模式,但我留下了一些我在网上找不到的未解答的问题。我不打算显示代码,因为我不想让这个问题比实际更复杂。我只想举个例子:

地铁商店:

组件 - > SubSandwich

ConcreteComponent - > 15cmSub,30cmSub

装饰者 - >成分

ConcreteDecorator - >白奶酪,黄奶酪,果酱,鸡肉。

这就是地铁商店的运作方式。选择你的核心三明治,然后添加你喜欢的所有浇头。但我还有一些问题:

  1. 如果成分组合无效怎么办?例如,地铁政策说在同一个子中不能有两种奶酪。现在让我们假设浇头有10000种可能的组合,只有一种是无效的。这是否完全破坏了装饰模式?

  2. 如果两种成分依赖,该怎么办?例如,如果您订购生菜那么您需要其他种类的蔬菜来制作“有效”的子菜。

  3. 何时使用装饰器模式而不是使用ComponentList of Componentient的SubSandwich类更好?我知道这里的成分不会增加行为,这使得地铁的例子不准确,但我们假设他们这样做了。

  4. 为何延伸?为什么不使用接口?

4 个答案:

答案 0 :(得分:4)

原始结构中的图案并未解决任何实际问题。您需要将它们与一般的SOLID原则混合使用。对于示例问题,您可以在Decorators之上实现Builder模式。你将拥有一些DSL类型的代码,例如。

Builder.aPizza().withCheeze(someCheeze).addTopping(someTopping).and(someOtherTopping).build();

使用此模式/样式,您可以在调用build()方法时验证对象的中间状态,甚至在最后阶段。

答案 1 :(得分:2)

  1. 模式不适用于每个用例。装饰器模式实际上对构建多组件结构没有用,因为它只需1个接口就可以将所有内容组合成1个对象。这种方法仅适用于新行为仅仅是“装饰”的情况。

    一个很好的例子是Collections.synchronizedList(List list),它通过在它们周围包裹synchronized块来“装饰”所有方法。

    您可能仍然可以使用装饰器来处理您描述的用例,但每个装饰步骤都必须检查是否可以应用,如果没有则抛出。

    老实说,我甚至不知道为什么这些例子如此受欢迎。我没有看到像这样使用的装饰器模式。它不是用于添加组件。

  2. 参见1.它甚至可能是不可能的,因为生菜装饰者不能强制执行之后有另一个装饰器。这显然不能很好地利用这种模式。

  3. 如果您喜欢它或者效果更好。喜欢三明治工厂。

  4. 因为这是装饰者的伎俩。它装饰了现有物品的行为。您不想添加新界面。一个有用的地方就是当你是中间人时:

    E.g。库A生成ListItem个对象,并且您希望使用库B来显示它们。你既不能改变A产生的东西,也不能改变B消耗它们的方式。但是当你不喜欢A如何实现B用来显示的toString()方法时,你可以简单地包装一个新的toString()方法。它仍然是ListItem,所以B不会注意到,就像A也没有注意到任何东西。

答案 2 :(得分:2)

装饰者模式的意图和你的应用程序的意图不匹配。装饰器不适合您的用例。

enter image description here

来源:https://en.wiki2.org/wiki/Decorator_pattern

  

装饰器模式可以用于静态地(或者在某些情况下)在运行时扩展(装饰)某个对象的功能,独立于同一类的其他实例,只要在设计时完成一些基础

你已经错过了" 独立"在装饰者的意图。如果你想建立相互关系或互斥,那么Decorator就无用了。

另一方面,Builder_pattern符合您的要求,因为您可以设置强制性&用于构建对象的可选参数。

您可以浏览这些SE问题:

When would you use the Builder Pattern?

Builder Pattern in Effective Java

答案 3 :(得分:1)

  
      
  1. 如果成分组合无效怎么办?例如,地铁政策说在同一个子中不能有两种奶酪。
  2.   

那么装饰者模式可能不是你想要的。 Decorator是关于在不改变界面的情况下编写行为。它不是关于连接属性,并且它不能很好地工作,因为只有最外面的装饰器实例(通常)直接可供用户使用。如果你需要以某种方式知道SubWithProvolone是否包裹SubWithTurkey - 或任何其他特定类型的子 - 那么你做错了。

我所知道的最好的例子是Java I / O类InputStreamOutputStreamReaderWriter以及它们的所有子类。任何InputStream都可以通过BufferedInputStream进行装饰来缓冲;任何InputStream都可以ReaderInputStreamReader作为BufferedWriter进行装饰。 ..

但是如果你开始投入诸如“BufferedWriter一定不能被另一个viewDidLoad”(假设)装饰的规则那么它会破坏 - 没有好的方法可以强制执行它,并且事实上,规则没有真正的需要。如果你试图模拟一个真正需要关于如何组合组件的规则的情况,那么Decorator不是很合适。

  
      
  1. 如果两种成分依赖,该怎么办?例如,如果您订购生菜那么您需要其他种类的蔬菜来制作“有效”的Sub。
  2.   

这与#1基本相同。你可能会把某些东西拼凑在一起来解决这个问题,而前一个问题,但如果这些问题完全存在,那么装饰师就不适合这种情况。

  
      
  1. 什么时候使用装饰器模式而不是使用ComponentList of Ingredient的SubSandwich类更好?我知道这里的成分不会增加行为,这使得地铁的例子不准确,但我们假设他们这样做了。
  2.   

你的问题的其他方面对于SO来说有点宽泛,但这个方面太宽泛了。我们回答有关编程细节的具体问题。

  
      
  1. 为何延伸?为什么不使用接口?
  2.   

你询问一个错误的二分法。 Java区分接口和纯抽象类是一种肤浅的语言设计细节,主要用于支持Java对单一继承实现的限制。如果您的问题被重定向到C ++而不是设计模式同样相关,那么接口和纯抽象类之间没有深刻的区别。您可以围绕作为Java接口的基类型实现Decorator模式。

话虽如此,我已经提到的Java I / O类确实使用类而不是接口作为基类型。既有优点也有缺点,但其中一个优点是它允许它们使用模板方法模式来提供更简单的具体实现类的实现。但即便如此,即使将装饰器类型声明为接口,也可以为此提供抽象基类。