Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

README.md

Decorator Pattern

What is the Decorator Pattern?

The Decorator Pattern is a structural design pattern that allows you to add new functionality to objects dynamically without altering their structure. It provides a flexible alternative to subclassing for extending object behavior.

Why is it Used?

  • Dynamic Behavior Addition: Add responsibilities to objects at runtime
  • Avoids Class Explosion: Prevents creating many subclasses for different combinations
  • Single Responsibility: Each decorator handles one specific responsibility
  • Flexibility: Mix and match decorators in any combination
  • Composition over Inheritance: Uses composition instead of inheritance

Problems it Solves

1. Class Explosion from Inheritance

// WITHOUT Decorator Pattern - Creates explosion of subclasses
class Coffee { }
class CoffeeWithMilk extends Coffee { }
class CoffeeWithMilkAndCaramel extends Coffee { }
class CoffeeWithMilkAndCaramelAndWhipped extends Coffee { }
// For n features, you need 2^n classes!

// WITH Decorator Pattern - Combine features dynamically
Coffee coffee = new WhippedCreamDecorator(
    new CaramelDecorator(
        new MilkDecorator(
            new SimpleCoffee()
        )
    )
);

2. Adding Responsibilities at Runtime

Features can be added or removed dynamically without modifying original classes.

3. Mixing and Matching Behaviors

Create different combinations without creating new classes.

SOLID Principles Included

1. Single Responsibility Principle (SRP)

  • Each decorator class has one specific responsibility
  • SimpleCoffee handles coffee
  • MilkDecorator handles milk addition
  • CaramelDecorator handles caramel addition

2. Open/Closed Principle (OCP)

  • Open for extension through new decorators
  • Closed for modification (no changes to existing classes)

3. Dependency Inversion Principle (DIP)

  • Decorators depend on the Coffee interface, not concrete implementations

Example Without Decorator Pattern

// Without Decorator - Multiple inheritance hierarchies
class SimpleCoffee extends Coffee { }
class CoffeeWithMilk extends SimpleCoffee { }
class CoffeeWithCaramel extends SimpleCoffee { }
class CoffeeWithMilkAndCaramel extends CoffeeWithMilk { }
class CoffeeWithMilkAndCaramelAndWhipped extends CoffeeWithMilkAndCaramel { }
// Exponential growth!

Coffee coffee = new CoffeeWithMilkAndCaramelAndWhipped(); // Limited combinations

Example With Decorator Pattern

// With Decorator - Flexible composition
Coffee coffee = new WhippedCreamDecorator(
    new CaramelDecorator(
        new MilkDecorator(
            new SimpleCoffee()
        )
    )
);
// Any combination possible!

Key Characteristics

Wrapping Objects: Decorators wrap the original object ✓ Same Interface: Decorators implement the same interface as the wrapped object ✓ Composition: Uses composition instead of inheritance ✓ Runtime Addition: Features added at runtime ✓ Chain of Responsibility: Multiple decorators can wrap each other

Comparison: Inheritance vs Decoration

Inheritance Decoration
Static, compile-time Dynamic, runtime
Class explosion No explosion
Rigid Flexible
Hard to modify Easy to modify

Use Cases

  • Adding features to I/O streams (compression, encryption)
  • GUI components with various enhancements
  • Coffee shop ordering systems
  • Logging/caching layers for methods
  • Java's InputStream/OutputStream hierarchy

Files in this Package

  • CoffeeDecorator.java - Coffee add-ons decorator
  • FileIODecorator.java - Compression and encryption decorators