creational
Decorator
Attach additional responsibilities to an object dynamically. A flexible alternative to subclassing.
You want to add optional behaviour to an object. Compression. Encryption. Buffering.
Subclasses can do this, but only if you know the combinations in advance. Three optional features means eight subclasses to cover every mix. Four means sixteen. It gets out of hand quickly.
Decorator adds behaviour by wrapping instead. Each wrapper adds one feature and passes the rest through. You stack them at runtime, in whatever order you need.
A plain coffee is your base. Add milk (Decorator 1), add sugar (Decorator 2). Each wraps the previous.
Key Concepts
1
The decorator implements the same interface as the object it wraps. That is what makes stacking possible: a wrapped object still looks like the original.
2
It holds a reference to the inner object. Each method does its own bit of work, then calls the same method on the inner object.
3
Because every layer shares one interface, code using the object cannot tell how many wrappers are present. new Buffered(new Encrypted(new FileStream())) is still just a stream.
new Buffered(new Encrypted(new FileStream()))
4
Java's InputStream classes are the classic example, and worth naming in an interview.
InputStream
When to use it
- Adding logging, encryption, compression, or caching
- Subclassing would produce too many combinations
- Add/remove behaviors at runtime
Watch out for
- Decorators can be stacked — order matters (encryption before compression ≠ compression before encryption).
java
public interface DataSource {
void writeData(String data);
String readData();
}
public class FileDataSource implements DataSource { /* file I/O */ }
public abstract class DataSourceDecorator implements DataSource {
protected final DataSource wrappee;
public DataSourceDecorator(DataSource s) { this.wrappee = s; }
public void writeData(String d) { wrappee.writeData(d); }
public String readData() { return wrappee.readData(); }
}
public class EncryptionDecorator extends DataSourceDecorator {
public void writeData(String d) { super.writeData(encrypt(d)); }
public String readData() { return decrypt(super.readData()); }
}