문제

I have recently started studying about decorator design pattern but I have a query. Decorators implement the same interface as the Component they are trying to decorate. Doesn't this violate the is-a relationship. Moreover since the decorator has the component (through composition) , why is it really required for the decorator to implement the same component interface which the concrete component implements.?

Going through the decorator design pattern on Headfirst, it gives me a feeling that decorators can directly implement the component. There is no need to have an abstract class / interface for decorator.

I am worries that this could really be a dumb question but help would allow me to have a strong foundation.

도움이 되었습니까?

해결책

It's important to understand the difference between composition and Decorator. Decorator is a form of composition, but the main thing that sets it apart is that it does so in a way that lets the wrapper be used by code that would normally use the decorated object.

Let's use a common example to help explore the question. Consider the interface InputStream. I might have a method that copies bytes from one stream to another:

public static void copy(InputStream in, OutputStream out) { ... }

Now say that we had a file that we wanted to copy. I would create a FileInputStream and pass it to copy().

But say I get a requirement that I need to count the number of bytes that were copied.

Well, I could create CountingFileInputStream which extends FileInputStream. The CountingFileInputStream is-a FileInputStream. But what if tomorrow I need to do the same thing for a SocketInputStream? I'd have to create a CountingSocketInputStream that extends SocketInputStream.

I could instead use composition! I could create a class that takes an InputStream and counts bytes that read to it:

public class StreamCounter {

   private final InputStream in;
   private long bytesRead;

   public int read() {
     int nextByte = in.read();
     if (nextByte != -1) bytesRead++;
     return nextByte;
   }
}

This can handle any InputStream, which is great. But we can't use our existing code that takes an InputStream, because StreamCounter is-not-an InputStream.

So this is where Decorator comes in. We can instead make a CountingInputStream that both implements InputStream (and so is-an InputStream) and delegates to another InputStream. That way we can use it in our copy() method.

So in short, in terms of the is-a relationship, CountingInputStream is-an InputStream (which is usually all we care about) but it is-not-a FileInputStream, which allows it to wrap any InputStream, like a LimitInputStream which is decorating a DeflaterInputStream which is decorating a BufferedInputStream which is decorating a FileInputStream. And at the end of the day, copy() doesn't need to care!

다른 팁

It is your choice how you want to implement Decorator class based on use case. It is not mandatory for Decorator class to implement same interface.

So if you see Collections.synchronizedCollection.(Collection<T> c) , we have a static method here which is acting as decorator and same interface is not implemented.

But in implementationshown t this link Decorator class does implement the interface as use case needs it [as polymorphism is used].

So its not mandatory and there is no voilation of "is a" relationship.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top