Structural Patternscritical

Decorator Pattern

Attach additional responsibilities to an object dynamically without modifying its class. Decorators provide a flexible alternative to subclassing for extending functionality.

Memory anchor

Decorator = Russian nesting dolls (matryoshka). Each layer wraps the previous one and adds something — paint, glitter, a hat. Unwrap them all and the core doll is unchanged. Stack as many layers as you want.

Expected depth

Decorator wraps an object that implements the same interface, delegating to the wrapped object and adding behaviour before or after. Classic example: Java I/O streams — BufferedReader wraps FileReader, GZIPInputStream wraps FileInputStream. Each wrapper adds one responsibility. This directly implements OCP: you extend behaviour without modifying the original class. Decorator vs Inheritance: inheritance is static (decided at compile time); Decorator is dynamic (composed at runtime and stackable).

Deep — senior internals

Decorator and Inheritance both solve 'add behaviour to a class', but their trade-offs diverge sharply. Inheritance creates a parallel hierarchy that explodes combinatorially: to have a LoggedCachedEncryptedRepository you need 3! = 6 subclasses for all orderings. Decorator composes: new LoggingDecorator(new CachingDecorator(new EncryptingDecorator(repository))). Order matters and is explicit. Downsides of Decorator: (1) many small objects make debugging harder — stack traces show many wrapper frames; (2) identity equality breaks — decorated object is not == original; (3) if the component interface is fat, each decorator must implement all methods, even those it does not decorate (ISP violation risk). Modern use: Spring AOP generates decorator-like proxies at runtime for @Transactional, @Cacheable, @Retryable.

🎤Interview-ready answer

Decorator is my preferred alternative to subclassing when I need stackable, runtime-configurable behaviour additions. I define a DataSource interface; FileDataSource is the concrete component. EncryptionDecorator, CompressionDecorator, and LoggingDecorator all implement DataSource and accept a DataSource in their constructor. I compose them: new LoggingDecorator(new CompressionDecorator(new FileDataSource('data.txt'))). Adding logging does not touch FileDataSource. Removing logging is one line. With subclassing, I'd need LoggedCompressedFileDataSource — the hierarchy explodes. I always discuss the identity and debugging trade-offs.

Common trap

The Decorator and Proxy patterns look identical structurally (both wrap the same interface). The distinction is intent: Decorator adds behaviour; Proxy controls access (lazy init, caching, access control, remote proxy). In interviews, always state the intent before the structure.

Related concepts