0% found this document useful (0 votes)
7 views7 pages

Decorator Pattern

The Decorator Pattern allows for dynamic addition of functionality to objects without modifying their original structure, addressing issues like class explosion in complex systems. Use cases include a Pizza Shop and a Coffee Cafe, where various toppings and add-ons can be combined without creating numerous subclasses. The implementation involves defining a component interface, concrete components, and decorators that extend functionality while maintaining a clean and manageable codebase.

Uploaded by

siddharthj.nitj
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
7 views7 pages

Decorator Pattern

The Decorator Pattern allows for dynamic addition of functionality to objects without modifying their original structure, addressing issues like class explosion in complex systems. Use cases include a Pizza Shop and a Coffee Cafe, where various toppings and add-ons can be combined without creating numerous subclasses. The implementation involves defining a component interface, concrete components, and decorators that extend functionality while maintaining a clean and manageable codebase.

Uploaded by

siddharthj.nitj
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 7

Decorator

Definition
Use cases
Example 1: Pizza Shop
Example 2: Coffee Cafe
Why do we need the Decorator Pattern?
Decorator Pattern as a Solution
Class Diagram
Understanding the Structure (e.g., Pizza Shop)
Breakdown of Relationships
Implementation
Code
Output

Resources

Video → 32. All Structural Design Patterns | Decorator, Proxy, Com


posite, Adapter, Bridge, Facade, FlyWeight
Video → 4. Decorator Design Pattern Explanation with Java Codin
g (Hindi) | LLD System Design

Definition
Decorator pattern allows you to add new functionality to objects dynamically without
altering their original structure.

Use cases
Example 1: Pizza Shop
What we have:
Basic Pizza: Margherita (Crust + Cheese)
Available Toppings: Extra Cheese, Olives, Jalapenos, Pepperoni, Veggies, Spicy Red Pepper, etc.
Pizzas
Type 1: Margherita + Extra Cheese
Type 2: Margherita + Olives + Jalapenos
Type 3: Margherita + Olives + Jalapenos + Veggies + Extra Cheese
Type 4: Margherita + Pepperoni + Spicy Red Pepper

Example 2: Coffee Cafe


What we have:
Espresso: A strong shot of pure coffee.
Available add-ons: Sugar, Hot water, Cold Water, Ice, Steamed Milk, Milk Foam, Cold Milk, Chocolate Syrup,
Vanilla ice cream, etc.
Coffee Beverages
Type 1: Doppio → A double shot of espresso.
Type 2: Americano → Espresso + Hot water.
Type 3: Cappuccino → Espresso + Steamed Milk + Milk Foam.
Type 4: Mocha → Espresso + Steamed Milk + Chocolate Syrup.
Type 5: Cold Coffee → Espresso + Cold Water + Cold Milk + Ice.
Type 6: MakeYourOwnCoffee → Espresso + Cold Water + Cold Milk + Ice + Vanilla Ice Cream + Chocolate
Syrup.

Why do we need the Decorator Pattern?


To grasp the necessity of the Decorator Pattern, we must first look into the challenges encountered when
implementing the problem using a naive approach, i.e., without utilizing the Decorator Pattern.
Issue: Class Explosion
Class explosion refers to a situation where the number of classes in your system grows rapidly and uncontrollably,
making the codebase hard to navigate, maintain, and understand.
This happens when every combination of additional behavior requires a new decorator class(if implemented in a
naive way).
You have a base component in the Coffee Cafe example, i.e., Coffee , and which has decorators like:
MilkDecorator , SugarDecorator , VanillaIceCreamDecorator ,
ChocolateSyrupDecorator, etc.
To support all combinations of these decorators, we need to either manually chain them(which doesn’t allow
changes at runtime) or create new classes for every common combination, e.g.,
MilkAndSugarCoffeeDecorator , SugarAndVanillaDecorator ,
MilkVanillaIceCreamDecorator , ChocolateSyrupVanillaIceCreamDecorator ,
etc.
That’s where class explosion kicks in; we begin to have too many classes(extreme subclassing) to represent all
possible combinations of features/behaviours.
Decorator Pattern as a Solution
Class Diagram

Understanding the Structure (e.g., Pizza Shop)

1. Component Interface ( BasePizza )


a. Defines the contract that both the base object and the decorators must follow.

b. Contains methods like getDescription() and getCost() .


PlainPizza , Farmhouse , TandooriPaneerDelight ,
2. Concrete Component (

ChickenDominator )
The base implementation that provides core functionality.
PlainPizza represents a simple pizza without any toppings.
Can contain a pre-formulated combination of features like Farmhouse , TandooriPaneerDelight ,
and ChickenDominator .
3. Base Decorator ( ToppingDecorator )
a. An abstract class that implements the Pizza interface.
b. Contains a reference to a Pizza object - the component itself (composition).
c. Delegates call the wrapped pizza object.

ExtraCheeseTopping , VeggiesTopping , MushroomTopping ,


4. Concrete Decorators (

PepperoniTopping , etc.)
Extend the base decorator to add specific functionality.
Override methods to add their behavior while calling the wrapped object.
Can modify both the description and cost.

Breakdown of Relationships
1. IS-A Relationship (Inheritance-based)

In the above example, Concrete Components ( PlainPizza , Farmhouse ,


TandooriPaneerDelight , ChickenDominator ) all implement the Component Interface
( BasePizza ).
Hence, they are types of Pizza → IS-A Relationship.
Thant means
PlainPizza is-a BasePizza .
ExtraCheeseTopping is-a ToppingDecorator .
ToppingDecorator is-a BasePizza .
Therefore, ExtraCheeseTopping is-a BasePizza .
2. HAS-A Relationship (Composition-based)

In the above example, ToppingDecorator has a BasePizza .


ExtraCheeseTopping has a PlainPizza , which is a BasePizza (e.g., Plain Pizza with extra
cheese).
MushroomTopping has a PlainPizza (e.g., Mushroom Pizza).
Each new decorator wraps around another Pizza , adding its behavior (like toppings).
That means
ToppingDecorator has-a Pizza .
Decorators like ExtraCheeseTopping , MushroomTopping , etc., have a reference to another
Pizza (result of another decorated component or the component itself).

Implementation
Code
1 // Step 1: Define the Component Interface
2 public interface BasePizza {
3 String getDescription();
4 double getCost();
5 }

1 // Step 2: Define the Concrete Component


2 class PlainPizza implements BasePizza {
3 @Override
4 public String getDescription() {
5 return "Plain Pizza";
6 }
7 @Override
8 public double getCost() {
9 return 200.00;
10 }
11 }
12 public class Farmhouse implements BasePizza {
13 @Override
14 public String getDescription() {
15 return "Farmhouse Pizza";
16 }
17 @Override
18 public double getCost() {
19 return 300.0;
20 }
21 }
22 public class TandooriPaneerDelight implements BasePizza {
23 @Override
24 public String getDescription() {
25 return "Tandoori Paneer Delight Pizza";
26 }
27 @Override
28 public double getCost() {
29 return 400.0;
30 }
31 }
32 public class ChickenDominator implements BasePizza {
33 @Override
34 public String getDescription() {
35 return "Chicken Dominator Pizza";
36 }
37
38 @Override
39 public double getCost() {
40 return 500.0;
41 }
42 }

1 // Step 3: Define the Abstract Base Decorator


2 public abstract class ToppingDecorator implements BasePizza {
3 BasePizza pizza;
4
5 public ToppingDecorator(BasePizza pizza) {
6 this.pizza = pizza;
7 }
8 }

1 // Step 4: Define the Concrete Decorator


2 public class ExtraCheeseTopping extends ToppingDecorator {
3
4 public ExtraCheeseTopping(BasePizza pizza) {
5 super(pizza);
6 }
7
8 @Override
9 public String getDescription() {
10 return pizza.getDescription() + " + Extra Cheese";
11 }
12
13 @Override
14 public double getCost() {
15 return pizza.getCost() + 20;
16 }
17 }
18 public class VeggiesTopping extends ToppingDecorator {
19
20 public VeggiesTopping(BasePizza pizza) {
21 super(pizza);
22 }
23
24 @Override
25 public String getDescription() {
26 return pizza.getDescription() + " + Veggies";
27 }
28
29 @Override
30 public double getCost() {
31 return pizza.getCost() + 30;
32 }
33 }
34 public class MushroomTopping extends ToppingDecorator {
35
36 public MushroomTopping(BasePizza pizza) {
37 super(pizza);
38 }
39
40 @Override
41 public String getDescription() {
42 return pizza.getDescription() + " + Mushroom";
43 }
44
45 @Override
46 public double getCost() {
47 return pizza.getCost() + 40;
48 }
49 }
50 public class PepperoniTopping extends ToppingDecorator {
51
52 public PepperoniTopping(BasePizza pizza) {
53 super(pizza);
54 }
55
56 @Override
57 public String getDescription() {
58 return pizza.getDescription() + " + Pepperoni";
59 }
60
61 @Override
62 public double getCost() {
63 return pizza.getCost() + 50;
64 }
65 }

1 // Step 5: Client Demonstration


2 public class PizzaShop {
3 public static void main(String[] args) {
4 System.out.println("======= Decorator Design Pattern ======");
5 // Create a plain pizza
6 BasePizza pizza1 = new PlainPizza();
7 System.out.println("Order 1: " + pizza1.getDescription() + " =
Rs." + pizza1.getCost());
8
9 // Add toppings to the PlainPizza - Extra Cheese Only
10 BasePizza pizza2 = new ExtraCheeseTopping(new PlainPizza());
11 System.out.println("Order 2: " + pizza2.getDescription() + " =
Rs." + pizza2.getCost());
12
13 // Add toppings to the PlainPizza - Extra Cheese and Veggies
14 BasePizza pizza3 = new VeggiesTopping(new
ExtraCheeseTopping(new PlainPizza()));
15 System.out.println("Order 3: " + pizza3.getDescription() + " =
Rs." + pizza3.getCost());
16
17 // Add toppings to the PlainPizza - Extra Cheese and Pepperoni
18 BasePizza pizza4 = new PepperoniTopping(new
ExtraCheeseTopping(new PlainPizza()));
19 System.out.println("Order 4: " + pizza4.getDescription() + " =
Rs." + pizza4.getCost());
20
21 // Add toppings to the PlainPizza - Extra Cheese, Mushroom and
Pepperoni
22 BasePizza pizza5 = new MushroomTopping(new
PepperoniTopping(new ExtraCheeseTopping(new PlainPizza())));
23 System.out.println("Order 5: " + pizza5.getDescription() + " =
Rs." + pizza5.getCost());
24
25 // Farmhouse Pizza
26 BasePizza pizza6 = new Farmhouse();
27 System.out.println("Order 6: " + pizza6.getDescription() + " =
Rs." + pizza6.getCost());
28
29 // Farmhouse Pizza with Extra Cheese and Mushroom
30 BasePizza pizza7 = new MushroomTopping(new
ExtraCheeseTopping(new Farmhouse()));
31 System.out.println("Order 7: " + pizza7.getDescription() + " =
Rs." + pizza7.getCost());
32
33 // Tandoori Paneer Delight Pizza
34 BasePizza pizza8 = new TandooriPaneerDelight();
35 System.out.println("Order 8: " + pizza8.getDescription() + " =
Rs." + pizza8.getCost());
36
37 // Chicken Dominator
38 BasePizza pizza9 = new ChickenDominator();
39 System.out.println("Order 9: " + pizza9.getDescription() + " =
Rs." + pizza9.getCost());
40
41 // Chicken Dominator with Mushroom
42 BasePizza pizza10 = new MushroomTopping(new
ChickenDominator());
43 System.out.println("Order 10: " + pizza10.getDescription() + "
= Rs." + pizza10.getCost());
44 }
45 }

Output

The key benefit of the Decorator pattern is that it provides a flexible alternative to subclassing for extending
functionality, allowing you to mix and match behaviors as needed at runtime.

You might also like