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 |
+-----------------+ +-----------------+
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());
}
}
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
- Component Interface (Pizza): Defines common operations for base pizzas and decorators
- Concrete Component (MargheritaPizza): The base pizza without any toppings
- Abstract Decorator (PizzaDecorator): Holds a reference to a Pizza object and delegates operations
- Concrete Decorators (CheeseDecorator, PaneerDecorator): Add specific toppings and costs
- 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
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
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!
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;
}
}
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
Top comments (0)