๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
develop_en/theory

๐ŸŽจ Mastering the Decorator Pattern: A Complete Guide to Structural Design Patterns

by JSsunday 2025. 7. 12.
728x90
๋ฐ˜์‘ํ˜•

๐Ÿค” What is the Decorator Pattern?

The Decorator Pattern is a structural design pattern that allows you to dynamically add new functionality to objects without changing their structure. It works by creating wrapper objects that encapsulate the original object and extend its behavior.

๐Ÿ—๏ธ Core Concepts of the Decorator Pattern

๐Ÿ“‹ Key Components

  • Component: The base interface or abstract class
  • ConcreteComponent: The basic implementation
  • Decorator: The base decorator class
  • ConcreteDecorator: Concrete decorator implementations

โœ… Advantages

  • Dynamic extension of object functionality at runtime
  • More flexible than inheritance for extending functionality
  • Follows the Single Responsibility Principle
  • Allows free combination of features

โš ๏ธ Disadvantages

  • Can create many small objects
  • Debugging can become complex
  • Object identification can be difficult

๐Ÿ’ป Real-World Example Code

1. โ˜• Java Example - Company System

// Component interface
interface Company {
    String getDescription();
    double getRevenue();
}

// ConcreteComponent
class BasicCompany implements Company {
    private String name;
    private double baseRevenue;
    
    public BasicCompany(String name, double baseRevenue) {
        this.name = name;
        this.baseRevenue = baseRevenue;
    }
    
    @Override
    public String getDescription() {
        return name;
    }
    
    @Override
    public double getRevenue() {
        return baseRevenue;
    }
}

// Decorator abstract class
abstract class CompanyDecorator implements Company {
    protected Company company;
    
    public CompanyDecorator(Company company) {
        this.company = company;
    }
    
    @Override
    public String getDescription() {
        return company.getDescription();
    }
    
    @Override
    public double getRevenue() {
        return company.getRevenue();
    }
}

// ConcreteDecorators
class TechDivision extends CompanyDecorator {
    public TechDivision(Company company) {
        super(company);
    }
    
    @Override
    public String getDescription() {
        return company.getDescription() + " + Tech Division";
    }
    
    @Override
    public double getRevenue() {
        return company.getRevenue() + 5000000;
    }
}

class MarketingDivision extends CompanyDecorator {
    public MarketingDivision(Company company) {
        super(company);
    }
    
    @Override
    public String getDescription() {
        return company.getDescription() + " + Marketing Division";
    }
    
    @Override
    public double getRevenue() {
        return company.getRevenue() + 3000000;
    }
}

class GlobalExpansion extends CompanyDecorator {
    public GlobalExpansion(Company company) {
        super(company);
    }
    
    @Override
    public String getDescription() {
        return company.getDescription() + " + Global Operations";
    }
    
    @Override
    public double getRevenue() {
        return company.getRevenue() * 1.5;
    }
}

// Usage example
public class CompanyExample {
    public static void main(String[] args) {
        // Basic company
        Company basicCompany = new BasicCompany("TechStart", 10000000);
        System.out.println(basicCompany.getDescription() + " - Revenue: " + basicCompany.getRevenue());
        
        // Add tech division
        Company techCompany = new TechDivision(basicCompany);
        System.out.println(techCompany.getDescription() + " - Revenue: " + techCompany.getRevenue());
        
        // Add marketing division
        Company fullCompany = new MarketingDivision(techCompany);
        System.out.println(fullCompany.getDescription() + " - Revenue: " + fullCompany.getRevenue());
        
        // Global expansion
        Company globalCompany = new GlobalExpansion(fullCompany);
        System.out.println(globalCompany.getDescription() + " - Revenue: " + globalCompany.getRevenue());
    }
}

2. ๐ŸŸฆ TypeScript Example - Employee System

// Component interface
interface Employee {
    getName(): string;
    getSalary(): number;
    getRole(): string;
}

// ConcreteComponent
class BasicEmployee implements Employee {
    private name: string;
    private baseSalary: number;
    
    constructor(name: string, baseSalary: number) {
        this.name = name;
        this.baseSalary = baseSalary;
    }
    
    getName(): string {
        return this.name;
    }
    
    getSalary(): number {
        return this.baseSalary;
    }
    
    getRole(): string {
        return "General Employee";
    }
}

// Decorator abstract class
abstract class EmployeeDecorator implements Employee {
    protected employee: Employee;
    
    constructor(employee: Employee) {
        this.employee = employee;
    }
    
    getName(): string {
        return this.employee.getName();
    }
    
    getSalary(): number {
        return this.employee.getSalary();
    }
    
