Use Cases and Examples of GoF Decorator Pattern for IO
Asked Answered
F

8

64

I have read in wikipedia that Decorator pattern is used for .Net and Java IO classes.

Can anybody explain how this is being used? And what is the benefit of it with a possible example?

There is an example of Windows forms on wikipedia but I want to know how it happens with Java IO classes.

Fireworks answered 16/6, 2011 at 2:5 Comment(2)
possible duplicate of when do we need Decorator Pattern?Blat
It is not a duplicate because the question is about the specific use cases of io library.Handicraftsman
M
164

InputStream is an abstract class. Most concrete implementations like BufferedInputStream, GzipInputStream, ObjectInputStream, etc. have a constructor that takes an instance of the same abstract class. That's the recognition key of the decorator pattern (this also applies to constructors taking an instance of the same interface).

When such a constructor is used, all methods will delegate to the wrapped instance, with changes in the way the methods behave. For example, buffering the stream in memory beforehand, decompressing the stream beforehand or interpreting the stream differently. Some even have additional methods that finally also delegate further to the wrapped instance. Those methods decorate the wrapped instance with extra behaviour.

Let's say that we have a bunch of serialized Java objects in a Gzipped file and that we want to read them quickly.

First open an inputstream of it:

FileInputStream fis = new FileInputStream("/objects.gz");

We want speed, so let's buffer it in memory:

BufferedInputStream bis = new BufferedInputStream(fis);

The file is gzipped, so we need to ungzip it:

GzipInputStream gis = new GzipInputStream(bis);

We need to unserialize those Java objects:

ObjectInputStream ois = new ObjectInputStream(gis);

Now we can finally use it:

SomeObject someObject = (SomeObject) ois.readObject();
// ...

The benefit is that you have a lot of freedom to decorate the stream using one or more various decorators to suit your needs. That's much better than having a single class for every possible combination like ObjectGzipBufferedFileInputStream, ObjectBufferedFileInputStream, GzipBufferedFileInputStream, ObjectGzipFileInputStream, ObjectFileInputStream, GzipFileInputStream, BufferedFileInputStream, etc.

Note that when you're about to close the stream, just closing the outermost decorator is sufficient. It will delegate the close call all the way to the bottom.

ois.close();

See also:

Magdamagdaia answered 16/6, 2011 at 2:37 Comment(5)
FileInputStream doesn't have a constructor that takes an InputStreamMiserly
@Magdamagdaia What happens if I decorate ObjectInputStream by ObjectInputStream ?Sanatorium
in a line it is a bit odd: ObjectInputStream ois = new ObjectInputStream(new GzipInputStream(new BufferedInputStream(new FileInputStream("/objects.gz")))); let's go the Kotlin way and get rid of the "new"Wat
Could we swap second and third? Create ois from bis, bis from gis and gis from fis?Heigl
@Luke: Of course, but gis step will obviously become slower as its source is not buffered in memory anymore.Magdamagdaia
C
19

Let's understand components of Decorator pattern before going through java IO classes.

Decorator pattern has four components ( Wikipedia )

  1. Component: The Component defines the interface for objects that can have responsibilties added dynamically
  2. ConcreteComponent: It is simply an implementation of Component interface
  3. Decorator: The Decorator has a reference to a Component, and also conforms to the Component interface. Decorator is essentially wrapping the Component
  4. ConcreteDecorator: The ConcreteDecorator just adds responsibilities to the original Component.

Now let's map these concepts to java.io pacakge classes.

Component:

InputStream :

This abstract class is the superclass of all classes representing an input stream of bytes.

Applications that need to define a subclass of InputStream must always provide a method that returns the next byte of input.

public abstract int read() is an abstract method.

ConcreteComponent:

FileInputStream:

A FileInputStream obtains input bytes from a file in a file system. What files are available depends on the host environment.

FileInputStream is meant for reading streams of raw bytes such as image data. For reading streams of characters, consider using FileReader.

Examples of all ConcreteComponents of InputStream:

AudioInputStream, ByteArrayInputStream, FileInputStream, FilterInputStream, 
InputStream, ObjectInputStream, PipedInputStream, SequenceInputStream, 
StringBufferInputStream

Decorator:

FilterInputStream:

A FilterInputStream contains some other input stream, which it uses as its basic source of data, possibly transforming the data along the way or providing additional functionality.

Please note that FilterInputStream implements InputStream => Decorator implements Component as shown in UML diagram.

public class FilterInputStream
extends InputStream

ConcreteDecorator:

BufferedInputStream

