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!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With