Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

decorator design pattern violating is-a relationship

Tags:

java

decorator

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.

like image 635
Tarun Bhatt Avatar asked Nov 11 '22 09:11

Tarun Bhatt


1 Answers

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!

like image 93
Mark Peters Avatar answered Nov 15 '22 05:11

Mark Peters