How to Implement Decorator Design Pattern in Java

The Decorator Design Pattern is a structural design pattern that allows behavior to be added to individual objects, either statically or dynamically, without affecting the behavior of other objects from the same class. This pattern provides a flexible alternative to subclassing for extending functionality.

Example: Decorator Design Pattern in Java

In this example, we will implement a basic coffee ordering system where different types of add-ons (like milk or sugar) can be “decorated” onto a basic coffee.

Step 1: Define the Coffee Interface

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

Step 2: Create a Concrete Component (Base Coffee)

// SimpleCoffee.java
public class SimpleCoffee implements Coffee {
    @Override
    public String getDescription() {
        return "Simple Coffee";
    }

    @Override
    public double cost() {
        return 2.00; // Base price for a simple coffee
    }
}

Step 3: Create the Abstract Decorator Class

The abstract decorator class will implement the Coffee interface and hold a reference to a Coffee object.

// CoffeeDecorator.java
public abstract class CoffeeDecorator implements Coffee {
    protected Coffee decoratedCoffee;

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

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

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

Step 4: Create Concrete Decorators

Now, let’s create some concrete decorators (Milk and Sugar) that will add functionality (extra cost and description) to the base coffee.

// MilkDecorator.java
public class MilkDecorator extends CoffeeDecorator {
    public MilkDecorator(Coffee coffee) {
        super(coffee);
    }

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

    @Override
    public double cost() {
        return decoratedCoffee.cost() + 0.50; // Add cost for milk
    }
}
// SugarDecorator.java
public class SugarDecorator extends CoffeeDecorator {
    public SugarDecorator(Coffee coffee) {
        super(coffee);
    }

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

    @Override
    public double cost() {
        return decoratedCoffee.cost() + 0.20; // Add cost for sugar
    }
}

Step 5: Using the Decorator Pattern in the Main Class

// Main.java
public class Main {
    public static void main(String[] args) {
        // Order a simple coffee
        Coffee myCoffee = new SimpleCoffee();
        System.out.println(myCoffee.getDescription() + " $" + myCoffee.cost());

        // Add milk to the coffee
        myCoffee = new MilkDecorator(myCoffee);
        System.out.println(myCoffee.getDescription() + " $" + myCoffee.cost());

        // Add sugar to the coffee
        myCoffee = new SugarDecorator(myCoffee);
        System.out.println(myCoffee.getDescription() + " $" + myCoffee.cost());

        // Add another milk (to show multiple decorations)
        myCoffee = new MilkDecorator(myCoffee);
        System.out.println(myCoffee.getDescription() + " $" + myCoffee.cost());
    }
}

Explanation of the Code

  1. Coffee Interface:
  • The Coffee interface defines the methods getDescription() and cost(), which will be implemented by both the base component and decorators.
  1. SimpleCoffee (Concrete Component):
  • The SimpleCoffee class implements the Coffee interface, providing a base description and cost for plain coffee.
  1. CoffeeDecorator (Abstract Decorator):
  • This abstract class implements the Coffee interface and contains a reference to a Coffee object. It forwards the calls to the wrapped coffee object, allowing the decorators to build on the base functionality.
  1. Concrete Decorators (MilkDecorator and SugarDecorator):
  • These classes extend CoffeeDecorator and modify the getDescription() and cost() methods to add extra description and cost for milk or sugar.
  1. Main Class:
  • In the main() method, we demonstrate how a simple coffee can be “decorated” with milk and sugar by wrapping the base SimpleCoffee object with the decorators.

Output

When you run the Main class, the output will be:

Simple Coffee $2.0
Simple Coffee, Milk $2.5
Simple Coffee, Milk, Sugar $2.7
Simple Coffee, Milk, Sugar, Milk $3.2

Key Points

  • The Decorator Pattern allows you to add behavior to individual objects without affecting the behavior of other objects of the same class.
  • It is a flexible alternative to subclassing because it provides a way to dynamically add or remove functionality at runtime.
  • You can apply multiple decorators to a single object, as demonstrated by adding multiple instances of MilkDecorator and SugarDecorator.

When to Use the Decorator Pattern

  • When you need to add responsibilities to individual objects dynamically and transparently.
  • When you need to avoid subclassing to extend functionality.
  • When you want to combine several behaviors, and these behaviors can be mixed in different combinations.

Conclusion

The Decorator Design Pattern is a powerful and flexible pattern for extending the functionality of an object. By wrapping an object with decorators, we can add behavior dynamically, avoiding the need for an extensive inheritance hierarchy. This promotes composition over inheritance, a key principle in object-oriented design.

            +-------------------+
            |    <<Interface>>   | 
            |     Component      |
            +-------------------+
            | +getDescription()  |
            | +cost()            |
            +-------------------+
                     ▲
                     |
                     |
            +-------------------+     
            |  ConcreteComponent |      
            +-------------------+      
            | -description:      |
            | +getDescription()  |
            | +cost()            |
            +-------------------+
                     ▲
                     |
          +----------+----------+
          |                     |
  +-------------------+   +------------------+
  |     Decorator      |   |  ConcreteDecorator|
  +-------------------+   +------------------+
  | -decoratedObject   |   | -additionalCost  |
  | +getDescription()  |   | +getDescription()|
  | +cost()            |   | +cost()          |
  +-------------------+   +------------------+

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *