ad
ad
Topview AI logo

4. Decorator Design Pattern Explanation with Java Coding, LLD System Design, System design interview

Education


Introduction

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. It is an essential pattern that helps to adhere to the Open/Closed principle and is widely used in various real-world applications, such as UI Components, Coffee Machines, Pizza Shops, and more.

Understanding the Decorator Pattern

In the Decorator Pattern, we start with a base object that has some features or properties. When we want to add additional features, we wrap the original object with a decorator. Each decorator can add more functionality or attributes to the base object. This allows for a combination of various features without modifying the base object itself.

Real-World Example: Pizza Shop

Consider a pizza shop where you have a base pizza. The toppings are additional features that can be added through decorators. For example:

  • Base Pizza
    • Extra Cheese (Decorator 1)
    • Mushrooms (Decorator 2)
    • Extra Veggies (Decorator 3)

You can keep decorating the base pizza with various toppings. Each new decorator can also be decorated further, leading to a combination where every decorator is an object of the same type. This example illustrates how decorators help in composing functionalities.

Why Use the Decorator Pattern?

The Decorator Pattern is essential to manage class explosion in projects where various combinations of features are needed. Instead of creating numerous subclasses for each combination of features (which can lead to complex class hierarchies), we can use decorators to add features at runtime without complicating the underlying class structure. This reduces the number of classes you need to manage.

Implementing the Decorator Pattern in Java

Here’s how you can implement the Decorator Pattern using Java.

  1. Base Component: Start with a base interface or abstract class to define the common functionalities.
  2. Concrete Component: Create concrete classes that implement the base component.
  3. Decorator Class: Create an abstract decorator class that implements the same base interface and holds a reference to a base component.
  4. Concrete Decorators: Implement concrete decorators that extend the functionality of the base component.

Java Code Example

// Component
abstract class Pizza (
    public abstract String getDescription();
    public abstract double cost();
)

// Concrete Component
class BasePizza extends Pizza (
    public String getDescription() {
        return "Base Pizza";
    )

    public double cost() (
        return 200; // Base cost
    )
}

// Decorator
abstract class ToppingDecorator extends Pizza (
    protected Pizza pizza;

    public ToppingDecorator(Pizza pizza) {
        this.pizza = pizza;
    )

    public abstract String getDescription();
}

// Concrete Decorator for Extra Cheese
class ExtraCheese extends ToppingDecorator (
    public ExtraCheese(Pizza pizza) {
        super(pizza);
    )

    public String getDescription() (
        return pizza.getDescription() + ", Extra Cheese";
    )

    public double cost() (
        return pizza.cost() + 50; // Additional cost for cheese
    )
}

// Concrete Decorator for Mushrooms
class Mushrooms extends ToppingDecorator (
    public Mushrooms(Pizza pizza) {
        super(pizza);
    )

    public String getDescription() (
        return pizza.getDescription() + ", Mushrooms";
    )

    public double cost() (
        return pizza.cost() + 30; // Additional cost for mushrooms
    )
}

// Client Code
public class PizzaStore (
    public static void main(String[] args) {
        Pizza pizza = new BasePizza();
        pizza = new ExtraCheese(pizza);
        pizza = new Mushrooms(pizza);
        
        System.out.println(pizza.getDescription());
        System.out.println("Total Cost: " + pizza.cost());
    )
}

In the code above, we define a Pizza as the base component, with BasePizza as the concrete component. We then add toppings using the ToppingDecorator abstract class and its implementations, ExtraCheese and Mushrooms. The PizzaStore class demonstrates how to create a pizza with various toppings authored using decorators.

Keywords

  • Decorator Pattern
  • Design Pattern
  • Java Coding
  • Class Explosion
  • Base Component
  • Concrete Component
  • Decorator Class
  • Topping Decorator
  • Pizza Shop Example
  • System Design Interview

FAQ

What is the Decorator Design Pattern?

The Decorator Design Pattern is a structural design pattern that allows you to add new behaviors to objects dynamically. It involves wrapping one class with another to enhance its functionality without modifying its structure.

Why should I use the Decorator Pattern?

The Decorator Pattern is useful for reducing class explosion scenarios in large systems where multiple features can be combined. This keeps your design flexible and maintainable.

How is the Decorator Pattern implemented in Java?

Implement the pattern using an abstract class or interface as the base component. Create concrete components that implement this interface. Use decorators that also implement the same interface and hold a reference to a base component to add new features.

Can I use multiple decorators in a single class?

Yes, you can stack multiple decorators. Each decorator can be applied to a component recursively, allowing for complex combinations of features.