What is the Decorator Design Pattern?

Introduction

In software engineering, the Decorator design pattern is a structural pattern that allows objects to be extended with new behaviours at runtime, without affecting the behaviour of other objects in the same class. It is one of the Gang of Four design patterns and is used to add or remove behaviours from an object dynamically. In this blog post, we will discuss the Decorator design pattern in detail, along with some examples.

The Decorator pattern is a structural pattern that allows you to add new behaviours to an existing object by wrapping it in a decorator object. The decorator object provides an alternative interface to the original object and allows you to modify the behaviour of the original object dynamically. The decorator pattern follows the Open-Closed Principle, which states that a class should be open for extension but closed for modification.

How does it work?

The Decorator pattern works by creating a decorator class that wraps an existing object. The decorator class implements the same interface as the original object and delegates all method calls to the original object. It then adds or modifies behaviour by adding new methods or properties to the decorator class. The decorator class can also call methods on the original object and modify its state.

Example

Let's consider an example of a coffee shop that sells different types of coffee, such as espresso, latte, and cappuccino. Each coffee has a base price, but customers can add extra ingredients such as sugar, cream, or chocolate syrup, which increase the price of the coffee. We can use the Decorator pattern to add the extra ingredients to the coffee dynamically.

First, we create an interface for the Coffee class:

public interface Coffee {
   public double getCost(); // returns the cost of the coffee
   public String getIngredients(); // returns the ingredients of the coffee
}

Next, we create a concrete implementation of the Coffee interface, called SimpleCoffee:

public class SimpleCoffee implements Coffee {
   public double getCost() {
      return 1.00;
   }

   public String getIngredients() {
      return "Coffee";
   }
}

The SimpleCoffee class represents a basic cup of coffee that costs 1 dollar and contains only coffee.

Now, we can use the Decorator pattern to add extra ingredients to the coffee. We create a decorator class for each extra ingredient:

public abstract class CoffeeDecorator implements Coffee {
   protected final Coffee decoratedCoffee;

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

   public double getCost() { // implementing methods of the interface
      return decoratedCoffee.getCost();
   }

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

public class Milk extends CoffeeDecorator {
   public Milk(Coffee decoratedCoffee) {
      super(decoratedCoffee);
   }

   public double getCost() { // adding extra functionality
      return super.getCost() + 0.5;
   }

   public String getIngredients() { // adding extra functionality
      return super.getIngredients() + ", Milk";
   }
}

public class Sugar extends CoffeeDecorator {
   public Sugar(Coffee decoratedCoffee) {
      super(decoratedCoffee);
   }

   public double getCost() { // adding extra functionality
      return super.getCost() + 0.2;
   }

   public String getIngredients() { // adding extra functionality
      return super.getIngredients() + ", Sugar";
   }
}

The Milk and Sugar classes are decorator classes that add milk and sugar to the coffee, respectively. They implement the Coffee interface and delegate all method calls to the decorated coffee object. They also add the extra functionality of adding milk or sugar to the coffee by modifying the getCost() and getIngredients() methods

Did you find this article valuable?

Support Harsh Mange by becoming a sponsor. Any amount is appreciated!