Explore Topics
π
Intent
Wrap, don't subclass
π§ͺ Decorator Stack Lab β wrap the base text in any combination, any order
Base text:Presets:
Available decorators Β· click to add a layer on TOP of the stack
π§
Onion Β· outermost layer on top
#1BorderDecorator
#2UpperCaseDecorator
β
BasicText (ConcreteComponent)"Hello World"
π Construction expression
new BorderDecorator(
new UpperCaseDecorator(
new BasicText("Hello World")
)
)Walk the parens inside-out: each `new` adds a layer to the onion.
π€ outermost.render() output2 layers
βββββββββββ HELLO WORLD βββββββββββ
Try the same decorators in two different orders (β² βΌ). The output changes β that's the order-sensitivity of composition. Each layer applies its transformation to whatever the wrapped component produced.
π
Intent
Wrap, don't subclass
Decorator is a **structural** GoF pattern that lets you attach additional responsibilities to a single object **dynamically**. A decorator IS-A Component (so callers cannot tell the difference) AND HAS-A Component (so it can forward the work and add to it). The result is an onion: peel any layer and the inner object is still the same shape β but each layer added a fresh responsibility on the way in and on the way out.
Key Ideas
- Add behavior to a **single instance**, not to a class β other instances of the same class are unaffected.
- A decorator **IS-A** Component (same interface) AND **HAS-A** Component (the wrapped object) β that dual relationship is the whole pattern.
- Composition happens at **runtime**: `new B(new A(plain))` is a totally different beast from `new A(new B(plain))`.
- Often called the **Wrapper** pattern β every Decorator wraps another Component, and that Component may itself be a wrapper.
Strengths
- β¦Extend a single object without touching the Component class or any of its siblings (Open/Closed Principle).
- β¦Combine decorators in any order, any depth β choose the recipe at runtime, not compile time.
- β¦Each decorator stays focused on one responsibility (Single Responsibility Principle).
Weaknesses
- β³Many small decorator classes β sometimes one per feature flag.
- β³Order is significant but invisible at the call site β `Hex(AddedText(p))` β `AddedText(Hex(p))`.
- β³Equality and identity are tricky: `d.equals(d.inner)` is false even though both expose the same interface.
Used in:A **BasicCar** rolls off the line. The buyer chooses to upgrade it to **SportsCar** trim (spoiler, racing seats), then chooses to add the **LuxuryCar** package (leather, audio, sunroof) on top of that. Each upgrade wraps the existing car instance β no new subclass for "Sports+Luxury" needs to exist.
Code Snippets
Quiz
Intent Quiz
QUIZ 1/6Which statement best captures the **intent** of the Decorator pattern?
Diagrams
Practice
Drag each class, method, or expression into the Decorator role it fills. Two items are traps β they belong to a DIFFERENT GoF pattern and should be sorted into the trap bucket.
EXERCISE 1/4interface PrintText { void print(String s); }
abstract class JComponent (the contract every Swing widget speaks)
class FileInputStream extends InputStream (raw bytes from disk)
class PlainPrintText implements PrintText (no-frills core)
abstract class PrintTextDecorator implements PrintText { protected final PrintText inner; }
class FilterInputStream extends InputStream { protected InputStream in; }
class BufferedReader extends Reader (adds line-by-line buffering)
class HexDecorator extends PrintTextDecorator { ... appends hex view }
class JScrollPane (adds scrolling to any JComponent it wraps)
class LoggerSingleton { private static LoggerSingleton INSTANCE; }
static Logger getLogger() { return LoggerSingleton.INSTANCE; }
Component (the shared contract)
ConcreteComponent (the no-frills core)
Decorator (abstract base β IS-A and HAS-A)
ConcreteDecorator (adds responsibility)
NOT part of Decorator (belongs to a different pattern)
Order the construction lines to build the onion from inside-out: HEX wraps AddedText wraps Plain.
EXERCISE 2/4Drag the lines into the correct order.
// β produces: [TAG] X -> HEX: 0x5b 0x54 0x41 ... (TAG prepended, then HEX appended)
PrintText core = new PlainPrintText();
PrintText outer = new HexDecorator(middle);
PrintText middle = new AddedTextDecorator(core);
outer.print("X");
8 common myths about the Decorator pattern β true or false?
EXERCISE 3/4A Decorator can only ADD behavior β it can never remove or subtract from the wrapped component's behavior.
The order in which decorators are stacked does not change the final result.
The Decorator pattern uses inheritance somewhere in its structure.
Decorator and Proxy have the same skeleton β they differ only in INTENT.
Forgetting to call `super.method()` in a ConcreteDecorator triggers a compile error.
`BufferedReader bw = new BufferedReader(new FileReader("x.txt"))` is an example of the Decorator pattern in the JDK.
A Decorator must add behavior on BOTH sides of the `super.method()` call.
Modeling N independent feature flags via inheritance requires N subclasses.
The decorator chain is silently swallowing the inner work β find the bug
EXERCISE 4/4Click on the line you think contains the bug, then press Confirm.