Skip to content

Design Pattern: Decorator (Structural)

Add behaviour at runtime without subclassing

Builds on Composition vs Inheritance and OCP - The Open/Closed Principle

Stage 3core2 min read

The Class Explosion Problem

This design uses Static Inheritance (the class tree is fixed when the code is written) for every variation. That works badly when features such as pizza toppings can be mixed in many ways.


1. Key Issues in this Design

Class Explosion (Combinatorial Nightmare)

  • The Issue: To cover every mix of toppings, you need a new class for each one (e.g., CheeseOlivePizza, CheeseStuffedPizza, CheeseOliveStuffedPizza).
  • The Impact: With $N$ toppings, you may need $2^N$ classes. One more topping (like "Mushroom") means several new combination classes.

Violation of the Single Responsibility Principle (SRP)

  • The Issue: A subclass like CheeseOliveStuffedPizza carries the logic of several separate features.
  • The Impact: The "Cheese" logic is spread over every subclass that includes it, so the code is hard to maintain.

Static Nature of Inheritance

  • The Issue: Inheritance is fixed at compile time, before the program runs.
  • The Impact: You cannot take an existing PlainPizza and "add" cheese at runtime when the user asks. You must throw it away and create a new CheesePizza.

Rigid and Brittle Hierarchy

  • The Issue: A deep inheritance tree breaks easily.
  • The Impact: If the price or logic of PlainPizza changes, the change reaches dozens of subclasses. This risks regression bugs (old features that break).

2. Structural Analysis

ComponentStatusObservation
Base Class (PlainPizza)OverloadedRoot of a tree that is too big to manage.
SubclassesRedundantMost subclasses exist only to stand for one mix of features.
ExtensionHardcodedEach new feature needs several combination classes written by hand.

3. Summary of the "Smell"

When you see a deep inheritance hierarchy where subclasses are named after combinations of features (e.g., FeatureAWithFeatureB), it is a clear sign of Class Explosion. The Decorator Pattern fixes this by using Composition (an object holding another object) instead of inheritance. You "wrap" behaviors at runtime.

In this topic

Before code1 file

Before/Main.java44 linesGitHub
package DesignPatterns.StructuralPatterns.DecoratorDesignPattern.Before;/** * PROBLEM: Combinatorial Explosion. * This design relies on inheritance to add features (toppings). */class PlainPizza {}// Adding one topping: Requires a new class.class CheesePizza extends PlainPizza {}class OlivePizza extends PlainPizza {}class StuffedPizza extends PlainPizza {}// Adding two toppings: Requires even more classes to cover every possible combination.class CheeseStuffedPizza extends CheesePizza {}class CheeseOlivePizza extends CheesePizza {}/** * PROBLEM: Exponential Growth. * If you add a 4th topping (like Mushroom), you would need to create classes for: * MushroomPizza, CheeseMushroomPizza, OliveMushroomPizza, CheeseOliveMushroomPizza, etc. */class CheeseOliveStuffedPizza extends CheeseOlivePizza {}public class Main {    public static void main(String[] args) {        // Base pizza        PlainPizza plainPizza = new PlainPizza();        // ISSUES:        // 1. Static Nature: You cannot add a topping to an existing object at runtime.        // 2. Rigid Structure: If you want to change the price of 'Cheese', you have to        //    update every single class that extends or includes Cheese.        // 3. Maintenance: It is nearly impossible to maintain 20+ classes for simple pizza variations.        CheeseOliveStuffedPizza cheeseOliveStuffedPizza =            new CheeseOliveStuffedPizza();    }}

Done with this topic?

How well do you know it?

PreviousDesign Pattern: Adapter (Structural)