我最近开始研究装饰设计模式,但我有一个查询。装饰器实现与他们试图装饰的组件相同的接口。这不违反is-a关系。 此外,由于装饰器具有组件(通过组合),为什么装饰器真的需要实现混凝土组件实现的相同组件接口。
通过Headfirst上的装饰器设计模式,它让我觉得装饰者可以直接实现组件。没有必要为装饰器提供抽象类/接口。
我担心这可能是一个愚蠢的问题,但帮助可以让我有一个坚实的基础。
答案 0 :(得分:1)
理解构图和装饰者之间的区别非常重要。装饰器是一种组合形式,但是它的主要之处在于它以一种允许包装器被通常使用装饰对象的代码的方式这样做。
让我们使用一个常见的例子来帮助探索这个问题。考虑接口InputStream
。我可能有一个方法将字节从一个流复制到另一个流:
public static void copy(InputStream in, OutputStream out) { ... }
现在说我们有一个我们想要复制的文件。我会创建一个FileInputStream
并将其传递给copy()
。
但是我说要求我需要计算复制的字节数。
好吧,我可以创建扩展CountingFileInputStream
的{{1}}。 FileInputStream
是一个CountingFileInputStream
。但是如果明天我需要为FileInputStream
做同样的事情呢?我必须创建一个扩展SocketInputStream
的{{1}}。
我可以改用合成!我可以创建一个带有CountingSocketInputStream
的类并计算读取它的字节:
SocketInputStream
这可以处理任何 InputStream
,这很棒。但我们无法使用我们现有的代码public class StreamCounter {
private final InputStream in;
private long bytesRead;
public int read() {
int nextByte = in.read();
if (nextByte != -1) bytesRead++;
return nextByte;
}
}
,因为InputStream
不是 InputStream
。
所以这就是Decorator的用武之地。我们可以制作一个CountingInputStream
来实现StreamCounter
(以及 is-an InputStream
)和委托到另一个InputStream
。这样我们就可以在InputStream
方法中使用它。
简而言之,就InputStream
关系来说,copy()
是一个is-a
(通常我们都在关注),但它不是{{1}允许它包装任何CountingInputStream
,就像LimitInputStream
正在装饰DeflaterInputStream
,正在装饰BufferedInputStream
,正在装饰FileInputStream
。在一天结束时,InputStream
不需要关心!
答案 1 :(得分:0)
根据用例,您可以选择如何实现Decorator类。 Decorator类不一定要实现相同的接口。
因此,如果您看到Collections.synchronizedCollection.(Collection<T> c)
,我们在这里有一个静态方法,它充当装饰器,并且没有实现相同的接口。
但是在实现中,这个link Decorator类确实实现了接口,因为用例需要它[因为使用了多态]。
所以它不是强制性的,也没有“是一种”关系的消息。