    getRole(): string {
        return this.employee.getRole();
    }
}

// ConcreteDecorators
class TeamLeader extends EmployeeDecorator {
    constructor(employee: Employee) {
        super(employee);
    }
    
    getRole(): string {
        return this.employee.getRole() + " + Team Leader";
    }
    
    getSalary(): number {
        return this.employee.getSalary() + 500000;
    }
}

class ProjectManager extends EmployeeDecorator {
    constructor(employee: Employee) {
        super(employee);
    }
    
    getRole(): string {
        return this.employee.getRole() + " + Project Manager";
    }
    
    getSalary(): number {
        return this.employee.getSalary() + 800000;
    }
}

class SeniorDeveloper extends EmployeeDecorator {
    constructor(employee: Employee) {
        super(employee);
    }
    
    getRole(): string {
        return this.employee.getRole() + " + Senior Developer";
    }
    
    getSalary(): number {
        return this.employee.getSalary() + 1000000;
    }
}

class Certification extends EmployeeDecorator {
    private certName: string;
    
    constructor(employee: Employee, certName: string) {
        super(employee);
        this.certName = certName;
    }
    
    getRole(): string {
        return this.employee.getRole() + ` + ${this.certName} Certified`;
    }
    
    getSalary(): number {
        return this.employee.getSalary() + 200000;
    }
}

// Usage example
const basicEmployee = new BasicEmployee("John Developer", 4000000);
console.log(`${basicEmployee.getName()} - ${basicEmployee.getRole()} - Salary: ${basicEmployee.getSalary()}`);

const teamLeader = new TeamLeader(basicEmployee);
console.log(`${teamLeader.getName()} - ${teamLeader.getRole()} - Salary: ${teamLeader.getSalary()}`);

const seniorTeamLeader = new SeniorDeveloper(teamLeader);
console.log(`${seniorTeamLeader.getName()} - ${seniorTeamLeader.getRole()} - Salary: ${seniorTeamLeader.getSalary()}`);

const certifiedSeniorTeamLeader = new Certification(seniorTeamLeader, "AWS");
console.log(`${certifiedSeniorTeamLeader.getName()} - ${certifiedSeniorTeamLeader.getRole()} - Salary: ${certifiedSeniorTeamLeader.getSalary()}`);

const projectManagerRole = new ProjectManager(certifiedSeniorTeamLeader);
console.log(`${projectManagerRole.getName()} - ${projectManagerRole.getRole()} - Salary: ${projectManagerRole.getSalary()}`);

3. ๐Ÿ Python Example - Animal System

from abc import ABC, abstractmethod

# Component interface
class Animal(ABC):
    @abstractmethod
    def get_description(self) -> str:
        pass
    
    @abstractmethod
    def get_abilities(self) -> list:
        pass
    
    @abstractmethod
    def get_power_level(self) -> int:
        pass

# ConcreteComponent
class BasicAnimal(Animal):
    def __init__(self, name: str, species: str, base_power: int):
        self.name = name
        self.species = species
        self.base_power = base_power
    
    def get_description(self) -> str:
        return f"{self.name} ({self.species})"
    
    def get_abilities(self) -> list:
        return ["Basic Survival"]
    
    def get_power_level(self) -> int:
        return self.base_power

# Decorator abstract class
class AnimalDecorator(Animal):
    def __init__(self, animal: Animal):
        self._animal = animal
    
    def get_description(self) -> str:
        return self._animal.get_description()
    
    def get_abilities(self) -> list:
        return self._animal.get_abilities()
    
    def get_power_level(self) -> int:
        return self._animal.get_power_level()

# ConcreteDecorators
class FlyingAbility(AnimalDecorator):
    def get_description(self) -> str:
        return self._animal.get_description() + " + Flying Ability"
    
    def get_abilities(self) -> list:
        abilities = self._animal.get_abilities().copy()
        abilities.append("Flying")
        return abilities
    
    def get_power_level(self) -> int:
        return self._animal.get_power_level() + 30

class SwimmingAbility(AnimalDecorator):
    def get_description(self) -> str:
        return self._animal.get_description() + " + Swimming Ability"
    
    def get_abilities(self) -> list:
        abilities = self._animal.get_abilities().copy()
        abilities.append("Swimming")
        return abilities
    
    def get_power_level(self) -> int:
        return self._animal.get_power_level() + 20

class PoisonousAbility(AnimalDecorator):
    def get_description(self) -> str:
        return self._animal.get_description() + " + Poisonous Ability"
    
    def get_abilities(self) -> list:
        abilities = self._animal.get_abilities().copy()
        abilities.append("Poisonous Attack")
        return abilities
    
    def get_power_level(self) -> int:
        return self._animal.get_power_level() + 50

