DEV Community

NOOB
NOOB

Posted on

LLD-8:Pizza Topping System

Pizza Topping System - Decorator Pattern Implementation

This is an implementation of the Decorator design pattern for a pizza shop billing system demonstrating how to stack multiple decorators of the same type for customizable orders.

Problem Statement

Building a billing system for a Pizza shop where customers can add multiple toppings, including multiple instances of the same topping. The challenge is handling orders like "Double Cheese Paneer Margherita" where the same decorator (Cheese) is applied twice.

Key Challenge: A customer wants:

  • Margherita + ExtraCheese + ExtraCheese + Paneer
  • Total Cost: ₹100 + ₹20 + ₹20 + ₹60 = ₹200
  • Must support stacking the same decorator multiple times

Class Diagram

          +----------------+
          |     Pizza      | <-------------------------+
          |   (Interface)  |                           |
          +----------------+                           |
          | + getCost()    |                           |
          | + getDesc()    |                           |
          +-------+--------+                           |
                  ^                                    |
                  | (Implements)                       |
                  |                                    | (Wraps / Has-A)
       +----------+-----------+             +----------+----------+
       |                      |             |                     |
 +-----+-----------+    +-----+-------------+---+      +----------+----------+
 | MargheritaPizza |    |     PizzaDecorator    |<>----|     pizza field     |
 +-----------------+    |       (Abstract)      |      +---------------------+
 | Returns 100.0   |    +-----------------------+
 +-----------------+              ^
                                  | (Extends)
                                  |
                   +--------------+--------------+
                   |                             |
          +--------+--------+           +--------+--------+
          | CheeseDecorator |           | PaneerDecorator |
          +-----------------+           +-----------------+
          | + getCost()     |           | + getCost()     |
          |   return 20.0   |           |   return 60.0   |
          |   + super.cost  |           |   + super.cost  |
          +-----------------+           +-----------------+
Enter fullscreen mode Exit fullscreen mode

Implementation

package decorator.pizzatoppingsystem;

/**
 * Pizza Topping System using the Decorator Pattern.
 * Demonstrates recursive composition to calculate total cost.
 */
public class PizzaToppingSystem {

    /**
     * Component Interface.
     * Defines the blueprint for Base Pizzas and Decorators.
     */
    interface Pizza {
        String getDescription();
        double getCost();
    }

    /**
     * Concrete Component (The Base Object).
     */
    static class MargheritaPizza implements Pizza {
        @Override
        public String getDescription() {
            return "Margherita Pizza";
        }

        @Override
        public double getCost() {
            return 100.00;
        }
    }

    /**
     * Abstract Decorator.
     * Contains a reference to a Pizza object (HAS-A relationship).
     */
    static abstract class PizzaDecorator implements Pizza {
        protected Pizza pizza; // The object being wrapped

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

        @Override
        public String getDescription() {
            return pizza.getDescription(); // Delegate to wrapped object
        }

        @Override
        public double getCost() {
            return pizza.getCost(); // Delegate to wrapped object
        }
    }

    /**
     * Concrete Decorator (Adds Cheese).
     */
    static class CheeseDecorator extends PizzaDecorator {
        public CheeseDecorator(Pizza pizza) {
            super(pizza);
        }

        @Override
        public String getDescription() {
            return super.getDescription() + ", Cheese";
        }

        @Override
        public double getCost() {
            return super.getCost() + 20.00;
        }
    }

    /**
     * Concrete Decorator (Adds Paneer).
     */
    static class PaneerDecorator extends PizzaDecorator {
        public PaneerDecorator(Pizza pizza) {
            super(pizza);
        }

        @Override
        public String getDescription() {
            return super.getDescription() + ", Paneer";
        }

        @Override
        public double getCost() {
            return super.getCost() + 60.00;
        }
    }

    public static void main(String[] args) {
        System.out.println("---- Pizza Shop Order ----");

        // 1. Order Base Pizza
        Pizza margheritaPizza = new MargheritaPizza();
        System.out.println(margheritaPizza.getDescription() + " : Rs. " + 
                         margheritaPizza.getCost());

        // 2. Add Cheese
        margheritaPizza = new CheeseDecorator(margheritaPizza);
        System.out.println(margheritaPizza.getDescription() + " : Rs. " + 
                         margheritaPizza.getCost());

        // 3. Add Extra Cheese (Recursion)
        margheritaPizza = new CheeseDecorator(margheritaPizza);
        System.out.println(margheritaPizza.getDescription() + " : Rs. " + 
                         margheritaPizza.getCost());

        // 4. Add Paneer
        margheritaPizza = new PaneerDecorator(margheritaPizza);
        System.out.println(margheritaPizza.getDescription() + " : Rs. " + 
                         margheritaPizza.getCost());
    }
}
Enter fullscreen mode Exit fullscreen mode