A BufferedInputStream adds functionality to another input stream-namely, the ability to buffer the input and to support the mark and reset methods.

Examples of all ConcreteDecorators:

BufferedInputStream, CheckedInputStream, CipherInputStream, DataInputStream, 
DeflaterInputStream, DigestInputStream, InflaterInputStream, 
LineNumberInputStream, ProgressMonitorInputStream, PushbackInputStream

Working example code:

I have used BufferedInputStream to read each character of a word, which has been stored in a text file a.txt

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("ravindra.txt")));
while(bis.available()>0)
{
        char c = (char)bis.read();
        System.out.println("Character = "+c);
}

When to use this pattern:

  1. Object responsibilities and behaviours should be dynamically added/removed
  2. Concrete implementations should be decoupled from responsibilities and behaviours
  3. When sub - classing is too costly to dynamically add/remove responsibilities
Chavira answered 27/5, 2016 at 11:42 Comment(2)
How is FileInputStream considered as ConcreteComponent. Isn't this not a decorator as it takes an InputStream?Toadflax
FileInputStream doesn't have any constructor with InputStream as its parameter. A FileInputStream obtains input bytes from a file in a file system that's why FileInputStream is ConcreteComponent but FilterInputStream takes InputStream as a constructor parameter to further work on it like FilterInputStream(InputStream in) that's why it is a decorator. A decorator will always take "component (like InputStream)" as it's constructor input to work on it as discussed above.Fuld
S
8

In .NET, there are a bunch of stream decorators, like BufferedStream, CryptoStream, GzipStream, etc. All those decorate Stream class.

Sphygmoid answered 16/6, 2011 at 2:11 Comment(0)
H
7

A - Decorator Pattern

A.1 - Use Case of Decorator Pattern

Decorator pattern is used for extending a legacy functionality without changing the legacy class. Let's say, we have a concrete class that implements an interface. And we need to extend the functionality of the existing method however because that the existing class, and its methods are already used by other classes, thus we don't want to make a change in the existing classes. But we also need extended functionality on newer class, then how do we solve this problem?

1- We can't change the existing legacy code
2- We want to extend the functionality

So we use decorator pattern, wrap the existing class inside the decorators.

B - Basic GoF Decorator Pattern Example

Here we have a simple interface and an implementation/concrete class. The interface has one simple method, which is getMessageOfTheDay and it returns a String. Assume that there are lots of other classes using this method. So if we want to make a change in the implementation/concrete class, it will affect the old legacy code. We want to change it for only the new classes so we use the decorator pattern.

Here is a trivial example of Gang Of Four Decorator Design pattern;

B.1 - Greeter.java

public interface Greeter {
    String getMessageOfTheDay();
}

B.2 - BasicGreeter.java

public class BasicGreeter implements Greeter {

    @Override
    public String getMessageOfTheDay() {
        return "Welcome to my server";
    }

}

B.3 - Abstract Decorator Class: GreeterDecorator.java

public abstract class GreeterDecorator implements Greeter {

    protected Greeter greeter;

    public GreeterDecorator(Greeter greeter) {
        this.greeter = greeter;
    }

    public String getMessageOfTheDay() {
        return greeter.getMessageOfTheDay();
    }

}

B.4 - Concrete Decorator Class: StrangerDecorator.java

public class StrangerDecorator extends GreeterDecorator {

    public StrangerDecorator(Greeter greeter) {
        super(greeter);
    }

    @Override
    public String getMessageOfTheDay() {
        return "Hello Stranger " + super.getMessageOfTheDay();
    }

}

B.5 - Demo Code: DecoratorDemo .java

public class DecoratorDemo {

    public static void main(String[] args) {
        Greeter greeter = new BasicGreeter();

        String motd = greeter.getMessageOfTheDay();

        System.out.println(motd);

        Greeter newGreeter = new StrangerDecorator(greeter);

        String newMotd = newGreeter.getMessageOfTheDay();

        System.out.println(newMotd);

        Greeter muchNewGreeter = new StrangerDecorator(new StrangerDecorator(greeter));

        String newestMotd = muchNewGreeter.getMessageOfTheDay();

        System.out.println(newestMotd);
    }

}

Take a look at those examples. The abstract decorator class is needed to wrap the original contract and implementation. Using the abstract decorator, you can create newer multiple decorators but in this example, BasicGreeter is wrapped inside the abstract decorator and we have only created on new decorator class which is StrangeGreeter. Please notify that decorator classes can be used like a train, we can wrap a decorator inside another decorator or the same. The functionality is extendable but the original class is preserved without any modification.

C - OutputStream Demo

