The Decorator Pattern
also known as: Wrapper in German: Dekorierer
The Problem
A bar serves several drinks:
Coffee
Espresso
…
Every drink can be modified using:
Whip
Milk
…
So there are a lot of possible combinations:
Coffe, Coffee with Milk, Coffe with Whip, Espresso with Milk and Whip, …
Subclass everything
class Coffee extends Drink { double cost() {
return 1.0;
}
public String getName() { return "Coffee";
} }
class CoffeeWithMilk extends Drink { double cost() {
return 1.2;
}
public String getName() { return "Coffee with milk";
} }
Subclass everything
Subclass everything
class Coffee extends Drink { double cost() {
return 1.0;
}
public String getName() { return "Coffee";
} }
class CoffeeWithMilk extends Coffee { double cost() {
return super.cost() + 0.2;
}
public String getName() {
return super.getName() +
„with milk“;
} }
The first Decoration
class Coffee extends Drink { double cost() {
return 1.0;
}
public String getName() { return "Coffee";
} }
class MilkDecorator extends Drink { Drink drink;
public DrinkDecorator(Drink drink) { this.drink = drink;
}
double cost() {
return drink.cost() + 0.2;
}
public String getName() {
return drink.getName() + „ with milk“;
} }
The Decorator Pattern
Drink
Coffee DrinkDecorator
MilkDecorator WhipDecorator
The Decorator Pattern
class Coffee extends Drink {
public double cost() { return 1.0; } public String getName() {
return "Coffee";
} }
class MilkDecorator extends DrinkDecorator { public MilkDecorator(Drink drink) {
super(drink);
}
public double cost() { return drink.cost() + 0.2; } public String getName() {
return drink.getName() + „ with milk“;
} }
abstract class Drink {
public abstract String getName();
public abstract double cost();
}
abstract class DrinkDecorator extends Drink { Drink drink;
public DrinkDecorator (Drink drink) { this.drink = drink;
} }
The Decorator Pattern
Drink drink = new MilkDecorator(new WhipDecorator(new Coffee()));
System.out.println( drink.cost() ); // Output: 1.6
Client MilkDecorator WhipDecorator Coffee
cost()
cost()
cost()
1.0 1.4
1.6
The Decorator Pattern
public class Bar {
public static void main(String[] args) {
Drink basicDrinks[] = {newEspresso(), newCoffee()};
for (Drink basicDrink:basicDrinks) { Drink drink1 = basicDrink;
System.out.println(drink1.getName() + " costs " + drink1.cost());
Drink drink2 = newMilkDecorator(basicDrink);
System.out.println(drink2.getName() + " costs " + drink2.cost());
Drink drink3 = newWhipDecorator(basicDrink);
System.out.println(drink3.getName() + " costs " + drink3.cost());
Drink drink4 = newWhipDecorator(newMilkDecorator(basicDrink));
System.out.println(drink4.getName() + " costs " + drink4.cost());
} } }
Espresso costs 1.5
Espresso with milk costs 1.7 Espresso with whip costs 1.9
Espresso with milk with whip costs 2.1 Coffee costs 1.0
Coffee with milk costs 1.2 Coffee with whip costs 1.4
Coffee with milk with whip costs 1.6
The Decorator Pattern
Drink
Coffee DrinkDecorator
MilkDecorator WhipDecorator +operation()
Component
+operation()
ConcreteComponent
+operation() Decorator
+operation()
ConcreteDecoratorA
+operation()
ConcreteDecoratorB
super.operation();
// something else component.operation();
GUI Decorators
VisualComponent
TextView Decorator
ScrollDecorator BorderDecorator Component
ConcreteComponent Decorator
ConcreteDecoratorA ConcreteDecoratorB
GUI Decorators
Use Decorator
More flexibility than static inheritance.
Avoids feature-laden classes high up in the hierarchy.
A decorator and ist component aren‘t identical.
Lots of little objects.
Java Streams
InputStream
FileInputStream FilterInputStream
InflaterInputStream BufferedInputStream ObjectInputStream
Java Streams
Read data from file:
InputStream in = new FileInputStream(“file.gz”);
in.read(bytes);
Read data from file using a buffer:
InputStream in = new BufferedInputStream(
new FileInputStream(“file.gz”));
in.read(bytes);
Read gzip compressed data from file:
InputStream in = new GZIPInputStream(
new FileInputStream(“file.gz”));
in.read(bytes);
Read gzip compressed data from socket:
InputStream in = new GZIPInputStream(
socket.getInputStream());
in.read(bytes);
Related Patterns
Adapter: A decorator is different from an adapter in that a decorator only changes an object‘s
responsibilities, not ist interface; an adapter will give an object a completely new interface.
Composite: A decorator can be viewed as a
degenerate composite with only one component.
However, a decorator adds additional
responsibilities — it isn‘t intended for object aggregation.
Strategy: A decorator lets you change the skin of an object; a strategy lets you change the guts. These are two alternative ways of changing an object.