简体   繁体   English

为什么要使用装饰模式?

[英]Why use the decorator pattern?

Ive done research on this pattern, and Im still not sure why it is 100% better than an alternative which I will describe shortly. 我已经对这种模式进行了研究,我仍然不确定为什么它比我稍后将要描述的替代品好100%。 I will discuss two ways to implement streams. 我将讨论两种实现流的方法。 As far as I am concerned, the decorator pattern and my approach are two sides of the same coin. 就我而言,装饰图案和我的方法是同一枚硬币的两面。

I wont describe the decorator pattern here since I will assume that people interested in this answer are already familiar with it. 我不会在这里描述装饰模式,因为我会假设对此答案感兴趣的人已经熟悉它。

The alternative is to use lists. 另一种方法是使用列表。 Lists are inherently ordered, and seem to do the same thing. 列表本质上是有序的,似乎做同样的事情。 Lets say we have an output stream that we wrap in an encryption stream which we wrap in a zip stream. 假设我们有一个输出流,我们将其包装在一个加密流中,我们将其压缩为一个压缩流。

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

The alternative to would be to create an output stream, and add a zipper and encrypter to it. 另一种方法是创建输出流,并为其添加拉链和加密器。

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

The code for OutputStream.write would look something like: OutputStream.write的代码如下所示:

public void write(String data) {
  for(StreamDecorator d : decorators) {
    data = d.doMyThing(data);
  }
  //continue normal write operation
}

am I correct in saying these two approaches are two sides of the same coin? 我是否正确地说这两种方法是同一枚硬币的两面? If not, where is the decorator pattern more useful than this approach? 如果没有,装饰模式在哪里比这种方法更有用?

It's roughly the same to me. 对我来说大致相同。 In your list pattern, you'll end up calling : 在您的列表模式中,您最终会调用:

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

The difference is just that you'll be using a specific method rather than constructors, which might be better in some situations. 区别在于您将使用特定方法而不是构造函数,这在某些情况下可能更好。

Your example bypasses a lot of things. 你的例子绕过很多东西。 If you look at Java's streams, you can see that each of them has a single responsibility. 如果你看一下Java的流,你可以看到他们每个人都有一个责任。 FileOutputStream knows how to write bytes to file, SocketOutputStream knows how to write bytes to socket, etc. They could be used by themselves or with other streams, but they're "terminating", ie the bytes are gone outside of your handling. FileOutputStream知道如何将字节写入文件, SocketOutputStream知道如何将字节写入套接字等。它们可以单独使用或与其他流一起使用,但它们“终止”,即字节不在处理之外。

Your Outputstream.write doesn't describe what the "normal write operation" is, but with your list approach, all the "terminating" streams would need to have a list of StreamDecorators and the same code to iterate them and process the data. 您的Outputstream.write没有描述“正常写入操作”是什么,但是使用列表方法,所有“终止”流都需要有一个StreamDecorators列表和相同的代码来迭代它们并处理数据。

While it's not at all impossible to do this with a list (therefore becoming Chain Of Responsibility ), it's a lot simpler to do it as a decorator, since the class needs to do only a single thing. 虽然使用列表(因此成为责任链 )完成这一点并不是不可能的,但是作为装饰器来做它会简单得多,因为类只需要做一件事。

Kayaman mentioned single responsibility, but it's more than that to me. Kayaman提到了单一责任,但对我来说不仅如此。

Decorators nest, which means that at a given level of nesting, there is encapsulation (and subsequently information hiding). 装饰器嵌套,这意味着在给定的嵌套级别,存在封装(以及随后的信息隐藏)。 This means that code is more resistant to changes, etc. 这意味着代码更能抵抗变化等。

It might help to look at the coupling in the two designs: 查看两种设计中的耦合可能会有所帮助:

流装饰器的UML对象图

UML带列表的备选对象图

In the Decorator example, the coupling only goes to a single component (the object that the stream wraps). 在Decorator示例中,耦合仅转到单个组件(流包装的对象)。 The goal of the decorator is to enhance the object it wraps. 装饰器的目标是增强它包装的对象。 It has no idea what that object is, apart from it respecting the type of objects it can wrap (eg, OutputStream ). 它不知道该对象是什么,除了它尊重它可以包装的对象类型(例如, OutputStream )。

In the list (with add() ) example, there is more coupling. 在列表中(使用add() )示例,有更多耦合。 The stream object needs to know more (and could have more reasons to change, re Single Responsibility). 流对象需要了解更多(并且可能有更多理由进行更改,重新单一责任)。 Stream has to worry about failures of any/all of the things it aggregates. Stream必须担心它聚合的任何/所有事情的失败。

I think if you try to implement your alternative all the way, you'll see that it's not as clean as Decorator. 我想如果你试图一直实现你的选择,你会发现它不像Decorator那么干净。 As suggested by another answer, you need 正如另一个答案所暗示的那样,你需要

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

which I don't think can be easily coded generally (for an arbitrary number of decorators add() ed to your Stream). 我认为不能轻易编码(对于任意数量的装饰器add()编辑到您的Stream)。

Another point involves types and nesting. 另一点涉及类型和嵌套。 Since types/subtypes can be used in the Decorator nesting process, it should prevent illogical nesting. 由于类型/子类型可以在Decorator嵌套过程中使用,因此它应该防止不合逻辑的嵌套。 It's not clear that add() respects these typing rules, or it would have to be more restricted. 目前尚不清楚add()遵守这些类型规则,或者它必须受到更多限制。

If you start hard-coding add() rules, you wind up with combined class functions of decorator (see the counter example in https://stackoverflow.com/a/6366543/1168342 ). 如果你开始硬编码add()规则,你最终会使用decorator的组合类函数(参见https://stackoverflow.com/a/6366543/1168342中的计数器示例)。

Your first alternative is not a "list," it is nested constructors. 你的第一个选择不是“列表”,它是嵌套的构造函数。

Anyways, I think that first alternative makes more sense because you want to encrypt the stream, then zip that. 无论如何,我认为第一种选择更有意义,因为你想加密流,然后拉链。

The second alternative looks like you are adding two streams to a single stream. 第二种方法看起来就像是将两个流添加到单个流中。 Where would the data be written in that case? 在这种情况下,数据写在哪里? I would think the original OutputSteam . 我会想原来的OutputSteam

From a functional point of view, as others say, it isn't that different. 从功能的角度来看,正如其他人所说,它并没有那么不同。

But, the Decorator Pattern is better because it is a known thing. 但是,装饰模式更好,因为它是一个众所周知的东西。 So when you describe your design or document it for others, you can say "and I used the decorator pattern here". 因此,当您描述您的设计或将其记录给其他人时,您可以说“我在这里使用了装饰器模式”。 This is much more concise and easier for them to validate than "and I used a List here, where I add the streams to it that I want to get called, and I call them in this order. This allows me to blah blah blah..." 这比他们更简洁,更容易验证,“我在这里使用了一个List,我在那里添加了我想要调用的流,我按照这个顺序调用它们。这让我可以等等等等。 ..”

All of the concepts behind the patterns have been around for decades; 模式背后的所有概念已存在数十年; by naming them, we can more efficiently communicate about the concepts and our intent. 通过命名它们,我们可以更有效地传达有关概念和意图的信息。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM