Thursday, 2 January 2014

Decorator Design pattern - Implementation [Pizza]

🍕 Master Core Java & Structural Design Patterns!

Enjoying this hands-on breakdown? Subscribe to Ram N Java for clean code, design architecture, and real-world Java tutorials.

🔔 Subscribe to Ram N Java

Understanding the Decorator Design Pattern

The Decorator Pattern is a structural design pattern that lets you dynamically attach additional responsibilities or toppings to an object at runtime without altering its original source code.

Instead of building massive class inheritance hierarchies for every possible combination, decorators wrap the target component using object composition.

The Real-World Scenario: Decorating a Pizza

Consider ordering a pizza. Every customer starts with a standard base pizza (such as a Plain Pizza or simple crust). From there, toppings are added dynamically:

  • Extra Cheese
  • Chicken / Meat
  • Vegetables

Creating subclasses for every combination—such as PlainPizzaWithCheeseAndChicken or PlainPizzaWithChickenOnly—leads to class explosion and rigid code.

With the Decorator Pattern, each topping acts as a wrapper that augments both description and price dynamically!

Step-by-Step Code Walkthrough

1. Component Interface

Define the core contract for preparing a pizza:

public interface Pizza {
    String makePizza();
}

2. Concrete Component

Create a base pizza implementation:

public class PlainPizza implements Pizza {
    @Override
    public String makePizza() {
        return "Plain Dough Pizza";
    }
}

3. Abstract Decorator

The abstract decorator implements Pizza and maintains a reference to a wrapped Pizza instance:

public abstract class PizzaDecorator implements Pizza {
    protected Pizza specialPizza;

    public PizzaDecorator(Pizza specialPizza) {
        this.specialPizza = specialPizza;
    }

    @Override
    public String makePizza() {
        return specialPizza.makePizza();
    }
}

4. Concrete Decorators (Toppings)

Implement specific toppings that enhance the pizza:

public class ChickenPizzaDecorator extends PizzaDecorator {
    public ChickenPizzaDecorator(Pizza specialPizza) {
        super(specialPizza);
    }

    @Override
    public String makePizza() {
        return specialPizza.makePizza() + " + Spicy Chicken";
    }
}

public class CheesePizzaDecorator extends PizzaDecorator {
    public CheesePizzaDecorator(Pizza specialPizza) {
        super(specialPizza);
    }

    @Override
    public String makePizza() {
        return specialPizza.makePizza() + " + Mozzarella Cheese";
    }
}

5. Execution Example

public class PizzaTest {
    public static void main(String[] args) {
        Pizza pizza = new CheesePizzaDecorator(new ChickenPizzaDecorator(new PlainPizza()));
        System.out.println(pizza.makePizza());
        // Output: Plain Dough Pizza + Spicy Chicken + Mozzarella Cheese
    }
}

Why Use the Decorator Pattern?

  • Open/Closed Principle: Introduce new toppings anytime without modifying existing pizza classes.
  • Flexible Combination: Chain toppings dynamically in any desired order.
  • Cleaner Code: Eliminates bloated inheritance hierarchies.

Related Tutorials You Might Like

Learn more about design patterns with these related lessons from the channel:


Click here to watch in Youtube : https://www.youtube.com/watch?v=Xz2eKQy9kBU

Click the below Image to Enlarge
Decorator Design pattern - Implementation [Pizza]

Decorator Design pattern - Implementation [Pizza] - Class Diagram



































Pizza.java

public interface Pizza
{
public String makePizza();
}

PlainPizza.java

public class PlainPizza implements Pizza
{

@Override
public String makePizza()
{
return "Plain Pizza ";
}
}

PizzaDecorator.java

abstract class PizzaDecorator implements Pizza
{

protected Pizza pizza;

public PizzaDecorator( Pizza pizza )
{
this.pizza = pizza;
}

public String makePizza()
{
return pizza.makePizza();
}
}

ChickenPizzaDecorator.java

public class ChickenPizzaDecorator extends PizzaDecorator
{

public ChickenPizzaDecorator( Pizza pizza )
{
super(pizza);
}

public String makePizza()
{
return pizza.makePizza() + addChickenAndCheese();

}

private String addChickenAndCheese()
{
return ",Chicken and Cheese added";
}
}

VegPizzaDecorator.java

public class VegPizzaDecorator extends PizzaDecorator
{

public VegPizzaDecorator( Pizza pizza )
{
super(pizza);
}

public String makePizza()
{
return pizza.makePizza() + addVegetableAndCheese();
}

private String addVegetableAndCheese()
{
return ",Vegetable and Cheese added";

}
}

DecoratorClient.java

public class DecoratorClient
{

public static void main( String args[] )
{
PlainPizza plainPizzaObj = new PlainPizza();
String plainPizza = plainPizzaObj.makePizza();
System.out.println(plainPizza);

String chickenPizza = new ChickenPizzaDecorator(plainPizzaObj).makePizza();
System.out.println("\n'" + chickenPizza + "' using ChickenPizzaDecorator");

String vegPizza = new VegPizzaDecorator(plainPizzaObj).makePizza();
System.out.println("\n'" + vegPizza + "' using VegPizzaDecorator");

}

}

Output

Plain Pizza 

'Plain Pizza ,Chicken and Cheese added' using ChickenPizzaDecorator

'Plain Pizza ,Vegetable and Cheese added' using VegPizzaDecorator


See also:

  • Decorator Design pattern - Introduction
  • Decorator Design pattern - Real Time Example [Dosa]
  • Decorator Design pattern - Real Time Example [Ice Cream]
  • Decorator Design pattern - Real Time Example [Pizza]
  • Decorator Design pattern - Real Time Example [Car]
  • Decorator Design pattern - Class Diagram
  • Decorator Design pattern - Sequence Diagram
  • Decorator Design pattern - Implementation [Dosa]
  • Decorator Design pattern - Implementation [Pizza]
  • Decorator Design pattern - Implementation [Ice Cream]
  • Decorator Design pattern - Implementation [Car]
  • Decorator Design pattern - Implementation [Shape]
  • Decorator Design pattern - Key Points
  • All Design Patterns Links
  • No comments:

    Post a Comment

    Tutorials