The refactored code uses the Decorator Pattern. It adds behavior to an object at runtime without changing its class. It uses Composition (one object holds another) instead of inheritance, so there is no "Class Explosion" from covering every mix of features.
1. Key Improvements
dynamic Behavior Addition
The Benefit: You can add toppings (Extra Cheese, Olives, Stuffed Crust) to a base pizza (Margherita, Plain) at runtime based on user selection.
The Impact: You do not need a MargheritaWithCheeseAndOlives class, because the toppings are "wrapped" around the base pizza.
Open/Closed Principle (OCP)
The Benefit: To add a new topping, such as "Mushroom," you only need to create one new Mushroom decorator class.
The Impact: Existing classes stay untouched, so there is less risk of bugs in the base pizza or other toppings.
Single Responsibility Principle (SRP)
The Benefit: Each decorator handles only its own feature (e.g., ExtraCheese only handles cheese cost and description).
The Impact: This is easier to maintain, test and debug than one big class that handles many combinations.
2. Structural Breakdown
Component
Responsibility
Component Interface (Pizza)
The shared contract (list of methods) for base objects and decorators.
Concrete Components (MargheritaPizza)
The basic pizzas that toppings can be added to.
Base Decorator (PizzaDecorator)
An abstract class that implements Pizza and also keeps a reference ("HAS-A") to a Pizza object.
Concrete Decorators (ExtraCheese, Olives)
Classes that extend the base decorator. They override getDescription() and getCost() to add their own text and price.
3. Comparison of "Before" vs "After"
Feature
Before (Inheritance)
After (Decorator Pattern)
Number of Classes
Increases exponentially ($2^N$ for $N$ toppings).
Increases linearly ($1$ class per new topping).
Flexibility
Static; combinations are fixed at compile-time.
Dynamic; combinations are built at runtime.
Maintenance
High; changing one base class affects many subclasses.
Low; features are isolated in their own decorators.
Code Reuse
Low; logic for "Cheese" is repeated in many classes.
High; "Cheese" logic exists in exactly one place.
4. Summary of the "Wrap"
The Decorator Pattern works like a Russian Nesting Doll. You start with a small base (the Pizza) and keep wrapping it in larger shells (the Decorators). Each shell asks the inner doll for its value and then adds its own description and cost.
1package DesignPatterns.StructuralPatterns.DecoratorDesignPattern.After;23/**4 * 1. THE COMPONENT INTERFACE5 * Defines the basic operations for both the objects being decorated6 * and the decorators themselves.7 */8interface Pizza {9 String getDescription();10 double getCost();11}1213/**14 * 2. CONCRETE COMPONENTS15 * These are the base objects that can be decorated.16 * Notice they don't know anything about toppings yet.17 */18class PlainPizza implements Pizza {1920 @Override21 public String getDescription() {22 return "Plain Pizza";23 }2425 @Override26 public double getCost() {27 return 150.00;28 }29}3031class MargheritaPizza implements Pizza {3233 @Override34 public String getDescription() {35 return "Margherita Pizza";36 }3738 @Override39 public double getCost() {40 return 200.00;41 }42}4344/**45 * 3. THE ABSTRACT DECORATOR46 * Key Concept: It implements the 'Pizza' interface AND contains a 'Pizza' object.47 * This "HAS-A" relationship allows us to wrap any Pizza (base or another decorator).48 */49abstract class PizzaDecorator implements Pizza {5051 protected Pizza pizza; // The "wrapped" object5253 public PizzaDecorator(Pizza pizza) {54 this.pizza = pizza;55 }56}5758/**59 * 4. CONCRETE DECORATORS60 * These classes add specific behavior (toppings/costs) to the wrapped pizza.61 */62class ExtraCheese extends PizzaDecorator {6364 public ExtraCheese(Pizza pizza) {65 super(pizza);66 }6768 @Override69 public String getDescription() {70 // We delegate to the wrapped object and then append our own info71 return pizza.getDescription() + ", Extra Cheese";72 }7374 @Override75 public double getCost() {76 // We delegate the base cost calculation and then add our own price77 return pizza.getCost() + 40.0;78 }79}8081class Olives extends PizzaDecorator {8283 public Olives(Pizza pizza) {84 super(pizza);85 }8687 @Override88 public String getDescription() {89 return pizza.getDescription() + ", Olives";90 }9192 @Override93 public double getCost() {94 return pizza.getCost() + 30.0;95 }96}9798class StuffedCrust extends PizzaDecorator {99100 public StuffedCrust(Pizza pizza) {101 super(pizza);102 }103104 @Override105 public String getDescription() {106 return pizza.getDescription() + ", Stuffed Crust";107 }108109 @Override110 public double getCost() {111 return pizza.getCost() + 50.0;112 }113}114115public class Main {116117 public static void main(String[] args) {118 // FLEXIBILITY: We can now build a pizza like an onion skin.119 // Base layer: Margherita120 Pizza myPizza = new MargheritaPizza();121122 // Layer 2: Wrap it in Cheese123 myPizza = new ExtraCheese(myPizza);124125 // Layer 3: Wrap it in Olives126 myPizza = new Olives(myPizza);127128 // Layer 4: Wrap it in Stuffed Crust129 myPizza = new StuffedCrust(myPizza);130131 // This recursive delegation calculates the total description and cost perfectly.132 System.out.println("Final Order: " + myPizza.getDescription());133 System.out.println("Total Bill: ₹" + myPizza.getCost());134 }135}136