Definition
Concept
Decorator pattern: structural design pattern. Purpose: attach additional responsibilities to objects dynamically. Alternative to subclassing. Enables flexible and reusable enhancements.
Scope
Applies to runtime behavior extension. Preserves original object interface. Supports recursive composition of decorators. Key in UI frameworks, I/O streams, logging systems.
Classification
Category: Structural pattern. Relation: complements Composite pattern. Often used in conjunction with Factory and Strategy patterns.
Motivation
Problem Statement
Subclassing: rigid, combinatorial explosion of subclasses. Static behavior extension. Maintenance overhead increases exponentially.
Solution Insight
Use composition over inheritance. Wrap objects with decorators providing extra features. Enable dynamic, transparent behavior addition at runtime.
Resulting Benefits
Reduced subclass proliferation. Increased flexibility. Runtime configuration possible. Adheres to Open/Closed Principle.
Structure
Class Diagram
Component interface: defines operations. ConcreteComponent: base object. Decorator: maintains reference to Component, implements Component interface. ConcreteDecorator: adds responsibilities.
Object Diagram
Objects layered: ConcreteComponent wrapped by one or more ConcreteDecorators. Each decorator forwards requests, optionally adds behavior.
UML Notation
Interface and abstract class usage. Aggregation links from Decorator to Component. Recursive composition evident.
| Element | Role |
|---|---|
| Component | Defines interface for objects |
| ConcreteComponent | Implements Component interface |
| Decorator | Holds reference to Component, delegates calls |
| ConcreteDecorator | Adds responsibilities dynamically |
Participants
Component
Abstract interface or class. Declares operations. Ensures consistent API for components and decorators.
ConcreteComponent
Defines core functionality. Object to be decorated. Implements Component interface.
Decorator
Abstract class implementing Component. Contains reference to Component. Delegates operations, enabling extension.
ConcreteDecorator
Concrete subclasses adding specific responsibilities. Overrides operations to enhance behavior before/after delegation.
Mechanism
Dynamic Wrapping
Decorator holds component reference. Methods override and extend behavior. Calls forwarded to wrapped object.
Recursive Composition
Decorators can wrap other decorators. Stackable behavior. Enables layering enhancements.
Interface Preservation
Decorators maintain component interface. Transparent to clients. Supports polymorphism.
interface Component { operation(): void;}class ConcreteComponent implements Component { operation() { // core behavior }}class Decorator implements Component { protected component: Component; constructor(component: Component) { this.component = component; } operation() { this.component.operation(); }}class ConcreteDecorator extends Decorator { operation() { // additional behavior before super.operation(); // additional behavior after }}Advantages
Flexibility
Behavior added/removed at runtime. No static inheritance limitations.
Single Responsibility
Divide concerns into separate decorators. Simplifies maintenance.
Open/Closed Principle
Classes open for extension, closed for modification. Enhancements via decorators.
Composability
Multiple decorators combined in arbitrary order. Reusable components.
Disadvantages
Complexity
Increased number of small classes. Difficult to understand decorator chains.
Debugging Difficulty
Tracing wrapped calls complicated. Stack traces harder to interpret.
Overhead
Runtime wrapping incurs slight performance cost. Increased memory usage.
Identity Issues
Equality checks and type identification complicated by wrappers.
Implementation
Steps
- Define Component interface.
- Create ConcreteComponent with core behavior.
- Implement abstract Decorator holding Component reference.
- Derive ConcreteDecorators adding behaviors.
- Compose objects by wrapping ConcreteComponent.
Language Considerations
Supports all OOP languages. Dynamic languages simplify implementation. Static languages require interfaces or abstract classes.
Performance Optimization
Minimize decorator layers. Cache results when possible. Avoid excessive wrapping.
| Step | Description |
|---|---|
| Define Interface | Common API for components and decorators |
| ConcreteComponent | Base object with original behavior |
| Abstract Decorator | Holds reference, delegates calls |
| ConcreteDecorator | Adds new behavior dynamically |
| Composition | Wrap instances to extend functionality |
Use Cases
User Interface Components
Adding borders, scrollbars, or shadows to widgets dynamically. Example: Swing decorators.
Input/Output Streams
Java I/O uses decorators for buffering, encryption, compression.
Logging and Auditing
Wrap core services to add logging, validation, or metrics collection.
Security Enhancements
Dynamic authorization checks by decorating service calls.
Comparison with Other Patterns
Decorator vs Proxy
Decorator adds responsibilities, Proxy controls access. Both wrap objects but intent differs.
Decorator vs Adapter
Adapter changes interface, Decorator preserves interface and adds behavior.
Decorator vs Composite
Composite composes objects into tree structures; Decorator adds behavior transparently.
Examples
Java I/O Streams
InputStream, BufferedInputStream, DataInputStream. Wrapping streams to add functionality.
Graphical User Interface
Swing components wrapped by decorators adding borders or effects.
Custom Logging Decorator
class LoggerDecorator extends Decorator { operation() { console.log("Before operation"); super.operation(); console.log("After operation"); }}Best Practices
Keep Decorators Lightweight
Minimize code in decorators. Delegate most behavior. Enhance only necessary parts.
Use Clear Naming Conventions
Name decorators by the responsibility they add. Improves readability and maintainability.
Limit Decorator Layers
Excessive wrapping complicates debugging. Balance flexibility and complexity.
Document Composition Order
Order of wrapping affects behavior. Explicitly document decorator stacking.
References
- Gamma, E., Helm, R., Johnson, R., Vlissides, J. Design Patterns: Elements of Reusable Object-Oriented Software, Addison-Wesley, 1994, pp. 175-196.
- Buschmann, F., Meunier, R., Rohnert, H., Sommerlad, P., Stal, M. Pattern-Oriented Software Architecture, Volume 1: A System of Patterns, Wiley, 1996, pp. 239-272.
- Freeman, E., Freeman, E., Sierra, K., Bates, B. Head First Design Patterns, O'Reilly Media, 2004, pp. 203-245.
- Shalloway, A., Trott, J.R. Design Patterns Explained: A New Perspective on Object-Oriented Design, Addison-Wesley, 2004, pp. 107-130.
- Metz, C. Practical UML Statecharts in C/C++, Elsevier, 2001, pp. 85-98.