Вопрос

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