Let's take a look at this example. We want to write a string to file with OutputStream. Here is the demo code;

C.1 - Sample OutputStream Demo To Write A File

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;

public class FileWriterDemo {

    public static void main(String[] args) throws IOException {
        File file = new File("./normal.txt");
        file.createNewFile();

        OutputStream oStream = new FileOutputStream(file);

        String content = "I love Commodore 64";

        oStream.write(content.getBytes());

        oStream.close();
    }

}

C.2 - JSON Decorator Output: normal.txt

There will be a new file with name "normal.txt" created under the project folder and the content will be;

I love Commodore 64

D - JSON OutputStream Decorator Demo

Now, I want to create a JSON wrapper format, which is as follows;

{
    data: <data here>
}

What I want is to write the content inside a simple one field JSON format. How can we achieve this goal? There are many trivial ways. However, I will use the GoF Decorator Pattern by writing a JSONDecorator which extends the OutputStream class of Java;

D.1 - JSON Decorator for OutputStream: JSONStream.java

public class JSONStream extends OutputStream {

    protected OutputStream outputStream;

    public JSONStream(OutputStream outputStream) {
        this.outputStream = outputStream;
    }

    @Override
    public void write(int b) throws IOException {
        outputStream.write(b);
    }

    @Override
    public void write(byte[] b) throws IOException {
        String content = new String(b);

        content = "{\r\n\tdata:\"" + content + "\"\r\n}";

        outputStream.write(content.getBytes());
    }

}

D.2 - JSON Decorator Demo: JSONDecoratorDemo.java

public class JSONDecoratorDemo {

    public static void main(String[] args) throws IOException {
        File file = new File("./json.txt");
        file.createNewFile();

        OutputStream oStream = new FileOutputStream(file);

        JSONStream js = new JSONStream(oStream);

        String content = "I love Commodore 64";

        js.write(content.getBytes());

        js.close();
        oStream.close();
    }

}

D.3 - JSON Decorator Output: json.txt

{
    data:"I love Commodore 64"
}

Actually, OutputStream itself a Decorator Pattern, it is the abstract decorator and concrete decorator in here is the JSONStream class.

Handicraftsman answered 23/12, 2017 at 2:2 Comment(1)
js.close() should also automatically close oStream by calling close from overriding close inside JSONStream. so oStream.close() is not required.Fuld
D
5

The decorator pattern is used in java.io classes when you manipulated input/output streams (and the same applies for readers and writers).

inputstream, bytearrayinputstream, stringbuilderinputstreams and so on are based elements. Filterinputstream is the base class for the decorator classes. Filter input streams (such as bufferedinput stream) can do additional things when they read streams or write to them.

They are built by encapsulating a stream, and are streams themselves.

new BufferedReader( new FileInputStream() ).readLine();

I can't think of any class implementing this pattern in java.net, but I think your were told about this package as it is strongly tied to java.io (socket.getInputStream for instance).

Actually, here is a course from O'Relly (pdf on uwosh.edu | archive.org, slides on slideshare.net) that explains how decorator is implemented in java.io.

Dratted answered 16/6, 2011 at 2:12 Comment(0)
E
2

One way you can decorate an input/output stream is to apply compression/decompression to it. See the classes in java.util.zip, for example. Such a decorated stream can be used exactly the same way as a "regular" input/output stream, with compression/decompression performed totally transparently.

Exemplar answered 16/6, 2011 at 2:8 Comment(0)
R
2

The decorator pattern is used to add functionality to existing objects such as a class defined in a library. You can then "decorate" it to fit your needs. If you are interested in learning more about patterns I recommend "Design Patterns" by the Gang of Four.

Retroflex answered 16/6, 2011 at 2:25 Comment(0)
D
2

Well, I may be late to the party but this question never gets old. The key point to understand Decorator is that it gives you the ability to plug an object to an existing object to another existing object and so on. It is popular to implement this pattern in a constructor. For example,

    Icecream ic = new RainbowTopUp(new ChocoTopUp(new Vanilla()));

If you look at the diagram in the wikipedia, you would see ConcreteComponent and Decorator inherit from the same superclass/interface, Component. That is, these two classes have the same implement method(s).

However, in the Decorator class, you'd see an arrow back to the Component, which means you use Component somewhere in the Decorator class. In this case, you use the Component as a datatype of a constructor in the Decorator. That is the big trick. Without this trick, you won't be able to plug a new object to an existing object.

After that, you can create subclasses inheriting from the Decorator class. Because all classes have the same root, every single classes can freely plugin without any order.

Danndanna answered 10/1, 2017 at 11:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.