class CamouflageAbility(AnimalDecorator):
    def get_description(self) -> str:
        return self._animal.get_description() + " + Camouflage Ability"
    
    def get_abilities(self) -> list:
        abilities = self._animal.get_abilities().copy()
        abilities.append("Camouflage")
        return abilities
    
    def get_power_level(self) -> int:
        return self._animal.get_power_level() + 25

class SuperStrength(AnimalDecorator):
    def get_description(self) -> str:
        return self._animal.get_description() + " + Super Strength"
    
    def get_abilities(self) -> list:
        abilities = self._animal.get_abilities().copy()
        abilities.append("Super Strength")
        return abilities
    
    def get_power_level(self) -> int:
        return self._animal.get_power_level() * 2

# Usage example
def print_animal_info(animal: Animal):
    print(f"๐Ÿฆ {animal.get_description()}")
    print(f"๐Ÿ’ช Abilities: {', '.join(animal.get_abilities())}")
    print(f"โšก Power Level: {animal.get_power_level()}")
    print("-" * 50)

# Basic animals
tiger = BasicAnimal("Tiger", "Predator", 80)
print_animal_info(tiger)

eagle = BasicAnimal("Eagle", "Bird of Prey", 60)
print_animal_info(eagle)

snake = BasicAnimal("Snake", "Reptile", 40)
print_animal_info(snake)

# Enhanced abilities
flying_tiger = FlyingAbility(tiger)
print_animal_info(flying_tiger)

swimming_flying_tiger = SwimmingAbility(flying_tiger)
print_animal_info(swimming_flying_tiger)

super_swimming_flying_tiger = SuperStrength(swimming_flying_tiger)
print_animal_info(super_swimming_flying_tiger)

# Enhanced eagle
camouflage_eagle = CamouflageAbility(eagle)
super_camouflage_eagle = SuperStrength(camouflage_eagle)
print_animal_info(super_camouflage_eagle)

# Enhanced snake
poisonous_snake = PoisonousAbility(snake)
camouflage_poisonous_snake = CamouflageAbility(poisonous_snake)
swimming_camouflage_poisonous_snake = SwimmingAbility(camouflage_poisonous_snake)
print_animal_info(swimming_camouflage_poisonous_snake)

๐Ÿ”„ Real-World Use Cases

๐Ÿ“ฑ UI Component Systems

  • Dynamically add icons, loading spinners, tooltips to basic buttons
  • Each feature can be combined independently

๐ŸŒ Middleware Systems

  • Add authentication, logging, caching, compression to HTTP requests in layers
  • Express.js and Spring Boot middleware chains

๐Ÿ’พ Data Stream Processing

  • Add encryption, compression, buffering to basic data streams
  • Java's InputStream/OutputStream classes

๐ŸŽฎ Game Development

  • Dynamically equip characters with weapons, armor, skills, buffs
  • Item combination systems

๐Ÿค Comparison with Other Patterns

๐Ÿ“Š Decorator vs Inheritance

  • Inheritance: Fixed at compile time, single extension only
  • Decorator: Dynamic at runtime, multiple feature combinations

๐Ÿ”ง Decorator vs Adapter

  • Adapter: Connects incompatible interfaces
  • Decorator: Adds functionality to the same interface

๐Ÿ—๏ธ Decorator vs Composite

  • Composite: Represents tree structure object relationships
  • Decorator: Linear feature extension

๐Ÿ“ Best Practices

โœจ Effective Usage

  1. Single Responsibility Principle: Each decorator handles one feature
  2. Interface Consistency: All decorators implement the same interface
  3. Order Independence: Decorator application order shouldn't affect results
  4. Performance Consideration: Too many decorator chains can impact performance

๐Ÿšซ Things to Avoid

  • Debugging difficulties from complex decorator chains
  • Strong dependencies between decorators
  • Breaking interface consistency

๐ŸŽฏ Conclusion

The Decorator Pattern is a powerful design pattern that allows flexible extension of object functionality. It overcomes the limitations of inheritance and enables dynamic feature combination at runtime, making it extremely useful in modern software development. It's widely used in microservice architectures, middleware systems, and UI component libraries.

The key is understanding the concept of "extension through feature combination" and implementing it appropriately for each language's characteristics. Use the examples above as a reference to apply the Decorator Pattern in your projects! ๐Ÿš€


Tags: DecoratorPattern, DesignPattern, StructuralPattern, Java, TypeScript, Python, ObjectOriented, SoftwareArchitecture, ProgrammingPattern, Developer

728x90
๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€