Use Cases and Examples of Gof Decorator Pattern for Io

Use Cases and Examples of GoF Decorator Pattern for IO

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:

  • Examples of GoF Design Patterns in Java's core libraries

Understand the Decorator Pattern with a real world example

Decorator pattern achieves a single objective of dynamically adding
responsibilities to any object.

Consider a case of a pizza shop. In the pizza shop they will sell few pizza varieties and they will also provide toppings in the menu. Now imagine a situation wherein if the pizza shop has to provide prices for each combination of pizza and topping. Even if there are four basic pizzas and 8 different toppings, the application would go crazy maintaining all these concrete combination of pizzas and toppings.

Here comes the decorator pattern.

As per the decorator pattern, you will implement toppings as decorators and pizzas will be decorated by those toppings' decorators. Practically each customer would want toppings of his desire and final bill-amount will be composed of the base pizzas and additionally ordered toppings. Each topping decorator would know about the pizzas that it is decorating and it's price. GetPrice() method of Topping object would return cumulative price of both pizza and the topping.

EDIT

Here's a code-example of explanation above.

public abstract class BasePizza
{
protected double myPrice;

public virtual double GetPrice()
{
return this.myPrice;
}
}

public abstract class ToppingsDecorator : BasePizza
{
protected BasePizza pizza;
public ToppingsDecorator(BasePizza pizzaToDecorate)
{
this.pizza = pizzaToDecorate;
}

public override double GetPrice()
{
return (this.pizza.GetPrice() + this.myPrice);
}
}

class Program
{
[STAThread]
static void Main()
{
//Client-code
Margherita pizza = new Margherita();
Console.WriteLine("Plain Margherita: " + pizza.GetPrice().ToString());

ExtraCheeseTopping moreCheese = new ExtraCheeseTopping(pizza);
ExtraCheeseTopping someMoreCheese = new ExtraCheeseTopping(moreCheese);
Console.WriteLine("Plain Margherita with double extra cheese: " + someMoreCheese.GetPrice().ToString());

MushroomTopping moreMushroom = new MushroomTopping(someMoreCheese);
Console.WriteLine("Plain Margherita with double extra cheese with mushroom: " + moreMushroom.GetPrice().ToString());

JalapenoTopping moreJalapeno = new JalapenoTopping(moreMushroom);
Console.WriteLine("Plain Margherita with double extra cheese with mushroom with Jalapeno: " + moreJalapeno.GetPrice().ToString());

Console.ReadLine();
}
}

public class Margherita : BasePizza
{
public Margherita()
{
this.myPrice = 6.99;
}
}

public class Gourmet : BasePizza
{
public Gourmet()
{
this.myPrice = 7.49;
}
}

public class ExtraCheeseTopping : ToppingsDecorator
{
public ExtraCheeseTopping(BasePizza pizzaToDecorate)
: base(pizzaToDecorate)
{
this.myPrice = 0.99;
}
}

public class MushroomTopping : ToppingsDecorator
{
public MushroomTopping(BasePizza pizzaToDecorate)
: base(pizzaToDecorate)
{
this.myPrice = 1.49;
}
}

public class JalapenoTopping : ToppingsDecorator
{
public JalapenoTopping(BasePizza pizzaToDecorate)
: base(pizzaToDecorate)
{
this.myPrice = 1.49;
}
}

Decorator pattern java.io.reader

The diagram is correct. If you have access to Intellij (or other capable tool) you can see the UML class diagram for Java types. The below snippet is one such diagram.

Sample Image

As you can see, it agrees with yours. One thing I would consider though is BufferedReader a concrete decorator since it provides additional behavior on Reader.

Use Cases and Examples of GoF Decorator Pattern for IO

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:

  • Examples of GoF Design Patterns in Java's core libraries

When to Use the Decorator Pattern?

The decorator pattern is used a lot with streams: you can wrap a stream with a stream to get added functionality. I've seen this with the .Net framework - as far as I know this occurs elsewhere. My favourite is using GZipStream around a FileStream, for added compression.

When do we need decorator pattern?

The Streams in Java - subclasses of InputStream and OutputStream are perfect examples of the decorator pattern.

As an example, writing a file to disk:

File toWriteTo = new File("C:\\temp\\tempFile.txt");
OutputStream outputStream = new FileOutputStream(toWriteTo);

outputStream.write("Sample text".getBytes());

Then should you require some extra functionality regarding the writing to disk:

File toWriteTo = new File("C:\\temp\\tempFile.txt");
OutputStream outputStream =
new GZIPOutputStream(new FileOutputStream(toWriteTo));

outputStream.write("Sample text".getBytes());

By simply "chaining" the constructors, you can create quite powerful ways of writing to disk. The beauty in this way is that you can add different (in this example) OutputStream implementations later on. Also, each implementation doesn't know how the others work - they all just work to the same contract. This also makes testing each implementation very easy in isolation.


There are plenty of "real world" examples of where the decorator pattern can be used. Off the top of my head, some examples:

  • Reading and writing to disk (above)
  • Construction of UI elements, such as adding scrollbars on to text areas etc

Head First Design Patterns has some more "real world" examples. It seems that O'Reilly has their sample chapter, which is on Decorator Pattern, for free; Google showed up this link: PDF

Difference between the Composite Pattern and Decorator Pattern?

They usually go hand in and hand. In that using the composite pattern often leads to also using the decorator pattern.

The composite pattern allows you to build a hierarchical structure (such as a tree of elements) in a way that allows your external code to view the entire structure as a single entity. So the interface to a leaf entity is exactly the same as the entity for a compound entity. So the essence is that all elements in your composite structure have the same interface even though some are leaf nodes and others are entire structures. User interfaces often use this approach to allow easy composability.

http://en.wikipedia.org/wiki/Composite_pattern

The decorator pattern allows an entity to completely contain another entity so that using the decorator looks identical to the contained entity. This allows the decorator to modify the behaviour and/or content of whatever it is encapsulating without changing the outward appearance of the entity. For example, you might use a decorator to add logging output on the usage of the contained element without changing any behaviour of the contained element.

http://en.wikipedia.org/wiki/Decorator_pattern



Related Topics



Leave a reply



Submit