Introduction
Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.
The Decorator Pattern is a structural design pattern that allows you to dynamically add behavior or responsibilities to an object without altering its structure. It is an alternative to subclassing and is often used to adhere to the Open/Closed Principle, which states that classes should be open for extension but closed for modification.
The Decorator Pattern emphasizes that a class should remain closed to modifications but open to extensions. This means new functionality can be introduced without altering the existing code. This approach is particularly helpful when we need to add specific functionalities to individual objects rather than the entire class. The pattern leverages object composition over inheritance, allowing for greater flexibility. By mastering this technique, developers can introduce new responsibilities to an object without impacting the underlying class structure.
Key Concepts
- Component: An interface or abstract class that defines the standard behavior.
- Concrete Component: A class that implements the Component interface and provides core functionality.
- Decorator: An abstract class or interface that implements the Component interface and contains a reference to a Component object.
- Concrete Decorator: A class that extends the Decorator and adds additional behavior to the Component.
Implementation in Java
Below is a step-by-step tutorial on implementing the Decorator Pattern in Java.
Step 1: Define the Component Interface
public interface Component { void operation(); }
Step 2: Create a Concrete Component
public class ConcreteComponent implements Component { @Override public void operation() { System.out.println("Performing core operation."); } }
Step 3: Create an Abstract Decorator
public abstract class Decorator implements Component { protected Component component; public Decorator(Component component) { this.component = component; } @Override public void operation() { component.operation(); } }
Step 4: Create Concrete Decorators
public class ConcreteDecoratorA extends Decorator { public ConcreteDecoratorA(Component component) { super(component); } @Override public void operation() { super.operation(); addBehaviorA(); } private void addBehaviorA() { System.out.println("Adding behavior A."); } } public class ConcreteDecoratorB extends Decorator { public ConcreteDecoratorB(Component component) { super(component); } @Override public void operation() { super.operation(); addBehaviorB(); } private void addBehaviorB() { System.out.println("Adding behavior B."); } }
Step 5: Test the Pattern
public class DecoratorPatternTest { public static void main(String[] args) { Component component = new ConcreteComponent(); System.out.println("Original Component:"); component.operation(); Component decoratorA = new ConcreteDecoratorA(component); System.out.println("\nComponent with Decorator A:"); decoratorA.operation(); Component decoratorB = new ConcreteDecoratorB(decoratorA); System.out.println("\nComponent with Decorator A and B:"); decoratorB.operation(); } }
Output
Original Component: Performing core operation. Component with Decorator A: Performing core operation. Adding behavior A. Component with Decorator A and B: Performing core operation. Adding behavior A. Adding behavior B.
Advantages
- Provides flexibility to add responsibilities to individual objects dynamically.
- Promotes code reusability by allowing composition instead of inheritance.
- Adheres to the Open/Closed Principle.
Disadvantages
- Can introduce complexity due to multiple small objects.
- Debugging can be challenging because of the layers of decorators.
Real-World Examples
Java I/O Streams: The java.io package heavily uses the Decorator Pattern. For example:
InputStream input = new FileInputStream("file.txt"); InputStream buffer = new BufferedInputStream(input); InputStream data = new DataInputStream(buffer);
Here, BufferedInputStream and DataInputStream are decorators.
GUI Frameworks: Adding scrollbars, borders, or other enhancements to UI components dynamically.