Java Language – 85 – Decorator

Design Patterns – Decorator
Introduction to the Decorator Pattern

The Decorator pattern is a structural design pattern used in Java to dynamically add responsibilities to objects. It allows behavior to be added to individual objects, either statically or dynamically, without affecting the behavior of other objects from the same class.

Why Use the Decorator Pattern?

The Decorator pattern provides several advantages, making it a valuable design pattern:

  • Open for Extension, Closed for Modification: It allows for adding new behaviors to objects without altering their code, adhering to the open/closed principle.
  • Single Responsibility Principle: It ensures that each class has a single responsibility, which improves maintainability and flexibility.
  • Composition over Inheritance: It promotes composition (combining objects) over inheritance (extending classes) to create flexible and reusable designs.
Implementing the Decorator Pattern

The Decorator pattern can be implemented in Java by creating a set of decorator classes that are used to wrap concrete components. Let’s look at an example:


// Component interface
public interface Coffee {
    String getDescription();
    double cost();
}

// Concrete component
public class SimpleCoffee implements Coffee {
    public String getDescription() {
        return "Simple Coffee";
    }

    public double cost() {
        return 2.0;
    }
}

// Decorator class
abstract class CoffeeDecorator implements Coffee {
    protected final Coffee decoratedCoffee;

    public CoffeeDecorator(Coffee coffee) {
        this.decoratedCoffee = coffee;
    }

    public String getDescription() {
        return decoratedCoffee.getDescription();
    }

    public double cost() {
        return decoratedCoffee.cost();
    }
}

// Concrete decorators
public class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }

    public String getDescription() {
        return decoratedCoffee.getDescription() + ", Milk";
    }

    public double cost() {
        return decoratedCoffee.cost() + 0.5;
    }
}

public class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee coffee) {
        super(coffee);
    }

    public String getDescription() {
        return decoratedCoffee.getDescription() + ", Sugar";
    }

    public double cost() {
        return decoratedCoffee.cost() + 0.2;
    }
}

In the example above, we have a Coffee interface with a concrete component class SimpleCoffee. The CoffeeDecorator is an abstract decorator class that implements the Coffee interface and contains a reference to the decorated coffee object. The concrete decorators, MilkDecorator and SugarDecorator, add milk and sugar to the coffee, respectively.

Usage of the Decorator Pattern

The Decorator pattern is often used in Java to add functionalities to classes in a flexible and reusable way. It is particularly useful when you need to extend the behavior of classes without modifying their source code.

Comparison with Inheritance

The Decorator pattern differs from traditional inheritance, where you might create subclasses to add new behaviors. In the Decorator pattern, you can combine and reuse decorators to achieve various combinations of behaviors, offering more flexibility.

Conclusion

The Decorator pattern is a powerful design pattern in Java for enhancing the responsibilities of objects dynamically. It follows principles like open/closed and single responsibility, leading to more maintainable and flexible code. By using the Decorator pattern, you can compose objects to create complex behaviors without altering their existing code, resulting in a clean and extensible design.