为什么要使用装饰模式?

时间:2015-12-09 16:32:09

标签: java design-patterns decorator

我已经对这种模式进行了研究,我仍然不确定为什么它比我稍后将描述的替代品好100%。我将讨论两种实现流的方法。就我而言,装饰图案和我的方法是同一枚硬币的两面。

我不会在这里描述装饰模式,因为我会假设对此答案感兴趣的人已经熟悉它。

另一种方法是使用列表。列表本质上是有序的,似乎做同样的事情。假设我们有一个输出流,我们将其包装在一个加密流中,我们将其压缩为一个压缩流。

OutputStream stream = new ZipStream(new EncryptStream(new OutputStream(outputLocation)));
stream.write(data);

替代方法是创建输出流,并为其添加拉链和加密器。

OutputStream stream = new OutputStream(outputLocation);
stream.add(new EncyrptStream());
stream.add(new ZipStream());
stream.write(data);

OutputStream.write的代码如下所示:

public void write(String data) {
  for(StreamDecorator d : decorators) {
    data = d.doMyThing(data);
  }
  //continue normal write operation
}
我是否正确地说这两种方法是同一枚硬币的两面?如果没有,装饰器模式在哪里比这种方法更有用?

5 个答案:

答案 0 :(得分:3)

对我来说大致相同。在您的列表模式中,您最终会调用:

findAll

不同之处仅在于您将使用特定方法而不是构造函数,这在某些情况下可能更好。

答案 1 :(得分:3)

你的例子绕过很多东西。如果你看一下Java的流,你可以看到他们每个人都有一个责任。 FileOutputStream知道如何将字节写入文件,SocketOutputStream知道如何将字节写入套接字等。它们可以单独使用或与其他流一起使用,但它们“终止”,即字节不在你的处理之外。

您的Outputstream.write没有描述“正常写入操作”是什么,但是使用您的列表方法,所有“终止”流都需要有StreamDecorators列表和相同的代码迭代它们并处理数据。

虽然使用列表(因此成为Chain Of Responsibility)完全不可能这样做,但是作为装饰器做这件事要简单得多,因为类只需要做一件事。

答案 2 :(得分:1)

Kayaman提到了单一责任,但对我来说不仅仅是这个。

装饰器嵌套,这意味着在给定的嵌套级别,存在封装(以及随后的信息隐藏)。这意味着代码更能抵抗变化等。

可能有助于查看两种设计中的耦合:

UML Object diagram of Stream Decorator

UML Object diagram of alternative with lists

在Decorator示例中,耦合仅转到单个组件(流包装的对象)。装饰器的目标是增强它包装的对象。它不知道该对象是什么,除了它尊重它可以包装的对象类型(例如,OutputStream)。

在列表(带add())示例中,有更多耦合。流对象需要了解更多(并且可能有更多理由进行更改,重新单一责任)。 Stream必须担心它聚合的任何/所有事情的失败。

我认为如果你试图一直实现你的选择,你会发现它不像Decorator那么干净。正如另一个答案所示,你需要

zipStream.doMyThing(encryptStream.doMyThing(outputStream.doMyThing(data))));

我认为不能轻易编码(对于您的Stream中任意数量的装饰器add()。)

另一点涉及类型和嵌套。由于类型/子类型可以在Decorator嵌套过程中使用,因此它应该防止不合逻辑的嵌套。目前尚不清楚add()是否尊重这些打字规则,或者它必须受到更多限制。

如果您开始编写add()规则的硬编码,最后会使用装饰器的组合类函数(请参阅https://stackoverflow.com/a/6366543/1168342中的计数器示例)。

答案 3 :(得分:0)

你的第一个选择不是“列表”,它是嵌套的构造函数。

无论如何,我认为第一种选择更有意义,因为你想要加密流,然后拉链。

第二种方法看起来就像是在单个流中添加两个流。在这种情况下,数据写在哪里?我想原来的<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <!-- Single menu item Set id, icon and Title for each menu item --> <item android:id="@+id/menu1" android:icon="@drawable/icon_menu1" android:title="menu1" /> <item android:id="@+id/menu2" android:icon="@drawable/icon_menu2" android:title="menu2" /> ....... </menu>

答案 4 :(得分:0)

从功能的角度来看,正如其他人所说,它并不是那么不同。

但是,装饰器模式更好,因为它是已知的东西。因此,当您描述您的设计或将其记录给其他人时,您可以说&#34;并且我在这里使用了装饰模式&#34;。这比他们更简洁,更容易验证和#34;我在这里使用了一个List,在那里我添加了我想要调用的流,我按照这个顺序调用它们。这让我可以等等等等......&#34;

模式背后的所有概念已存在数十年;通过命名它们,我们可以更有效地传达有关概念和意图的信息。