Decorator Pattern
Decorator Pattern: Adding Behavior Dynamically
Section titled “Decorator Pattern: Adding Behavior Dynamically”Now let’s explore the Decorator Pattern - a powerful structural design pattern that lets you add behavior to objects dynamically.
Why Decorator Pattern?
Section titled “Why Decorator Pattern?”Imagine you’re ordering a coffee. You start with a basic coffee, then add milk, then add sugar, then add whipped cream. Each addition modifies your coffee without changing the base coffee itself. The Decorator Pattern works the same way - you wrap objects with decorators to add functionality dynamically!
The Decorator Pattern attaches additional responsibilities to an object dynamically. It provides a flexible alternative to subclassing for extending functionality.
What’s the Use of Decorator Pattern?
Section titled “What’s the Use of Decorator Pattern?”The Decorator Pattern is useful when:
- You want to add behavior dynamically - At runtime, not compile time
- You want to add behavior to individual objects - Not entire classes
- You want to avoid subclass explosion - Too many combinations to subclass
- You want flexible extension - Add features without modifying existing code
- You want to combine features - Multiple decorators can be combined
What Happens If We Don’t Use Decorator Pattern?
Section titled “What Happens If We Don’t Use Decorator Pattern?”Without the Decorator Pattern, you might:
- Subclass explosion - Need a class for every combination (Coffee, CoffeeWithMilk, CoffeeWithSugar, CoffeeWithMilkAndSugar, etc.)
- Modify existing code - Need to change base classes to add features
- Rigid design - Can’t add features at runtime
- Violate Open/Closed Principle - Need to modify code to extend functionality
- Code duplication - Similar code repeated across subclasses
Simple Example: The Coffee Shop
Section titled “Simple Example: The Coffee Shop”Let’s start with a super simple example that anyone can understand!
Visual Representation
Section titled “Visual Representation”Interaction Flow
Section titled “Interaction Flow”Here’s how the Decorator Pattern works in practice - showing how decorators wrap components:
The Problem
Section titled “The Problem”You’re building a coffee shop system. You have different types of coffee and different add-ons (milk, sugar, whipped cream). Without Decorator Pattern:
# ❌ Without Decorator Pattern - Subclass explosion!
class Coffee: """Base coffee""" def get_cost(self) -> float: return 5.0
def get_description(self) -> str: return "Coffee"
class CoffeeWithMilk(Coffee): """Coffee with milk""" def get_cost(self) -> float: return super().get_cost() + 1.0
def get_description(self) -> str: return super().get_description() + ", Milk"
class CoffeeWithSugar(Coffee): """Coffee with sugar""" def get_cost(self) -> float: return super().get_cost() + 0.5
def get_description(self) -> str: return super().get_description() + ", Sugar"
class CoffeeWithMilkAndSugar(Coffee): """Coffee with milk and sugar""" def get_cost(self) -> float: return super().get_cost() + 1.0 + 0.5
def get_description(self) -> str: return super().get_description() + ", Milk, Sugar"
class CoffeeWithWhippedCream(Coffee): """Coffee with whipped cream""" def get_cost(self) -> float: return super().get_cost() + 2.0
def get_description(self) -> str: return super().get_description() + ", Whipped Cream"
# Problems:# - Need a class for every combination!# - CoffeeWithMilkAndSugarAndWhippedCream?# - CoffeeWithMilkAndWhippedCream?# - 2^3 = 8 classes for 3 add-ons!# - Violates Open/Closed Principle// ❌ Without Decorator Pattern - Subclass explosion!
public class Coffee { // Base coffee public double getCost() { return 5.0; }
public String getDescription() { return "Coffee"; }}
public class CoffeeWithMilk extends Coffee { // Coffee with milk @Override public double getCost() { return super.getCost() + 1.0; }
@Override public String getDescription() { return super.getDescription() + ", Milk"; }}
public class CoffeeWithSugar extends Coffee { // Coffee with sugar @Override public double getCost() { return super.getCost() + 0.5; }
@Override public String getDescription() { return super.getDescription() + ", Sugar"; }}
public class CoffeeWithMilkAndSugar extends Coffee { // Coffee with milk and sugar @Override public double getCost() { return super.getCost() + 1.0 + 0.5; }
@Override public String getDescription() { return super.getDescription() + ", Milk, Sugar"; }}
public class CoffeeWithWhippedCream extends Coffee { // Coffee with whipped cream @Override public double getCost() { return super.getCost() + 2.0; }
@Override public String getDescription() { return super.getDescription() + ", Whipped Cream"; }}
// Problems:// - Need a class for every combination!// - CoffeeWithMilkAndSugarAndWhippedCream?// - CoffeeWithMilkAndWhippedCream?// - 2^3 = 8 classes for 3 add-ons!// - Violates Open/Closed Principle// ❌ Without Decorator Pattern - Subclass explosion!
class Coffee { /** Base coffee */ getCost(): number { return 5.0; }
getDescription(): string { return "Coffee"; }}
class CoffeeWithMilk extends Coffee { /** Coffee with milk */ getCost(): number { return super.getCost() + 1.0; }
getDescription(): string { return super.getDescription() + ", Milk"; }}
class CoffeeWithSugar extends Coffee { /** Coffee with sugar */ getCost(): number { return super.getCost() + 0.5; }
getDescription(): string { return super.getDescription() + ", Sugar"; }}
class CoffeeWithMilkAndSugar extends Coffee { /** Coffee with milk and sugar */ getCost(): number { return super.getCost() + 1.0 + 0.5; }
getDescription(): string { return super.getDescription() + ", Milk, Sugar"; }}
class CoffeeWithWhippedCream extends Coffee { /** Coffee with whipped cream */ getCost(): number { return super.getCost() + 2.0; }
getDescription(): string { return super.getDescription() + ", Whipped Cream"; }}
// Problems:// - Need a class for every combination!// - CoffeeWithMilkAndSugarAndWhippedCream?// - CoffeeWithMilkAndWhippedCream?// - 2^3 = 8 classes for 3 add-ons!// - Violates Open/Closed Principle// ❌ Without Decorator Pattern - Subclass explosion!
#include <string>
class Coffee {public: // Base coffee virtual double getCost() { return 5.0; }
virtual std::string getDescription() { return "Coffee"; }
virtual ~Coffee() = default;};
class CoffeeWithMilk : public Coffee {public: // Coffee with milk double getCost() override { return Coffee::getCost() + 1.0; }
std::string getDescription() override { return Coffee::getDescription() + ", Milk"; }};
class CoffeeWithSugar : public Coffee {public: // Coffee with sugar double getCost() override { return Coffee::getCost() + 0.5; }
std::string getDescription() override { return Coffee::getDescription() + ", Sugar"; }};
class CoffeeWithMilkAndSugar : public Coffee {public: // Coffee with milk and sugar double getCost() override { return Coffee::getCost() + 1.0 + 0.5; }
std::string getDescription() override { return Coffee::getDescription() + ", Milk, Sugar"; }};
class CoffeeWithWhippedCream : public Coffee {public: // Coffee with whipped cream double getCost() override { return Coffee::getCost() + 2.0; }
std::string getDescription() override { return Coffee::getDescription() + ", Whipped Cream"; }};
// Problems:// - Need a class for every combination!// - CoffeeWithMilkAndSugarAndWhippedCream?// - CoffeeWithMilkAndWhippedCream?// - 2^3 = 8 classes for 3 add-ons!// - Violates Open/Closed Principle// ❌ Without Decorator Pattern - Subclass explosion!
public class Coffee{ /** Base coffee */ public virtual double GetCost() { return 5.0; }
public virtual string GetDescription() { return "Coffee"; }}
public class CoffeeWithMilk : Coffee{ /** Coffee with milk */ public override double GetCost() { return base.GetCost() + 1.0; }
public override string GetDescription() { return base.GetDescription() + ", Milk"; }}
public class CoffeeWithSugar : Coffee{ /** Coffee with sugar */ public override double GetCost() { return base.GetCost() + 0.5; }
public override string GetDescription() { return base.GetDescription() + ", Sugar"; }}
public class CoffeeWithMilkAndSugar : Coffee{ /** Coffee with milk and sugar */ public override double GetCost() { return base.GetCost() + 1.0 + 0.5; }
public override string GetDescription() { return base.GetDescription() + ", Milk, Sugar"; }}
public class CoffeeWithWhippedCream : Coffee{ /** Coffee with whipped cream */ public override double GetCost() { return base.GetCost() + 2.0; }
public override string GetDescription() { return base.GetDescription() + ", Whipped Cream"; }}
// Problems:// - Need a class for every combination!// - CoffeeWithMilkAndSugarAndWhippedCream?// - CoffeeWithMilkAndWhippedCream?// - 2^3 = 8 classes for 3 add-ons!// - Violates Open/Closed Principle// ❌ Without Decorator Pattern - Struct explosion!
package main
type Coffee struct{}func (c *Coffee) GetCost() float64 { return 5.0 }func (c *Coffee) GetDescription() string { return "Coffee" }
type CoffeeWithMilk struct{}func (c *CoffeeWithMilk) GetCost() float64 { return 6.0 }func (c *CoffeeWithMilk) GetDescription() string { return "Coffee, Milk" }
type CoffeeWithSugar struct{}func (c *CoffeeWithSugar) GetCost() float64 { return 5.5 }func (c *CoffeeWithSugar) GetDescription() string { return "Coffee, Sugar" }
type CoffeeWithMilkAndSugar struct{}func (c *CoffeeWithMilkAndSugar) GetCost() float64 { return 6.5 }func (c *CoffeeWithMilkAndSugar) GetDescription() string { return "Coffee, Milk, Sugar" }
// Problems:// - Need a struct for every combination!// - CoffeeWithMilkAndSugarAndWhippedCream?// - 2^3 = 8 structs for 3 add-ons!// - Violates Open/Closed Principle// The Problemstruct Coffee { milk: bool, sugar: bool,}impl Coffee { fn cost(&self) -> f64 { 2.0 + if self.milk { 0.5 } else { 0.0 } + if self.sugar { 0.2 } else { 0.0 } }}Problems:
- Subclass explosion - Need a class for every combination (2^n classes for n features)
- Hard to extend - Adding new feature requires many new classes
- Violates Open/Closed Principle - Need to modify code to extend functionality
- Can’t combine features dynamically - Combinations are fixed at compile time
The Solution: Decorator Pattern
Section titled “The Solution: Decorator Pattern”Class Structure
Section titled “Class Structure”from abc import ABC, abstractmethod
# Step 1: Define the component interfaceclass Coffee(ABC): """Component interface - what coffee should do"""
@abstractmethod def get_cost(self) -> float: """Get the cost of the coffee""" pass
@abstractmethod def get_description(self) -> str: """Get the description of the coffee""" pass
# Step 2: Implement the concrete componentclass SimpleCoffee(Coffee): """Concrete component - basic coffee"""
def get_cost(self) -> float: """Get base coffee cost""" return 5.0
def get_description(self) -> str: """Get base coffee description""" return "Coffee"
# Step 3: Create the base decoratorclass CoffeeDecorator(Coffee): """Base decorator - wraps a coffee component"""
def __init__(self, coffee: Coffee): self.coffee = coffee # Wraps the component
def get_cost(self) -> float: """Delegate to wrapped coffee""" return self.coffee.get_cost()
def get_description(self) -> str: """Delegate to wrapped coffee""" return self.coffee.get_description()
# Step 4: Implement concrete decoratorsclass MilkDecorator(CoffeeDecorator): """Concrete decorator - adds milk"""
def get_cost(self) -> float: """Add milk cost to wrapped coffee""" return self.coffee.get_cost() + 1.0
def get_description(self) -> str: """Add milk description to wrapped coffee""" return self.coffee.get_description() + ", Milk"
class SugarDecorator(CoffeeDecorator): """Concrete decorator - adds sugar"""
def get_cost(self) -> float: """Add sugar cost to wrapped coffee""" return self.coffee.get_cost() + 0.5
def get_description(self) -> str: """Add sugar description to wrapped coffee""" return self.coffee.get_description() + ", Sugar"
class WhippedCreamDecorator(CoffeeDecorator): """Concrete decorator - adds whipped cream"""
def get_cost(self) -> float: """Add whipped cream cost to wrapped coffee""" return self.coffee.get_cost() + 2.0
def get_description(self) -> str: """Add whipped cream description to wrapped coffee""" return self.coffee.get_description() + ", Whipped Cream"
# Usage - Clean and flexible!def main(): # Start with simple coffee coffee = SimpleCoffee() print(f"{coffee.get_description()}: ${coffee.get_cost():.2f}")
# Add milk coffee = MilkDecorator(coffee) print(f"{coffee.get_description()}: ${coffee.get_cost():.2f}")
# Add sugar coffee = SugarDecorator(coffee) print(f"{coffee.get_description()}: ${coffee.get_cost():.2f}")
# Add whipped cream coffee = WhippedCreamDecorator(coffee) print(f"{coffee.get_description()}: ${coffee.get_cost():.2f}")
print("\n✅ Decorator Pattern allows dynamic feature combination!")
# Can combine in any order! coffee2 = SimpleCoffee() coffee2 = SugarDecorator(coffee2) # Sugar first coffee2 = MilkDecorator(coffee2) # Then milk print(f"\nDifferent order: {coffee2.get_description()}: ${coffee2.get_cost():.2f}")
if __name__ == "__main__": main()// Step 1: Define the component interfaceinterface Coffee { // Component interface - what coffee should do double getCost(); String getDescription();}
// Step 2: Implement the concrete componentclass SimpleCoffee implements Coffee { // Concrete component - basic coffee @Override public double getCost() { // Get base coffee cost return 5.0; }
@Override public String getDescription() { // Get base coffee description return "Coffee"; }}
// Step 3: Create the base decoratorabstract class CoffeeDecorator implements Coffee { // Base decorator - wraps a coffee component protected Coffee coffee; // Wraps the component
public CoffeeDecorator(Coffee coffee) { this.coffee = coffee; }
@Override public double getCost() { // Delegate to wrapped coffee return coffee.getCost(); }
@Override public String getDescription() { // Delegate to wrapped coffee return coffee.getDescription(); }}
// Step 4: Implement concrete decoratorsclass MilkDecorator extends CoffeeDecorator { // Concrete decorator - adds milk public MilkDecorator(Coffee coffee) { super(coffee); }
@Override public double getCost() { // Add milk cost to wrapped coffee return coffee.getCost() + 1.0; }
@Override public String getDescription() { // Add milk description to wrapped coffee return coffee.getDescription() + ", Milk"; }}
class SugarDecorator extends CoffeeDecorator { // Concrete decorator - adds sugar public SugarDecorator(Coffee coffee) { super(coffee); }
@Override public double getCost() { // Add sugar cost to wrapped coffee return coffee.getCost() + 0.5; }
@Override public String getDescription() { // Add sugar description to wrapped coffee return coffee.getDescription() + ", Sugar"; }}
class WhippedCreamDecorator extends CoffeeDecorator { // Concrete decorator - adds whipped cream public WhippedCreamDecorator(Coffee coffee) { super(coffee); }
@Override public double getCost() { // Add whipped cream cost to wrapped coffee return coffee.getCost() + 2.0; }
@Override public String getDescription() { // Add whipped cream description to wrapped coffee return coffee.getDescription() + ", Whipped Cream"; }}
// Usage - Clean and flexible!public class Main { public static void main(String[] args) { // Start with simple coffee Coffee coffee = new SimpleCoffee(); System.out.println(coffee.getDescription() + ": $" + coffee.getCost());
// Add milk coffee = new MilkDecorator(coffee); System.out.println(coffee.getDescription() + ": $" + coffee.getCost());
// Add sugar coffee = new SugarDecorator(coffee); System.out.println(coffee.getDescription() + ": $" + coffee.getCost());
// Add whipped cream coffee = new WhippedCreamDecorator(coffee); System.out.println(coffee.getDescription() + ": $" + coffee.getCost());
System.out.println("\n✅ Decorator Pattern allows dynamic feature combination!");
// Can combine in any order! Coffee coffee2 = new SimpleCoffee(); coffee2 = new SugarDecorator(coffee2); // Sugar first coffee2 = new MilkDecorator(coffee2); // Then milk System.out.println("\nDifferent order: " + coffee2.getDescription() + ": $" + coffee2.getCost()); }}// Step 1: Define the component interfaceinterface Coffee { /** Component interface - what coffee should do */ getCost(): number; getDescription(): string;}
// Step 2: Implement the concrete componentclass SimpleCoffee implements Coffee { /** Concrete component - basic coffee */ getCost(): number { /** Get base coffee cost */ return 5.0; }
getDescription(): string { /** Get base coffee description */ return "Coffee"; }}
// Step 3: Create the base decoratorabstract class CoffeeDecorator implements Coffee { /** Base decorator - wraps a coffee component */ protected coffee: Coffee; // Wraps the component
constructor(coffee: Coffee) { this.coffee = coffee; }
getCost(): number { /** Delegate to wrapped coffee */ return this.coffee.getCost(); }
getDescription(): string { /** Delegate to wrapped coffee */ return this.coffee.getDescription(); }}
// Step 4: Implement concrete decoratorsclass MilkDecorator extends CoffeeDecorator { /** Concrete decorator - adds milk */ getCost(): number { /** Add milk cost to wrapped coffee */ return this.coffee.getCost() + 1.0; }
getDescription(): string { /** Add milk description to wrapped coffee */ return this.coffee.getDescription() + ", Milk"; }}
class SugarDecorator extends CoffeeDecorator { /** Concrete decorator - adds sugar */ getCost(): number { /** Add sugar cost to wrapped coffee */ return this.coffee.getCost() + 0.5; }
getDescription(): string { /** Add sugar description to wrapped coffee */ return this.coffee.getDescription() + ", Sugar"; }}
class WhippedCreamDecorator extends CoffeeDecorator { /** Concrete decorator - adds whipped cream */ getCost(): number { /** Add whipped cream cost to wrapped coffee */ return this.coffee.getCost() + 2.0; }
getDescription(): string { /** Add whipped cream description to wrapped coffee */ return this.coffee.getDescription() + ", Whipped Cream"; }}
// Usage - Clean and flexible!function main(): void { // Start with simple coffee let coffee: Coffee = new SimpleCoffee(); console.log(`${coffee.getDescription()}: $${coffee.getCost().toFixed(2)}`);
// Add milk coffee = new MilkDecorator(coffee); console.log(`${coffee.getDescription()}: $${coffee.getCost().toFixed(2)}`);
// Add sugar coffee = new SugarDecorator(coffee); console.log(`${coffee.getDescription()}: $${coffee.getCost().toFixed(2)}`);
// Add whipped cream coffee = new WhippedCreamDecorator(coffee); console.log(`${coffee.getDescription()}: $${coffee.getCost().toFixed(2)}`);
console.log("\n✅ Decorator Pattern allows dynamic feature combination!");
// Can combine in any order! let coffee2: Coffee = new SimpleCoffee(); coffee2 = new SugarDecorator(coffee2); // Sugar first coffee2 = new MilkDecorator(coffee2); // Then milk console.log(`\nDifferent order: ${coffee2.getDescription()}: $${coffee2.getCost().toFixed(2)}`);}
main();#include <iostream>#include <string>#include <memory>#include <iomanip>
// Step 1: Define the component interfaceclass Coffee {public: virtual ~Coffee() = default; // Component interface - what coffee should do virtual double getCost() const = 0; virtual std::string getDescription() const = 0;};
// Step 2: Implement the concrete componentclass SimpleCoffee : public Coffee {public: // Concrete component - basic coffee double getCost() const override { // Get base coffee cost return 5.0; }
std::string getDescription() const override { // Get base coffee description return "Coffee"; }};
// Step 3: Create the base decoratorclass CoffeeDecorator : public Coffee {protected: std::shared_ptr<Coffee> coffee; // Wraps the component
public: CoffeeDecorator(std::shared_ptr<Coffee> coffee) : coffee(coffee) {}
double getCost() const override { // Delegate to wrapped coffee return coffee->getCost(); }
std::string getDescription() const override { // Delegate to wrapped coffee return coffee->getDescription(); }};
// Step 4: Implement concrete decoratorsclass MilkDecorator : public CoffeeDecorator {public: MilkDecorator(std::shared_ptr<Coffee> coffee) : CoffeeDecorator(coffee) {}
double getCost() const override { // Add milk cost to wrapped coffee return coffee->getCost() + 1.0; }
std::string getDescription() const override { // Add milk description to wrapped coffee return coffee->getDescription() + ", Milk"; }};
class SugarDecorator : public CoffeeDecorator {public: SugarDecorator(std::shared_ptr<Coffee> coffee) : CoffeeDecorator(coffee) {}
double getCost() const override { // Add sugar cost to wrapped coffee return coffee->getCost() + 0.5; }
std::string getDescription() const override { // Add sugar description to wrapped coffee return coffee->getDescription() + ", Sugar"; }};
class WhippedCreamDecorator : public CoffeeDecorator {public: WhippedCreamDecorator(std::shared_ptr<Coffee> coffee) : CoffeeDecorator(coffee) {}
double getCost() const override { // Add whipped cream cost to wrapped coffee return coffee->getCost() + 2.0; }
std::string getDescription() const override { // Add whipped cream description to wrapped coffee return coffee->getDescription() + ", Whipped Cream"; }};
// Usage - Clean and flexible!int main() { std::cout << std::fixed << std::setprecision(2);
// Start with simple coffee std::shared_ptr<Coffee> coffee = std::make_shared<SimpleCoffee>(); std::cout << coffee->getDescription() << ": $" << coffee->getCost() << std::endl;
// Add milk coffee = std::make_shared<MilkDecorator>(coffee); std::cout << coffee->getDescription() << ": $" << coffee->getCost() << std::endl;
// Add sugar coffee = std::make_shared<SugarDecorator>(coffee); std::cout << coffee->getDescription() << ": $" << coffee->getCost() << std::endl;
// Add whipped cream coffee = std::make_shared<WhippedCreamDecorator>(coffee); std::cout << coffee->getDescription() << ": $" << coffee->getCost() << std::endl;
std::cout << "\n✅ Decorator Pattern allows dynamic feature combination!" << std::endl;
// Can combine in any order! std::shared_ptr<Coffee> coffee2 = std::make_shared<SimpleCoffee>(); coffee2 = std::make_shared<SugarDecorator>(coffee2); // Sugar first coffee2 = std::make_shared<MilkDecorator>(coffee2); // Then milk std::cout << "\nDifferent order: " << coffee2->getDescription() << ": $" << coffee2->getCost() << std::endl;
return 0;}using System;
// Step 1: Define the component interfacepublic interface ICoffee{ /** Component interface - what coffee should do */ double GetCost(); string GetDescription();}
// Step 2: Implement the concrete componentpublic class SimpleCoffee : ICoffee{ /** Concrete component - basic coffee */ public double GetCost() { /** Get base coffee cost */ return 5.0; }
public string GetDescription() { /** Get base coffee description */ return "Coffee"; }}
// Step 3: Create the base decoratorpublic abstract class CoffeeDecorator : ICoffee{ /** Base decorator - wraps a coffee component */ protected ICoffee coffee; // Wraps the component
public CoffeeDecorator(ICoffee coffee) { this.coffee = coffee; }
public virtual double GetCost() { /** Delegate to wrapped coffee */ return coffee.GetCost(); }
public virtual string GetDescription() { /** Delegate to wrapped coffee */ return coffee.GetDescription(); }}
// Step 4: Implement concrete decoratorspublic class MilkDecorator : CoffeeDecorator{ /** Concrete decorator - adds milk */ public MilkDecorator(ICoffee coffee) : base(coffee) {}
public override double GetCost() { /** Add milk cost to wrapped coffee */ return coffee.GetCost() + 1.0; }
public override string GetDescription() { /** Add milk description to wrapped coffee */ return coffee.GetDescription() + ", Milk"; }}
public class SugarDecorator : CoffeeDecorator{ /** Concrete decorator - adds sugar */ public SugarDecorator(ICoffee coffee) : base(coffee) {}
public override double GetCost() { /** Add sugar cost to wrapped coffee */ return coffee.GetCost() + 0.5; }
public override string GetDescription() { /** Add sugar description to wrapped coffee */ return coffee.GetDescription() + ", Sugar"; }}
public class WhippedCreamDecorator : CoffeeDecorator{ /** Concrete decorator - adds whipped cream */ public WhippedCreamDecorator(ICoffee coffee) : base(coffee) {}
public override double GetCost() { /** Add whipped cream cost to wrapped coffee */ return coffee.GetCost() + 2.0; }
public override string GetDescription() { /** Add whipped cream description to wrapped coffee */ return coffee.GetDescription() + ", Whipped Cream"; }}
// Usage - Clean and flexible!class Program{ static void Main() { // Start with simple coffee ICoffee coffee = new SimpleCoffee(); Console.WriteLine($"{coffee.GetDescription()}: ${coffee.GetCost():F2}");
// Add milk coffee = new MilkDecorator(coffee); Console.WriteLine($"{coffee.GetDescription()}: ${coffee.GetCost():F2}");
// Add sugar coffee = new SugarDecorator(coffee); Console.WriteLine($"{coffee.GetDescription()}: ${coffee.GetCost():F2}");
// Add whipped cream coffee = new WhippedCreamDecorator(coffee); Console.WriteLine($"{coffee.GetDescription()}: ${coffee.GetCost():F2}");
Console.WriteLine("\n✅ Decorator Pattern allows dynamic feature combination!");
// Can combine in any order! ICoffee coffee2 = new SimpleCoffee(); coffee2 = new SugarDecorator(coffee2); // Sugar first coffee2 = new MilkDecorator(coffee2); // Then milk Console.WriteLine($"\nDifferent order: {coffee2.GetDescription()}: ${coffee2.GetCost():F2}"); }}package main
import "fmt"
// Step 1: Component interfacetype Coffee interface { GetCost() float64 GetDescription() string}
// Step 2: Concrete componenttype SimpleCoffee struct{}
func (c *SimpleCoffee) GetCost() float64 { return 5.0 }func (c *SimpleCoffee) GetDescription() string { return "Coffee" }
// Step 3: Base decorator (embed via interface)type MilkDecorator struct{ coffee Coffee }
func NewMilkDecorator(c Coffee) *MilkDecorator { return &MilkDecorator{coffee: c} }func (d *MilkDecorator) GetCost() float64 { return d.coffee.GetCost() + 1.0 }func (d *MilkDecorator) GetDescription() string { return d.coffee.GetDescription() + ", Milk" }
type SugarDecorator struct{ coffee Coffee }
func NewSugarDecorator(c Coffee) *SugarDecorator { return &SugarDecorator{coffee: c} }func (d *SugarDecorator) GetCost() float64 { return d.coffee.GetCost() + 0.5 }func (d *SugarDecorator) GetDescription() string { return d.coffee.GetDescription() + ", Sugar" }
type WhippedCreamDecorator struct{ coffee Coffee }
func NewWhippedCreamDecorator(c Coffee) *WhippedCreamDecorator { return &WhippedCreamDecorator{coffee: c}}func (d *WhippedCreamDecorator) GetCost() float64 { return d.coffee.GetCost() + 2.0 }func (d *WhippedCreamDecorator) GetDescription() string { return d.coffee.GetDescription() + ", Whipped Cream" }
func main() { var coffee Coffee = &SimpleCoffee{} fmt.Printf("%s: $%.2f\n", coffee.GetDescription(), coffee.GetCost())
coffee = NewMilkDecorator(coffee) fmt.Printf("%s: $%.2f\n", coffee.GetDescription(), coffee.GetCost())
coffee = NewSugarDecorator(coffee) fmt.Printf("%s: $%.2f\n", coffee.GetDescription(), coffee.GetCost())
coffee = NewWhippedCreamDecorator(coffee) fmt.Printf("%s: $%.2f\n", coffee.GetDescription(), coffee.GetCost())
fmt.Println("\n✅ Decorator Pattern allows dynamic feature combination!")
// Different order var coffee2 Coffee = &SimpleCoffee{} coffee2 = NewSugarDecorator(coffee2) coffee2 = NewMilkDecorator(coffee2) fmt.Printf("\nDifferent order: %s: $%.2f\n", coffee2.GetDescription(), coffee2.GetCost())}// Class Structuretrait Coffee { fn cost(&self) -> f64;}struct SimpleCoffee;impl Coffee for SimpleCoffee { fn cost(&self) -> f64 { 2.0 }}struct MilkDecorator<T: Coffee> { inner: T,}impl<T: Coffee> Coffee for MilkDecorator<T> { fn cost(&self) -> f64 { self.inner.cost() + 0.5 }}Real-World Software Example: Text Formatting System
Section titled “Real-World Software Example: Text Formatting System”Now let’s see a realistic software example - a text editor that needs to apply different formatting (bold, italic, underline) to text dynamically.
The Problem
Section titled “The Problem”You’re building a text editor. You need to apply formatting (bold, italic, underline) to text. Without Decorator Pattern:
# ❌ Without Decorator Pattern - Subclass explosion!
class Text: """Base text""" def render(self) -> str: return "Hello World"
class BoldText(Text): """Bold text""" def render(self) -> str: return f"<b>{super().render()}</b>"
class ItalicText(Text): """Italic text""" def render(self) -> str: return f"<i>{super().render()}</i>"
class BoldItalicText(Text): """Bold and italic text""" def render(self) -> str: return f"<b><i>{super().render()}</i></b>"
class UnderlineText(Text): """Underlined text""" def render(self) -> str: return f"<u>{super().render()}</u>"
class BoldItalicUnderlineText(Text): """Bold, italic, and underlined text""" def render(self) -> str: return f"<b><i><u>{super().render()}</u></i></b>"
# Problems:# - Need a class for every combination!# - BoldUnderlineText?# - ItalicUnderlineText?# - 2^3 = 8 classes for 3 formats!// ❌ Without Decorator Pattern - Subclass explosion!
public class Text { // Base text public String render() { return "Hello World"; }}
public class BoldText extends Text { // Bold text @Override public String render() { return "<b>" + super.render() + "</b>"; }}
public class ItalicText extends Text { // Italic text @Override public String render() { return "<i>" + super.render() + "</i>"; }}
public class BoldItalicText extends Text { // Bold and italic text @Override public String render() { return "<b><i>" + super.render() + "</i></b>"; }}
public class UnderlineText extends Text { // Underlined text @Override public String render() { return "<u>" + super.render() + "</u>"; }}
public class BoldItalicUnderlineText extends Text { // Bold, italic, and underlined text @Override public String render() { return "<b><i><u>" + super.render() + "</u></i></b>"; }}
// Problems:// - Need a class for every combination!// - BoldUnderlineText?// - ItalicUnderlineText?// - 2^3 = 8 classes for 3 formats!// ❌ Without Decorator Pattern - Subclass explosion!
class Text { /** Base text */ render(): string { return "Hello World"; }}
class BoldText extends Text { /** Bold text */ render(): string { return `<b>${super.render()}</b>`; }}
class ItalicText extends Text { /** Italic text */ render(): string { return `<i>${super.render()}</i>`; }}
class BoldItalicText extends Text { /** Bold and italic text */ render(): string { return `<b><i>${super.render()}</i></b>`; }}
class UnderlineText extends Text { /** Underlined text */ render(): string { return `<u>${super.render()}</u>`; }}
class BoldItalicUnderlineText extends Text { /** Bold, italic, and underlined text */ render(): string { return `<b><i><u>${super.render()}</u></i></b>`; }}
// Problems:// - Need a class for every combination!// - BoldUnderlineText?// - ItalicUnderlineText?// - 2^3 = 8 classes for 3 formats!// ❌ Without Decorator Pattern - Subclass explosion!
#include <string>
class Text {public: // Base text virtual std::string render() { return "Hello World"; }
virtual ~Text() = default;};
class BoldText : public Text {public: // Bold text std::string render() override { return "<b>" + Text::render() + "</b>"; }};
class ItalicText : public Text {public: // Italic text std::string render() override { return "<i>" + Text::render() + "</i>"; }};
class BoldItalicText : public Text {public: // Bold and italic text std::string render() override { return "<b><i>" + Text::render() + "</i></b>"; }};
class UnderlineText : public Text {public: // Underlined text std::string render() override { return "<u>" + Text::render() + "</u>"; }};
class BoldItalicUnderlineText : public Text {public: // Bold, italic, and underlined text std::string render() override { return "<b><i><u>" + Text::render() + "</u></i></b>"; }};
// Problems:// - Need a class for every combination!// - BoldUnderlineText?// - ItalicUnderlineText?// - 2^3 = 8 classes for 3 formats!// ❌ Without Decorator Pattern - Subclass explosion!
public class Text{ /** Base text */ public virtual string Render() { return "Hello World"; }}
public class BoldText : Text{ /** Bold text */ public override string Render() { return $"<b>{base.Render()}</b>"; }}
public class ItalicText : Text{ /** Italic text */ public override string Render() { return $"<i>{base.Render()}</i>"; }}
public class BoldItalicText : Text{ /** Bold and italic text */ public override string Render() { return $"<b><i>{base.Render()}</i></b>"; }}
public class UnderlineText : Text{ /** Underlined text */ public override string Render() { return $"<u>{base.Render()}</u>"; }}
public class BoldItalicUnderlineText : Text{ /** Bold, italic, and underlined text */ public override string Render() { return $"<b><i><u>{base.Render()}</u></i></b>"; }}
// Problems:// - Need a class for every combination!// - BoldUnderlineText?// - ItalicUnderlineText?// - 2^3 = 8 classes for 3 formats!// ❌ Without Decorator Pattern - Struct explosion!
package main
type Text struct{}func (t *Text) Render() string { return "Hello World" }
type BoldText struct{}func (t *BoldText) Render() string { return "<b>Hello World</b>" }
type ItalicText struct{}func (t *ItalicText) Render() string { return "<i>Hello World</i>" }
type BoldItalicText struct{}func (t *BoldItalicText) Render() string { return "<b><i>Hello World</i></b>" }
type UnderlineText struct{}func (t *UnderlineText) Render() string { return "<u>Hello World</u>" }
// Problems:// - Need a struct for every combination!// - BoldUnderlineText? ItalicUnderlineText?// - 2^3 = 8 structs for 3 formats!// The Problemstruct Coffee { milk: bool, sugar: bool,}impl Coffee { fn cost(&self) -> f64 { 2.0 + if self.milk { 0.5 } else { 0.0 } + if self.sugar { 0.2 } else { 0.0 } }}Problems:
- Subclass explosion - Need a class for every combination
- Hard to extend - Adding new format requires many new classes
- Can’t combine dynamically - Combinations are fixed at compile time
The Solution: Decorator Pattern
Section titled “The Solution: Decorator Pattern”from abc import ABC, abstractmethod
# Step 1: Define the component interfaceclass TextComponent(ABC): """Component interface - what text should do"""
@abstractmethod def render(self) -> str: """Render the text""" pass
# Step 2: Implement the concrete componentclass PlainText(TextComponent): """Concrete component - plain text"""
def __init__(self, content: str): self.content = content
def render(self) -> str: """Render plain text""" return self.content
# Step 3: Create the base decoratorclass TextDecorator(TextComponent): """Base decorator - wraps a text component"""
def __init__(self, text: TextComponent): self.text = text # Wraps the component
def render(self) -> str: """Delegate to wrapped text""" return self.text.render()
# Step 4: Implement concrete decoratorsclass BoldDecorator(TextDecorator): """Concrete decorator - adds bold formatting"""
def render(self) -> str: """Add bold tags to wrapped text""" return f"<b>{self.text.render()}</b>"
class ItalicDecorator(TextDecorator): """Concrete decorator - adds italic formatting"""
def render(self) -> str: """Add italic tags to wrapped text""" return f"<i>{self.text.render()}</i>"
class UnderlineDecorator(TextDecorator): """Concrete decorator - adds underline formatting"""
def render(self) -> str: """Add underline tags to wrapped text""" return f"<u>{self.text.render()}</u>"
# Usage - Clean and flexible!def main(): # Start with plain text text = PlainText("Hello World") print(f"Plain: {text.render()}")
# Add bold text = BoldDecorator(text) print(f"Bold: {text.render()}")
# Add italic text = ItalicDecorator(text) print(f"Bold + Italic: {text.render()}")
# Add underline text = UnderlineDecorator(text) print(f"Bold + Italic + Underline: {text.render()}")
print("\n✅ Decorator Pattern allows dynamic formatting combination!")
# Can combine in any order! text2 = PlainText("Python") text2 = ItalicDecorator(text2) # Italic first text2 = BoldDecorator(text2) # Then bold print(f"\nDifferent order: {text2.render()}")
if __name__ == "__main__": main()// Step 1: Define the component interfaceinterface TextComponent { // Component interface - what text should do String render();}
// Step 2: Implement the concrete componentclass PlainText implements TextComponent { // Concrete component - plain text private String content;
public PlainText(String content) { this.content = content; }
@Override public String render() { // Render plain text return content; }}
// Step 3: Create the base decoratorabstract class TextDecorator implements TextComponent { // Base decorator - wraps a text component protected TextComponent text; // Wraps the component
public TextDecorator(TextComponent text) { this.text = text; }
@Override public String render() { // Delegate to wrapped text return text.render(); }}
// Step 4: Implement concrete decoratorsclass BoldDecorator extends TextDecorator { // Concrete decorator - adds bold formatting public BoldDecorator(TextComponent text) { super(text); }
@Override public String render() { // Add bold tags to wrapped text return "<b>" + text.render() + "</b>"; }}
class ItalicDecorator extends TextDecorator { // Concrete decorator - adds italic formatting public ItalicDecorator(TextComponent text) { super(text); }
@Override public String render() { // Add italic tags to wrapped text return "<i>" + text.render() + "</i>"; }}
class UnderlineDecorator extends TextDecorator { // Concrete decorator - adds underline formatting public UnderlineDecorator(TextComponent text) { super(text); }
@Override public String render() { // Add underline tags to wrapped text return "<u>" + text.render() + "</u>"; }}
// Usage - Clean and flexible!public class Main { public static void main(String[] args) { // Start with plain text TextComponent text = new PlainText("Hello World"); System.out.println("Plain: " + text.render());
// Add bold text = new BoldDecorator(text); System.out.println("Bold: " + text.render());
// Add italic text = new ItalicDecorator(text); System.out.println("Bold + Italic: " + text.render());
// Add underline text = new UnderlineDecorator(text); System.out.println("Bold + Italic + Underline: " + text.render());
System.out.println("\n✅ Decorator Pattern allows dynamic formatting combination!");
// Can combine in any order! TextComponent text2 = new PlainText("Python"); text2 = new ItalicDecorator(text2); // Italic first text2 = new BoldDecorator(text2); // Then bold System.out.println("\nDifferent order: " + text2.render()); }}// Step 1: Define the component interfaceinterface TextComponent { /** Component interface - what text should do */ render(): string;}
// Step 2: Implement the concrete componentclass PlainText implements TextComponent { /** Concrete component - plain text */ private content: string;
constructor(content: string) { this.content = content; }
render(): string { /** Render plain text */ return this.content; }}
// Step 3: Create the base decoratorabstract class TextDecorator implements TextComponent { /** Base decorator - wraps a text component */ protected text: TextComponent; // Wraps the component
constructor(text: TextComponent) { this.text = text; }
render(): string { /** Delegate to wrapped text */ return this.text.render(); }}
// Step 4: Implement concrete decoratorsclass BoldDecorator extends TextDecorator { /** Concrete decorator - adds bold formatting */ render(): string { /** Add bold tags to wrapped text */ return `<b>${this.text.render()}</b>`; }}
class ItalicDecorator extends TextDecorator { /** Concrete decorator - adds italic formatting */ render(): string { /** Add italic tags to wrapped text */ return `<i>${this.text.render()}</i>`; }}
class UnderlineDecorator extends TextDecorator { /** Concrete decorator - adds underline formatting */ render(): string { /** Add underline tags to wrapped text */ return `<u>${this.text.render()}</u>`; }}
// Usage - Clean and flexible!function main(): void { // Start with plain text let text: TextComponent = new PlainText("Hello World"); console.log("Plain: " + text.render());
// Add bold text = new BoldDecorator(text); console.log("Bold: " + text.render());
// Add italic text = new ItalicDecorator(text); console.log("Bold + Italic: " + text.render());
// Add underline text = new UnderlineDecorator(text); console.log("Bold + Italic + Underline: " + text.render());
console.log("\n✅ Decorator Pattern allows dynamic formatting combination!");
// Can combine in any order! let text2: TextComponent = new PlainText("Python"); text2 = new ItalicDecorator(text2); // Italic first text2 = new BoldDecorator(text2); // Then bold console.log("\nDifferent order: " + text2.render());}
main();#include <iostream>#include <string>#include <memory>
// Step 1: Define the component interfaceclass TextComponent {public: virtual ~TextComponent() = default; // Component interface - what text should do virtual std::string render() const = 0;};
// Step 2: Implement the concrete componentclass PlainText : public TextComponent {private: std::string content;
public: PlainText(const std::string& content) : content(content) {}
std::string render() const override { // Render plain text return content; }};
// Step 3: Create the base decoratorclass TextDecorator : public TextComponent {protected: std::shared_ptr<TextComponent> text; // Wraps the component
public: TextDecorator(std::shared_ptr<TextComponent> text) : text(text) {}
std::string render() const override { // Delegate to wrapped text return text->render(); }};
// Step 4: Implement concrete decoratorsclass BoldDecorator : public TextDecorator {public: BoldDecorator(std::shared_ptr<TextComponent> text) : TextDecorator(text) {}
std::string render() const override { // Add bold tags to wrapped text return "<b>" + text->render() + "</b>"; }};
class ItalicDecorator : public TextDecorator {public: ItalicDecorator(std::shared_ptr<TextComponent> text) : TextDecorator(text) {}
std::string render() const override { // Add italic tags to wrapped text return "<i>" + text->render() + "</i>"; }};
class UnderlineDecorator : public TextDecorator {public: UnderlineDecorator(std::shared_ptr<TextComponent> text) : TextDecorator(text) {}
std::string render() const override { // Add underline tags to wrapped text return "<u>" + text->render() + "</u>"; }};
// Usage - Clean and flexible!int main() { // Start with plain text std::shared_ptr<TextComponent> text = std::make_shared<PlainText>("Hello World"); std::cout << "Plain: " << text->render() << std::endl;
// Add bold text = std::make_shared<BoldDecorator>(text); std::cout << "Bold: " << text->render() << std::endl;
// Add italic text = std::make_shared<ItalicDecorator>(text); std::cout << "Bold + Italic: " << text->render() << std::endl;
// Add underline text = std::make_shared<UnderlineDecorator>(text); std::cout << "Bold + Italic + Underline: " << text->render() << std::endl;
std::cout << "\n✅ Decorator Pattern allows dynamic formatting combination!" << std::endl;
// Can combine in any order! std::shared_ptr<TextComponent> text2 = std::make_shared<PlainText>("Python"); text2 = std::make_shared<ItalicDecorator>(text2); // Italic first text2 = std::make_shared<BoldDecorator>(text2); // Then bold std::cout << "\nDifferent order: " << text2->render() << std::endl;
return 0;}using System;
// Step 1: Define the component interfacepublic interface ITextComponent{ /** Component interface - what text should do */ string Render();}
// Step 2: Implement the concrete componentpublic class PlainText : ITextComponent{ /** Concrete component - plain text */ private string content;
public PlainText(string content) { this.content = content; }
public string Render() { /** Render plain text */ return content; }}
// Step 3: Create the base decoratorpublic abstract class TextDecorator : ITextComponent{ /** Base decorator - wraps a text component */ protected ITextComponent text; // Wraps the component
public TextDecorator(ITextComponent text) { this.text = text; }
public virtual string Render() { /** Delegate to wrapped text */ return text.Render(); }}
// Step 4: Implement concrete decoratorspublic class BoldDecorator : TextDecorator{ /** Concrete decorator - adds bold formatting */ public BoldDecorator(ITextComponent text) : base(text) {}
public override string Render() { /** Add bold tags to wrapped text */ return $"<b>{text.Render()}</b>"; }}
public class ItalicDecorator : TextDecorator{ /** Concrete decorator - adds italic formatting */ public ItalicDecorator(ITextComponent text) : base(text) {}
public override string Render() { /** Add italic tags to wrapped text */ return $"<i>{text.Render()}</i>"; }}
public class UnderlineDecorator : TextDecorator{ /** Concrete decorator - adds underline formatting */ public UnderlineDecorator(ITextComponent text) : base(text) {}
public override string Render() { /** Add underline tags to wrapped text */ return $"<u>{text.Render()}</u>"; }}
// Usage - Clean and flexible!class Program{ static void Main() { // Start with plain text ITextComponent text = new PlainText("Hello World"); Console.WriteLine("Plain: " + text.Render());
// Add bold text = new BoldDecorator(text); Console.WriteLine("Bold: " + text.Render());
// Add italic text = new ItalicDecorator(text); Console.WriteLine("Bold + Italic: " + text.Render());
// Add underline text = new UnderlineDecorator(text); Console.WriteLine("Bold + Italic + Underline: " + text.Render());
Console.WriteLine("\n✅ Decorator Pattern allows dynamic formatting combination!");
// Can combine in any order! ITextComponent text2 = new PlainText("Python"); text2 = new ItalicDecorator(text2); // Italic first text2 = new BoldDecorator(text2); // Then bold Console.WriteLine("\nDifferent order: " + text2.Render()); }}package main
import "fmt"
// Step 1: Component interfacetype TextComponent interface { Render() string}
// Step 2: Concrete componenttype PlainText struct{ content string }
func NewPlainText(content string) *PlainText { return &PlainText{content: content} }func (t *PlainText) Render() string { return t.content }
// Step 4: Concrete decoratorstype BoldDecorator struct{ text TextComponent }
func NewBoldDecorator(t TextComponent) *BoldDecorator { return &BoldDecorator{text: t} }func (d *BoldDecorator) Render() string { return "<b>" + d.text.Render() + "</b>" }
type ItalicDecorator struct{ text TextComponent }
func NewItalicDecorator(t TextComponent) *ItalicDecorator { return &ItalicDecorator{text: t} }func (d *ItalicDecorator) Render() string { return "<i>" + d.text.Render() + "</i>" }
type UnderlineDecorator struct{ text TextComponent }
func NewUnderlineDecorator(t TextComponent) *UnderlineDecorator { return &UnderlineDecorator{text: t}}func (d *UnderlineDecorator) Render() string { return "<u>" + d.text.Render() + "</u>" }
func main() { var text TextComponent = NewPlainText("Hello World") fmt.Println("Plain: " + text.Render())
text = NewBoldDecorator(text) fmt.Println("Bold: " + text.Render())
text = NewItalicDecorator(text) fmt.Println("Bold + Italic: " + text.Render())
text = NewUnderlineDecorator(text) fmt.Println("Bold + Italic + Underline: " + text.Render())
fmt.Println("\n✅ Decorator Pattern allows dynamic formatting combination!")
var text2 TextComponent = NewPlainText("Python") text2 = NewItalicDecorator(text2) text2 = NewBoldDecorator(text2) fmt.Println("\nDifferent order: " + text2.Render())}// The Solution: Decorator Patterntrait Coffee { fn cost(&self) -> f64;}struct SimpleCoffee;impl Coffee for SimpleCoffee { fn cost(&self) -> f64 { 2.0 }}struct MilkDecorator<T: Coffee> { inner: T,}impl<T: Coffee> Coffee for MilkDecorator<T> { fn cost(&self) -> f64 { self.inner.cost() + 0.5 }}Decorator Pattern Variants
Section titled “Decorator Pattern Variants”There are different ways to implement the Decorator Pattern:
1. Transparent Decorator (Preferred)
Section titled “1. Transparent Decorator (Preferred)”Decorator implements the same interface as component, client doesn’t know it’s decorated:
# Transparent Decorator - same interfaceclass Component: def operation(self): pass
class ConcreteComponent(Component): def operation(self): return "Component"
class Decorator(Component): def __init__(self, component: Component): self.component = component
def operation(self): return self.component.operation()
# Client treats decorator same as componentcomponent = ConcreteComponent()decorated = Decorator(component)# Both have same interface - transparent!// Transparent Decorator - same interfaceinterface Component { String operation();}
class ConcreteComponent implements Component { @Override public String operation() { return "Component"; }}
class Decorator implements Component { protected Component component;
public Decorator(Component component) { this.component = component; }
@Override public String operation() { return component.operation(); }}
// Client treats decorator same as componentComponent component = new ConcreteComponent();Component decorated = new Decorator(component);// Both have same interface - transparent!// Transparent Decorator - same interfaceinterface Component { operation(): string;}
class ConcreteComponent implements Component { operation(): string { return "Component"; }}
class Decorator implements Component { protected component: Component;
constructor(component: Component) { this.component = component; }
operation(): string { return this.component.operation(); }}
// Client treats decorator same as componentconst component: Component = new ConcreteComponent();const decorated: Component = new Decorator(component);// Both have same interface - transparent!#include <string>#include <memory>
// Transparent Decorator - same interfaceclass Component {public: virtual ~Component() = default; virtual std::string operation() = 0;};
class ConcreteComponent : public Component {public: std::string operation() override { return "Component"; }};
class Decorator : public Component {protected: std::shared_ptr<Component> component;
public: Decorator(std::shared_ptr<Component> component) : component(component) {}
std::string operation() override { return component->operation(); }};
// Client treats decorator same as component// std::shared_ptr<Component> component = std::make_shared<ConcreteComponent>();// std::shared_ptr<Component> decorated = std::make_shared<Decorator>(component);// Both have same interface - transparent!// Transparent Decorator - same interfacepublic interface IComponent{ string Operation();}
public class ConcreteComponent : IComponent{ public string Operation() { return "Component"; }}
public class Decorator : IComponent{ protected IComponent component;
public Decorator(IComponent component) { this.component = component; }
public string Operation() { return component.Operation(); }}
// Client treats decorator same as component// IComponent component = new ConcreteComponent();// IComponent decorated = new Decorator(component);// Both have same interface - transparent!package main
// Transparent Decorator - same interfacetype Component interface { Operation() string}
type ConcreteComponent struct{}
func (c *ConcreteComponent) Operation() string { return "Component" }
type Decorator struct { component Component}
func NewDecorator(c Component) *Decorator { return &Decorator{component: c} }func (d *Decorator) Operation() string { return d.component.Operation() }
// Client treats decorator same as component// var component Component = &ConcreteComponent{}// var decorated Component = NewDecorator(component)// Both have same interface - transparent!// 1. Transparent Decorator Preferredtrait Coffee { fn cost(&self) -> f64;}struct SimpleCoffee;impl Coffee for SimpleCoffee { fn cost(&self) -> f64 { 2.0 }}struct MilkDecorator<T: Coffee> { inner: T,}impl<T: Coffee> Coffee for MilkDecorator<T> { fn cost(&self) -> f64 { self.inner.cost() + 0.5 }}Pros: Client doesn’t need to know about decorators, uniform interface
Cons: Can’t access decorator-specific methods
2. Opaque Decorator
Section titled “2. Opaque Decorator”Decorator adds new methods, client needs to know it’s decorated:
# Opaque Decorator - adds new methodsclass Component: def operation(self): pass
class Decorator(Component): def __init__(self, component: Component): self.component = component
def operation(self): return self.component.operation()
def extra_operation(self): # New method return "Extra"
# Client needs to know it's decorated to use extra_operationdecorated = Decorator(component)decorated.extra_operation() # Only works if client knows it's Decorator// Opaque Decorator - adds new methodsinterface Component { String operation();}
class Decorator implements Component { protected Component component;
public Decorator(Component component) { this.component = component; }
@Override public String operation() { return component.operation(); }
public String extraOperation() { // New method return "Extra"; }}
// Client needs to know it's decorated to use extraOperationDecorator decorated = new Decorator(component);decorated.extraOperation(); // Only works if client knows it's Decorator// Opaque Decorator - adds new methodsinterface Component { operation(): string;}
class Decorator implements Component { protected component: Component;
constructor(component: Component) { this.component = component; }
operation(): string { return this.component.operation(); }
extraOperation(): string { // New method return "Extra"; }}
// Client needs to know it's decorated to use extraOperationconst decorated = new Decorator(component);decorated.extraOperation(); // Only works if client knows it's Decorator#include <string>#include <memory>
// Opaque Decorator - adds new methodsclass Component {public: virtual ~Component() = default; virtual std::string operation() = 0;};
class Decorator : public Component {protected: std::shared_ptr<Component> component;
public: Decorator(std::shared_ptr<Component> component) : component(component) {}
std::string operation() override { return component->operation(); }
std::string extraOperation() { // New method return "Extra"; }};
// Client needs to know it's decorated to use extraOperation// auto decorated = std::make_shared<Decorator>(component);// decorated->extraOperation(); // Only works if client knows it's Decorator// Opaque Decorator - adds new methodspublic interface IComponent{ string Operation();}
public class Decorator : IComponent{ protected IComponent component;
public Decorator(IComponent component) { this.component = component; }
public string Operation() { return component.Operation(); }
public string ExtraOperation() // New method { return "Extra"; }}
// Client needs to know it's decorated to use ExtraOperation// Decorator decorated = new Decorator(component);// decorated.ExtraOperation(); // Only works if client knows it's Decoratorpackage main
// Opaque Decorator - adds new methodstype OpaqueComponent interface { Operation() string}
type OpaqueDecorator struct { component OpaqueComponent}
func NewOpaqueDecorator(c OpaqueComponent) *OpaqueDecorator { return &OpaqueDecorator{component: c}}
func (d *OpaqueDecorator) Operation() string { return d.component.Operation() }
func (d *OpaqueDecorator) ExtraOperation() string { return "Extra" } // New method
// Client needs to know it's decorated to use ExtraOperation// decorated := NewOpaqueDecorator(component)// decorated.ExtraOperation() // Only works if client knows it's OpaqueDecorator// 2. Opaque Decoratortrait Coffee { fn cost(&self) -> f64;}struct SimpleCoffee;impl Coffee for SimpleCoffee { fn cost(&self) -> f64 { 2.0 }}struct MilkDecorator<T: Coffee> { inner: T,}impl<T: Coffee> Coffee for MilkDecorator<T> { fn cost(&self) -> f64 { self.inner.cost() + 0.5 }}Pros: Can add decorator-specific functionality
Cons: Client needs to know about decorators, breaks transparency
When to Use Decorator Pattern?
Section titled “When to Use Decorator Pattern?”Use Decorator Pattern when:
✅ You want to add behavior dynamically - At runtime, not compile time
✅ You want to add behavior to individual objects - Not entire classes
✅ You want to avoid subclass explosion - Too many combinations to subclass
✅ You want flexible extension - Add features without modifying existing code
✅ You want to combine features - Multiple decorators can be combined
When NOT to Use Decorator Pattern?
Section titled “When NOT to Use Decorator Pattern?”Don’t use Decorator Pattern when:
❌ Simple inheritance works - If you only have a few combinations, inheritance might be simpler
❌ Performance critical - Decorator adds indirection (usually negligible)
❌ Over-engineering - Don’t add complexity for simple cases
❌ Feature order matters - If decorator order affects behavior significantly
Common Mistakes to Avoid
Section titled “Common Mistakes to Avoid”Mistake 1: Confusing Decorator with Adapter
Section titled “Mistake 1: Confusing Decorator with Adapter”# ❌ Confusing Decorator with Adapter
# Decorator Pattern: Adds behavior dynamically# - Wraps object to add functionality# - Same interface, enhanced behavior# - Can combine multiple decorators
# Adapter Pattern: Makes incompatible interfaces compatible# - Adapts interface to work together# - Different interface, same functionality# - About compatibility, not enhancement
# Example: Decoratorclass Coffee: def get_cost(self): return 5.0
class MilkDecorator(Coffee): def __init__(self, coffee: Coffee): self.coffee = coffee # Wraps to add behavior
def get_cost(self): return self.coffee.get_cost() + 1.0 # Adds functionality
# Example: Adapterclass OldInterface: def old_method(self): return "old"
class Adapter: def __init__(self, old: OldInterface): self.old = old # Adapts to make compatible
def new_method(self): # Different interface! return self.old.old_method() # Makes compatible// ❌ Confusing Decorator with Adapter
// Decorator Pattern: Adds behavior dynamically// - Wraps object to add functionality// - Same interface, enhanced behavior// - Can combine multiple decorators
// Adapter Pattern: Makes incompatible interfaces compatible// - Adapts interface to work together// - Different interface, same functionality// - About compatibility, not enhancement
// Example: Decoratorinterface Coffee { double getCost();}
class MilkDecorator implements Coffee { private Coffee coffee; // Wraps to add behavior
public MilkDecorator(Coffee coffee) { this.coffee = coffee; }
@Override public double getCost() { return coffee.getCost() + 1.0; // Adds functionality }}
// Example: Adapterclass OldInterface { public String oldMethod() { return "old"; }}
class Adapter { private OldInterface old; // Adapts to make compatible
public Adapter(OldInterface old) { this.old = old; }
public String newMethod() { // Different interface! return old.oldMethod(); // Makes compatible }}// ❌ Confusing Decorator with Adapter
// Decorator Pattern: Adds behavior dynamically// - Wraps object to add functionality// - Same interface, enhanced behavior// - Can combine multiple decorators
// Adapter Pattern: Makes incompatible interfaces compatible// - Adapts interface to work together// - Different interface, same functionality// - About compatibility, not enhancement
// Example: Decoratorclass Coffee { getCost(): number { return 5.0; }}
class MilkDecorator extends Coffee { private coffee: Coffee;
constructor(coffee: Coffee) { super(); this.coffee = coffee; // Wraps to add behavior }
getCost(): number { return this.coffee.getCost() + 1.0; // Adds functionality }}
// Example: Adapterclass OldInterface { oldMethod(): string { return "old"; }}
class Adapter { private old: OldInterface; // Adapts to make compatible
constructor(old: OldInterface) { this.old = old; }
newMethod(): string { // Different interface! return this.old.oldMethod(); // Makes compatible }}#include <string>
// ❌ Confusing Decorator with Adapter
// Decorator Pattern: Adds behavior dynamically// - Wraps object to add functionality// - Same interface, enhanced behavior// - Can combine multiple decorators
// Adapter Pattern: Makes incompatible interfaces compatible// - Adapts interface to work together// - Different interface, same functionality// - About compatibility, not enhancement
// Example: Decoratorclass Coffee {public: virtual double getCost() { return 5.0; } virtual ~Coffee() = default;};
class MilkDecorator : public Coffee {private: Coffee* coffee; // Wraps to add behavior
public: MilkDecorator(Coffee* coffee) : coffee(coffee) {}
double getCost() override { return coffee->getCost() + 1.0; // Adds functionality }};
// Example: Adapterclass OldInterface {public: std::string oldMethod() { return "old"; }};
class Adapter {private: OldInterface* old; // Adapts to make compatible
public: Adapter(OldInterface* old) : old(old) {}
std::string newMethod() { // Different interface! return old->oldMethod(); // Makes compatible }};// ❌ Confusing Decorator with Adapter
// Decorator Pattern: Adds behavior dynamically// - Wraps object to add functionality// - Same interface, enhanced behavior// - Can combine multiple decorators
// Adapter Pattern: Makes incompatible interfaces compatible// - Adapts interface to work together// - Different interface, same functionality// - About compatibility, not enhancement
// Example: Decoratorpublic class Coffee{ public virtual double GetCost() { return 5.0; }}
public class MilkDecorator : Coffee{ private Coffee coffee; // Wraps to add behavior
public MilkDecorator(Coffee coffee) { this.coffee = coffee; }
public override double GetCost() { return coffee.GetCost() + 1.0; // Adds functionality }}
// Example: Adapterpublic class OldInterface{ public string OldMethod() { return "old"; }}
public class Adapter{ private OldInterface old; // Adapts to make compatible
public Adapter(OldInterface old) { this.old = old; }
public string NewMethod() // Different interface! { return old.OldMethod(); // Makes compatible }}package main
// Decorator Pattern: Adds behavior dynamically// - Wraps object to add functionality// - Same interface, enhanced behavior
// Adapter Pattern: Makes incompatible interfaces compatible// - Adapts interface to work together// - Different interface, same functionality
// Example: Decorator (same interface, adds cost)type CoffeeDec interface{ GetCost() float64 }
type SimpleCoffeeDec struct{}func (c *SimpleCoffeeDec) GetCost() float64 { return 5.0 }
type MilkDec struct{ coffee CoffeeDec } // Wraps to add behaviorfunc (d *MilkDec) GetCost() float64 { return d.coffee.GetCost() + 1.0 }
// Example: Adapter (different interface, makes compatible)type OldInterface struct{}func (o *OldInterface) OldMethod() string { return "old" }
type Adapter struct{ old *OldInterface } // Adapts to make compatiblefunc (a *Adapter) NewMethod() string { return a.old.OldMethod() } // Different interface!// Mistake 1: Confusing Decorator with Adapterstruct Coffee { milk: bool, sugar: bool,}impl Coffee { fn cost(&self) -> f64 { 2.0 + if self.milk { 0.5 } else { 0.0 } + if self.sugar { 0.2 } else { 0.0 } }}Mistake 2: Not Delegating to Wrapped Component
Section titled “Mistake 2: Not Delegating to Wrapped Component”# ❌ Bad: Not delegating to wrapped componentclass Decorator: def __init__(self, component): self.component = component
def operation(self): return "Decorated" # Bad: Doesn't use component!
# ✅ Good: Delegates to wrapped componentclass Decorator: def __init__(self, component): self.component = component
def operation(self): result = self.component.operation() # Good: Delegates first return f"Decorated({result})" # Then adds behavior// ❌ Bad: Not delegating to wrapped componentclass Decorator implements Component { protected Component component;
public Decorator(Component component) { this.component = component; }
@Override public String operation() { return "Decorated"; // Bad: Doesn't use component! }}
// ✅ Good: Delegates to wrapped componentclass Decorator implements Component { protected Component component;
public Decorator(Component component) { this.component = component; }
@Override public String operation() { String result = component.operation(); // Good: Delegates first return "Decorated(" + result + ")"; // Then adds behavior }}// ❌ Bad: Not delegating to wrapped componentclass BadDecorator implements Component { protected component: Component;
constructor(component: Component) { this.component = component; }
operation(): string { return "Decorated"; // Bad: Doesn't use component! }}
// ✅ Good: Delegates to wrapped componentclass GoodDecorator implements Component { protected component: Component;
constructor(component: Component) { this.component = component; }
operation(): string { const result = this.component.operation(); // Good: Delegates first return `Decorated(${result})`; // Then adds behavior }}#include <string>#include <memory>
class Component {public: virtual ~Component() = default; virtual std::string operation() = 0;};
// ❌ Bad: Not delegating to wrapped componentclass BadDecorator : public Component {protected: std::shared_ptr<Component> component;
public: BadDecorator(std::shared_ptr<Component> component) : component(component) {}
std::string operation() override { return "Decorated"; // Bad: Doesn't use component! }};
// ✅ Good: Delegates to wrapped componentclass GoodDecorator : public Component {protected: std::shared_ptr<Component> component;
public: GoodDecorator(std::shared_ptr<Component> component) : component(component) {}
std::string operation() override { std::string result = component->operation(); // Good: Delegates first return "Decorated(" + result + ")"; // Then adds behavior }};// ❌ Bad: Not delegating to wrapped componentpublic class BadDecorator : IComponent{ protected IComponent component;
public BadDecorator(IComponent component) { this.component = component; }
public string Operation() { return "Decorated"; // Bad: Doesn't use component! }}
// ✅ Good: Delegates to wrapped componentpublic class GoodDecorator : IComponent{ protected IComponent component;
public GoodDecorator(IComponent component) { this.component = component; }
public string Operation() { string result = component.Operation(); // Good: Delegates first return $"Decorated({result})"; // Then adds behavior }}package main
type DecComponent interface{ Operation() string }
// ❌ Bad: Not delegating to wrapped componenttype BadDecDecorator struct{ component DecComponent }
func (d *BadDecDecorator) Operation() string { return "Decorated" // Bad: Doesn't use component!}
// ✅ Good: Delegates to wrapped componenttype GoodDecDecorator struct{ component DecComponent }
func (d *GoodDecDecorator) Operation() string { result := d.component.Operation() // Good: Delegates first return "Decorated(" + result + ")" // Then adds behavior}// Mistake 2: Not Delegating to Wrapped Componentstruct Coffee { milk: bool, sugar: bool,}impl Coffee { fn cost(&self) -> f64 { 2.0 + if self.milk { 0.5 } else { 0.0 } + if self.sugar { 0.2 } else { 0.0 } }}Mistake 3: Modifying Component Instead of Decorating
Section titled “Mistake 3: Modifying Component Instead of Decorating”# ❌ Bad: Modifying component directlyclass Component: def __init__(self): self.features = [] # Bad: Storing features in component
def add_feature(self, feature): self.features.append(feature) # Bad: Modifying component
# ✅ Good: Using decoratorclass Component: def operation(self): return "Component"
class Decorator(Component): def __init__(self, component): self.component = component # Good: Wraps component
def operation(self): return self.component.operation() + " + Feature" # Good: Adds behavior// ❌ Bad: Modifying component directlyclass Component { private List<String> features = new ArrayList<>(); // Bad: Storing features in component
public void addFeature(String feature) { features.add(feature); // Bad: Modifying component }}
// ✅ Good: Using decoratorinterface Component { String operation();}
class Decorator implements Component { protected Component component; // Good: Wraps component
public Decorator(Component component) { this.component = component; }
@Override public String operation() { return component.operation() + " + Feature"; // Good: Adds behavior }}// ❌ Bad: Modifying component directlyclass BadComponent { private features: string[] = []; // Bad: Storing features in component
addFeature(feature: string): void { this.features.push(feature); // Bad: Modifying component }}
// ✅ Good: Using decoratorinterface Component { operation(): string;}
class GoodComponent implements Component { operation(): string { return "Component"; }}
class Decorator implements Component { protected component: Component; // Good: Wraps component
constructor(component: Component) { this.component = component; }
operation(): string { return this.component.operation() + " + Feature"; // Good: Adds behavior }}#include <string>#include <vector>#include <memory>
// ❌ Bad: Modifying component directlyclass BadComponent {private: std::vector<std::string> features; // Bad: Storing features in component
public: void addFeature(const std::string& feature) { features.push_back(feature); // Bad: Modifying component }};
// ✅ Good: Using decoratorclass Component {public: virtual ~Component() = default; virtual std::string operation() = 0;};
class GoodComponent : public Component {public: std::string operation() override { return "Component"; }};
class Decorator : public Component {protected: std::shared_ptr<Component> component; // Good: Wraps component
public: Decorator(std::shared_ptr<Component> component) : component(component) {}
std::string operation() override { return component->operation() + " + Feature"; // Good: Adds behavior }};using System.Collections.Generic;
// ❌ Bad: Modifying component directlypublic class BadComponent{ private List<string> features = new List<string>(); // Bad: Storing features in component
public void AddFeature(string feature) { features.Add(feature); // Bad: Modifying component }}
// ✅ Good: Using decoratorpublic interface IComponent{ string Operation();}
public class GoodComponent : IComponent{ public string Operation() { return "Component"; }}
public class Decorator : IComponent{ protected IComponent component; // Good: Wraps component
public Decorator(IComponent component) { this.component = component; }
public string Operation() { return component.Operation() + " + Feature"; // Good: Adds behavior }}package main
// ❌ Bad: Storing features directly in componenttype BadComponent struct { features []string // Bad: Modifying component}
func (c *BadComponent) AddFeature(feature string) { c.features = append(c.features, feature) // Bad!}
// ✅ Good: Using decoratortype GoodBaseComponent interface { Operation() string}
type GoodConcreteComponent struct{}
func (c *GoodConcreteComponent) Operation() string { return "Component" }
type GoodDecorator struct { component GoodBaseComponent // Good: Wraps component}
func (d *GoodDecorator) Operation() string { return d.component.Operation() + " + Feature" // Good: Adds behavior}// Mistake 3: Modifying Component Instead of Decoratingstruct Coffee { milk: bool, sugar: bool,}impl Coffee { fn cost(&self) -> f64 { 2.0 + if self.milk { 0.5 } else { 0.0 } + if self.sugar { 0.2 } else { 0.0 } }}Benefits of Decorator Pattern
Section titled “Benefits of Decorator Pattern”- No Subclass Explosion - 1 base + N decorators = 1 + N classes (not 2^N)
- Dynamic Combination - Can combine decorators at runtime
- Flexible Extension - Add new decorators without modifying existing code
- Open/Closed Principle - Open for extension, closed for modification
- Single Responsibility - Each decorator adds one feature
- Runtime Behavior - Can add/remove features at runtime
Revision: Quick Catch-Up
Section titled “Revision: Quick Catch-Up”What is Decorator Pattern?
Section titled “What is Decorator Pattern?”Decorator Pattern is a structural design pattern that attaches additional responsibilities to an object dynamically. It provides a flexible alternative to subclassing for extending functionality.
Why Use It?
Section titled “Why Use It?”- ✅ Avoid subclass explosion - 1 base + N decorators = 1 + N classes (not 2^N)
- ✅ Dynamic combination - Can combine decorators at runtime
- ✅ Flexible extension - Add features without modifying code
- ✅ Open/Closed Principle - Open for extension, closed for modification
- ✅ Runtime behavior - Add/remove features at runtime
How It Works?
Section titled “How It Works?”- Define component interface - What the component should do
- Implement concrete component - Base object
- Create base decorator - Wraps component, implements same interface
- Implement concrete decorators - Each adds one feature
- Wrap dynamically - Combine decorators at runtime
Key Components
Section titled “Key Components”Component (interface)├── ConcreteComponent (implements Component)└── Decorator (implements Component, wraps Component) ├── ConcreteDecorator1 (extends Decorator) └── ConcreteDecorator2 (extends Decorator)- Component - Interface for objects that can be decorated
- ConcreteComponent - Base object
- Decorator - Base decorator that wraps component
- ConcreteDecorator - Specific decorator that adds one feature
- Client - Uses decorated components
Simple Example
Section titled “Simple Example”class Component: def operation(self): pass
class ConcreteComponent(Component): def operation(self): return "Component"
class Decorator(Component): def __init__(self, component: Component): self.component = component def operation(self): return self.component.operation()
class ConcreteDecorator(Decorator): def operation(self): return f"Decorated({self.component.operation()})"interface Component { String operation(); }
class ConcreteComponent implements Component { public String operation() { return "Component"; }}
class Decorator implements Component { protected Component component; Decorator(Component c) { component = c; } public String operation() { return component.operation(); }}
class ConcreteDecorator extends Decorator { ConcreteDecorator(Component c) { super(c); } public String operation() { return "Decorated(" + component.operation() + ")"; }}interface Component { operation(): string; }
class ConcreteComponent implements Component { operation(): string { return "Component"; }}
class Decorator implements Component { constructor(protected component: Component) {} operation(): string { return this.component.operation(); }}
class ConcreteDecorator extends Decorator { operation(): string { return `Decorated(${this.component.operation()})`; }}class Component {public: virtual std::string operation() = 0;};
class ConcreteComponent : public Component {public: std::string operation() override { return "Component"; }};
class Decorator : public Component {protected: Component* component;public: Decorator(Component* c) : component(c) {} std::string operation() override { return component->operation(); }};
class ConcreteDecorator : public Decorator {public: ConcreteDecorator(Component* c) : Decorator(c) {} std::string operation() override { return "Decorated(" + component->operation() + ")"; }};interface IComponent { string Operation(); }
class ConcreteComponent : IComponent { public string Operation() => "Component";}
class Decorator : IComponent { protected IComponent component; public Decorator(IComponent c) => component = c; public virtual string Operation() => component.Operation();}
class ConcreteDecorator : Decorator { public ConcreteDecorator(IComponent c) : base(c) {} public override string Operation() => $"Decorated({component.Operation()})";}type Component interface{ Operation() string }
type ConcreteComponent struct{}func (c *ConcreteComponent) Operation() string { return "Component" }
type Decorator struct{ component Component }func (d *Decorator) Operation() string { return d.component.Operation() }
type ConcreteDecorator struct{ Decorator }func (d *ConcreteDecorator) Operation() string { return "Decorated(" + d.component.Operation() + ")"}// Simple Exampletrait Coffee { fn cost(&self) -> f64;}struct SimpleCoffee;impl Coffee for SimpleCoffee { fn cost(&self) -> f64 { 2.0 }}struct MilkDecorator<T: Coffee> { inner: T,}impl<T: Coffee> Coffee for MilkDecorator<T> { fn cost(&self) -> f64 { self.inner.cost() + 0.5 }}When to Use?
Section titled “When to Use?”✅ Add behavior dynamically
✅ Avoid subclass explosion
✅ Combine features flexibly
✅ Extend without modifying code
✅ Add features at runtime
When NOT to Use?
Section titled “When NOT to Use?”❌ Simple inheritance works
❌ Performance critical (adds indirection)
❌ Over-engineering simple cases
❌ Feature order significantly affects behavior
Key Takeaways
Section titled “Key Takeaways”- Decorator Pattern = Adds behavior dynamically by wrapping
- Component = Interface for objects
- Decorator = Wraps component, adds behavior
- Benefit = No subclass explosion, dynamic combination
- Use Case = Many feature combinations (coffee add-ons, text formatting)
Common Pattern Structure
Section titled “Common Pattern Structure”class Component: def operation(self): pass
class ConcreteComponent(Component): def operation(self): return "Component"
class Decorator(Component): def __init__(self, component: Component): self.component = component def operation(self): return self.component.operation()
class ConcreteDecorator(Decorator): def operation(self): result = self.component.operation() return f"Enhanced({result})"interface Component { String operation(); }
class ConcreteComponent implements Component { public String operation() { return "Component"; }}
class Decorator implements Component { protected Component component; Decorator(Component c) { component = c; } public String operation() { return component.operation(); }}
class ConcreteDecorator extends Decorator { ConcreteDecorator(Component c) { super(c); } public String operation() { return "Enhanced(" + component.operation() + ")"; }}interface Component { operation(): string; }
class ConcreteComponent implements Component { operation(): string { return "Component"; }}
class Decorator implements Component { constructor(protected component: Component) {} operation(): string { return this.component.operation(); }}
class ConcreteDecorator extends Decorator { operation(): string { const result = this.component.operation(); return `Enhanced(${result})`; }}class Component {public: virtual std::string operation() = 0;};
class ConcreteComponent : public Component {public: std::string operation() override { return "Component"; }};
class Decorator : public Component {protected: Component* component;public: Decorator(Component* c) : component(c) {} std::string operation() override { return component->operation(); }};
class ConcreteDecorator : public Decorator {public: ConcreteDecorator(Component* c) : Decorator(c) {} std::string operation() override { std::string result = component->operation(); return "Enhanced(" + result + ")"; }};interface IComponent { string Operation(); }
class ConcreteComponent : IComponent { public string Operation() => "Component";}
class Decorator : IComponent { protected IComponent component; public Decorator(IComponent c) => component = c; public virtual string Operation() => component.Operation();}
class ConcreteDecorator : Decorator { public ConcreteDecorator(IComponent c) : base(c) {} public override string Operation() => $"Enhanced({component.Operation()})";}type Component interface{ Operation() string }
type ConcreteComponent struct{}func (c *ConcreteComponent) Operation() string { return "Component" }
type Decorator struct{ component Component }func (d *Decorator) Operation() string { return d.component.Operation() }
type ConcreteDecorator struct{ Decorator }func (d *ConcreteDecorator) Operation() string { result := d.component.Operation() return "Enhanced(" + result + ")"}// Common Pattern Structuretrait Coffee { fn cost(&self) -> f64;}struct SimpleCoffee;impl Coffee for SimpleCoffee { fn cost(&self) -> f64 { 2.0 }}struct MilkDecorator<T: Coffee> { inner: T,}impl<T: Coffee> Coffee for MilkDecorator<T> { fn cost(&self) -> f64 { self.inner.cost() + 0.5 }}Remember
Section titled “Remember”- Decorator Pattern wraps components to add behavior
- It uses same interface as component
- Decorators can be combined dynamically
- It prevents subclass explosion (2^n → 1+n)
- It’s about enhancement, not compatibility!
Interview Focus: Decorator Pattern
Section titled “Interview Focus: Decorator Pattern”Key Points to Remember
Section titled “Key Points to Remember”1. Core Concept
Section titled “1. Core Concept”What to say:
“Decorator Pattern is a structural design pattern that attaches additional responsibilities to an object dynamically. It wraps objects with decorators to add functionality without modifying the base object, providing a flexible alternative to subclassing.”
Why it matters:
- Shows you understand the fundamental purpose
- Demonstrates knowledge of when to use it
- Indicates you can explain concepts clearly
2. When to Use Decorator Pattern
Section titled “2. When to Use Decorator Pattern”Must mention:
- ✅ Avoid subclass explosion - Too many combinations to subclass (2^n problem)
- ✅ Dynamic behavior - Want to add features at runtime
- ✅ Flexible combination - Want to combine features dynamically
- ✅ Extend without modifying - Don’t want to modify existing code
Example scenario to give:
“I’d use Decorator Pattern when building a coffee shop system. I have a base coffee and 5 add-ons (milk, sugar, whipped cream, caramel, chocolate). Without Decorator, I’d need 2^5 = 32 subclasses for all combinations. With Decorator, I need 1 base + 5 decorators = 6 classes, and I can combine them dynamically at runtime.”
3. Decorator vs Adapter Pattern
Section titled “3. Decorator vs Adapter Pattern”Must discuss:
- Decorator: Adds behavior, same interface, enhances functionality
- Adapter: Makes compatible, different interface, about compatibility
- Key difference: Decorator enhances, Adapter adapts
Example to give:
“Decorator Pattern wraps objects to add behavior while keeping the same interface. Adapter Pattern wraps objects to make incompatible interfaces compatible. Decorator is about enhancement, Adapter is about compatibility. Decorator adds features, Adapter changes interface.”
4. Benefits and Trade-offs
Section titled “4. Benefits and Trade-offs”Benefits to mention:
- No subclass explosion - 1 + N instead of 2^N classes
- Dynamic combination - Can combine decorators at runtime
- Flexible extension - Add new decorators without modifying code
- Open/Closed Principle - Open for extension, closed for modification
- Single Responsibility - Each decorator adds one feature
Trade-offs to acknowledge:
- Complexity - Adds abstraction layer
- Performance - Multiple wrappers add indirection
- Over-engineering risk - Can be overkill for simple cases
- Order dependency - Decorator order can matter
5. Common Interview Questions
Section titled “5. Common Interview Questions”Q: “What’s the difference between Decorator Pattern and Inheritance?”
A:
“Inheritance creates a static relationship at compile time - you can’t change behavior at runtime. Decorator Pattern creates a dynamic relationship at runtime - you can wrap objects with decorators to add behavior dynamically. Inheritance requires a class for each combination, Decorator allows runtime combination.”
Q: “How does Decorator Pattern relate to SOLID principles?”
A:
“Decorator Pattern supports the Open/Closed Principle - you can extend functionality (add new decorators) without modifying existing code. It supports Single Responsibility Principle - each decorator adds one feature. It supports Dependency Inversion Principle by depending on the Component abstraction rather than concrete implementations.”
Q: “Can decorators be removed after being added?”
A:
“Yes, decorators can be removed, but it requires careful design. You’d need to maintain references to the original component or use a different structure. However, the typical use case is to build up decorators rather than remove them. If you need frequent add/remove, consider a different pattern like Strategy.”
Interview Checklist
Section titled “Interview Checklist”Before your interview, make sure you can:
- Define Decorator Pattern clearly in one sentence
- Explain when to use it (with examples showing subclass explosion)
- Describe Decorator vs Adapter Pattern
- Implement Decorator Pattern from scratch
- Compare with other structural patterns (Adapter, Composite)
- List benefits and trade-offs
- Identify common mistakes (not delegating, modifying component)
- Give 2-3 real-world examples
- Connect to SOLID principles
- Discuss when NOT to use it
- Explain how decorators can be combined
Remember: Decorator Pattern is about adding behavior dynamically - wrap objects to enhance functionality without subclass explosion! ☕