Key Features

  • Decorator Pattern: Dynamically adds toppings to pizza without modifying base class
  • Recursive Stacking: Same decorator can be applied multiple times (double cheese, triple cheese, etc.)
  • Flexible Combinations: Any combination of toppings in any order
  • Composition over Inheritance: Uses wrapping instead of creating subclasses for each combination
  • Open/Closed Principle: Open for extension, closed for modification
  • Cost Accumulation: Each decorator adds its cost to the wrapped object's cost

How It Works

  1. Component Interface (Pizza): Defines common operations for base pizzas and decorators
  2. Concrete Component (MargheritaPizza): The base pizza without any toppings
  3. Abstract Decorator (PizzaDecorator): Holds a reference to a Pizza object and delegates operations
  4. Concrete Decorators (CheeseDecorator, PaneerDecorator): Add specific toppings and costs
  5. Recursive Wrapping: Each decorator wraps the previous object, creating a nested chain

Sample Output

---- Pizza Shop Order ----
Margherita Pizza : Rs. 100.0
Margherita Pizza, Cheese : Rs. 120.0
Margherita Pizza, Cheese, Cheese : Rs. 140.0
Margherita Pizza, Cheese, Cheese, Paneer : Rs. 200.0
Enter fullscreen mode Exit fullscreen mode

Visualization of Recursive Wrapping

Order: Margherita + Cheese + Cheese + Paneer

PaneerDecorator (₹60)
    └── wraps CheeseDecorator (₹20)
            └── wraps CheeseDecorator (₹20)
                    └── wraps MargheritaPizza (₹100)

When getCost() is called:
1. PaneerDecorator.getCost() = super.getCost() + 60.00
2. CheeseDecorator.getCost() = super.getCost() + 20.00
3. CheeseDecorator.getCost() = super.getCost() + 20.00
4. MargheritaPizza.getCost() = 100.00
5. Result: 100.00 + 20.00 + 20.00 + 60.00 = 200.00
Enter fullscreen mode Exit fullscreen mode

Real-World Applications

  • Pizza ordering systems (Domino's, Pizza Hut)
  • Burger customization (extra patties, extra cheese)
  • Sandwich builders (Subway)
  • Ice cream with multiple scoops and toppings
  • Car wash packages (basic + wax + tire shine)
  • Software feature bundles
  • Cloud service add-ons

Key Advantage: Stacking Same Decorator

Problem Without Decorator:

  • Need separate classes: PizzaWithCheese, PizzaWithDoubleCheese, PizzaWithTripleCheese
  • Exponential class explosion with combinations

Solution With Decorator:

Pizza pizza = new MargheritaPizza();
pizza = new CheeseDecorator(pizza);  // Add cheese once
pizza = new CheeseDecorator(pizza);  // Add cheese again
pizza = new CheeseDecorator(pizza);  // Add cheese third time
// Can stack indefinitely!
Enter fullscreen mode Exit fullscreen mode

Adding New Toppings

Adding a new topping is straightforward:

static class OlivesDecorator extends PizzaDecorator {
    public OlivesDecorator(Pizza pizza) {
        super(pizza);
    }

    @Override
    public String getDescription() {
        return super.getDescription() + ", Olives";
    }

    @Override
    public double getCost() {
        return super.getCost() + 15.00;
    }
}
Enter fullscreen mode Exit fullscreen mode

No need to modify existing code - just add the new decorator class!

Extension Ideas

// Create complex orders easily
Pizza vegSupreme = new MargheritaPizza();
vegSupreme = new CheeseDecorator(vegSupreme);
vegSupreme = new PaneerDecorator(vegSupreme);
vegSupreme = new OlivesDecorator(vegSupreme);
vegSupreme = new MushroomDecorator(vegSupreme);
vegSupreme = new CheeseDecorator(vegSupreme);  // Extra cheese on top!

System.out.println(vegSupreme.getDescription() + " : Rs. " + vegSupreme.getCost());
// Output: Margherita Pizza, Cheese, Paneer, Olives, Mushroom, Cheese : Rs. 235.0
Enter fullscreen mode Exit fullscreen mode

Top comments (0)