λ””μžμΈνŒ¨ν„΄

[λ””μžμΈνŒ¨ν„΄] λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄

μ„œμ•„λž‘πŸ˜ 2024. 8. 23. 08:17

 


λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄ (Decorator Pattern)

λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄μ€ 객체 지ν–₯ λ””μžμΈ νŒ¨ν„΄ 쀑 ν•˜λ‚˜λ‘œ, 객체에 λ™μ μœΌλ‘œ μƒˆλ‘œμš΄ κΈ°λŠ₯을 μΆ”κ°€ν•  수 μžˆλŠ” ꡬ쑰λ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€. 이 νŒ¨ν„΄μ€ 기쑴의 클래슀λ₯Ό μˆ˜μ •ν•˜μ§€ μ•Šκ³ λ„ 객체에 좔가적인 μ±…μž„μ„ λΆ€μ—¬ν•  수 있게 ν•΄μ€λ‹ˆλ‹€. λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄μ€ 상속 λŒ€μ‹  μ‘°ν•©(composition)을 톡해 κΈ°λŠ₯을 ν™•μž₯ν•˜λŠ” 방법을 μ œμ‹œν•˜λ©°, μ΄λŠ” 객체 지ν–₯ μ„€κ³„μ˜ 개방-폐쇄 원칙(Open-Closed Principle)을 μ€€μˆ˜ν•˜λŠ” λŒ€ν‘œμ μΈ μ˜ˆμž…λ‹ˆλ‹€.

1. λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄μ˜ κΈ°λ³Έ κ°œλ…

  • ꡬ성 μš”μ†Œ:
    1. μ»΄ν¬λ„ŒνŠΈ(Component): κΈ°λ³Έ μΈν„°νŽ˜μ΄μŠ€λ‚˜ 좔상 클래슀이며, ꡬ체적인 객체듀이 이λ₯Ό κ΅¬ν˜„ν•©λ‹ˆλ‹€.
    2. 콘크리트 μ»΄ν¬λ„ŒνŠΈ(Concrete Component): μ‹€μ œ κΈ°λŠ₯을 κ΅¬ν˜„ν•œ ν΄λž˜μŠ€μž…λ‹ˆλ‹€. λ°μ½”λ ˆμ΄ν„°μ— μ˜ν•΄ μž₯식될 κ°μ²΄μž…λ‹ˆλ‹€.
    3. λ°μ½”λ ˆμ΄ν„°(Decorator): Component μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•˜λ©°, Concrete Component 객체λ₯Ό κ°μ‹Έμ„œ μƒˆλ‘œμš΄ κΈ°λŠ₯을 μΆ”κ°€ν•©λ‹ˆλ‹€.
    4. 콘크리트 λ°μ½”λ ˆμ΄ν„°(Concrete Decorator): λ°μ½”λ ˆμ΄ν„°μ˜ ꡬ체적인 κ΅¬ν˜„μ²΄λ‘œ, 좔가적인 κΈ°λŠ₯을 κ΅¬ν˜„ν•˜κ³  이λ₯Ό μ›λž˜ 객체에 μœ„μž„ν•©λ‹ˆλ‹€.
  • μž₯점:
    • λŸ°νƒ€μž„μ— 객체에 μƒˆλ‘œμš΄ κΈ°λŠ₯을 λ™μ μœΌλ‘œ μΆ”κ°€ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
    • 상속을 λ‚¨λ°œν•˜μ§€ μ•ŠμœΌλ©΄μ„œλ„ 객체의 κΈ°λŠ₯을 ν™•μž₯ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
    • 클래슀 κ³„μΈ΅μ˜ λ³΅μž‘μ„±μ„ μ€„μ—¬μ€λ‹ˆλ‹€.
  • 단점:
    • λ°μ½”λ ˆμ΄ν„°κ°€ μ—¬λŸ¬ 개 μ€‘μ²©λ˜λ©΄ λ³΅μž‘λ„κ°€ 증가할 수 μžˆμŠ΅λ‹ˆλ‹€.
    • 객체의 ꡬ성(decorator chain)이 λ³΅μž‘ν•΄μ§€λ©΄, 디버깅이 μ–΄λ €μ›Œμ§ˆ 수 μžˆμŠ΅λ‹ˆλ‹€.

 

2. λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄ 예제

μ•„λž˜ μ˜ˆμ œμ—μ„œλŠ” 컀피λ₯Ό λ§Œλ“œλŠ” μ‹œμŠ€ν…œμ—μ„œ λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄μ„ μ μš©ν•΄ λ³΄κ² μŠ΅λ‹ˆλ‹€. 기본적인 컀피(ConcreteComponent)에 좔가적인 재료(예: 우유, 섀탕 λ“±)λ₯Ό λ™μ μœΌλ‘œ μΆ”κ°€ν•˜λŠ” κΈ°λŠ₯을 λ°μ½”λ ˆμ΄ν„°λ₯Ό 톡해 κ΅¬ν˜„ν•©λ‹ˆλ‹€.(C++)

#include <iostream>
#include <memory>
#include <string>

// Component
class Coffee {
public:
    virtual std::string getDescription() const = 0;
    virtual double cost() const = 0;
    virtual ~Coffee() = default;
};

// ConcreteComponent
class SimpleCoffee : public Coffee {
public:
    std::string getDescription() const override {
        return "Simple Coffee";
    }

    double cost() const override {
        return 2.0;
    }
};

// Decorator
class CoffeeDecorator : public Coffee {
protected:
    std::unique_ptr<Coffee> decoratedCoffee;

public:
    CoffeeDecorator(std::unique_ptr<Coffee> coffee) : decoratedCoffee(std::move(coffee)) {}

    std::string getDescription() const override {
        return decoratedCoffee->getDescription();
    }

    double cost() const override {
        return decoratedCoffee->cost();
    }
};

// ConcreteDecorator
class MilkDecorator : public CoffeeDecorator {
public:
    MilkDecorator(std::unique_ptr<Coffee> coffee) : CoffeeDecorator(std::move(coffee)) {}

    std::string getDescription() const override {
        return decoratedCoffee->getDescription() + ", Milk";
    }

    double cost() const override {
        return decoratedCoffee->cost() + 0.5;
    }
};

// ConcreteDecorator
class SugarDecorator : public CoffeeDecorator {
public:
    SugarDecorator(std::unique_ptr<Coffee> coffee) : CoffeeDecorator(std::move(coffee)) {}

    std::string getDescription() const override {
        return decoratedCoffee->getDescription() + ", Sugar";
    }

    double cost() const override {
        return decoratedCoffee->cost() + 0.3;
    }
};

int main() {
    // κΈ°λ³Έ 컀피 생성
    std::unique_ptr<Coffee> myCoffee = std::make_unique<SimpleCoffee>();
    std::cout << myCoffee->getDescription() << " $" << myCoffee->cost() << std::endl;

    // 우유 μΆ”κ°€
    myCoffee = std::make_unique<MilkDecorator>(std::move(myCoffee));
    std::cout << myCoffee->getDescription() << " $" << myCoffee->cost() << std::endl;

    // 섀탕 μΆ”κ°€
    myCoffee = std::make_unique<SugarDecorator>(std::move(myCoffee));
    std::cout << myCoffee->getDescription() << " $" << myCoffee->cost() << std::endl;
    
    // ν•œλ²ˆμ— λͺ¨λ‘ 생성
		std::unique_ptr<Coffee> myCoffee2 = 
				std::make_unique<SugarDecorator>(
						std::make_unique<MilkDecorator>(
								std::make_unique<SimpleCoffee>()));
		std::cout << myCoffee2->getDescription() << " $" << myCoffee2->cost() << std::endl;
		
		return 0;
}
Simple Coffee $2
Simple Coffee, Milk $2.5
Simple Coffee, Milk, Sugar $2.8
Simple Coffee, Milk, Sugar $2.8

3. μ½”λ“œ μ„€λͺ…

  1. Component: Coffee μΈν„°νŽ˜μ΄μŠ€λŠ” μ»€ν”Όμ˜ getDescriptionκ³Ό cost λ©”μ„œλ“œλ₯Ό μ •μ˜ν•©λ‹ˆλ‹€.
  2. ConcreteComponent: SimpleCoffee ν΄λž˜μŠ€λŠ” κΈ°λ³Έ μ»€ν”Όμ˜ μ„€λͺ…κ³Ό 가격을 μ œκ³΅ν•©λ‹ˆλ‹€.
  3. Decorator: CoffeeDecorator ν΄λž˜μŠ€λŠ” Coffeeλ₯Ό 상속받아 컀피λ₯Ό μž₯μ‹ν•˜λŠ” 역할을 ν•©λ‹ˆλ‹€. 이 ν΄λž˜μŠ€λŠ” κ΅¬μ„±λœ 컀피 객체λ₯Ό 가지고 있으며, λ°μ½”λ ˆμ΄ν„° ν΄λž˜μŠ€λŠ” 이 객체에 좔가적인 κΈ°λŠ₯을 λ§λΆ™μž…λ‹ˆλ‹€.
  4. ConcreteDecorator: MilkDecorator와 SugarDecoratorλŠ” CoffeeDecoratorλ₯Ό 상속받아 μš°μœ μ™€ 섀탕을 μΆ”κ°€ν•˜λŠ” κΈ°λŠ₯을 κ΅¬ν˜„ν•©λ‹ˆλ‹€.

이 μ˜ˆμ œμ—μ„œ 컀피에 μš°μœ μ™€ 섀탕을 μΆ”κ°€ν•˜λ©΄μ„œλ„, μ›λž˜μ˜ SimpleCoffee ν΄λž˜μŠ€λŠ” μˆ˜μ •ν•˜μ§€ μ•Šκ³  μƒˆλ‘œμš΄ κΈ°λŠ₯을 μΆ”κ°€ν•  수 μžˆμ—ˆμŠ΅λ‹ˆλ‹€. 이처럼 λ°μ½”λ ˆμ΄ν„° νŒ¨ν„΄μ€ 객체에 κΈ°λŠ₯을 λ™μ μœΌλ‘œ μΆ”κ°€ν•˜λŠ” 데 맀우 μœ μš©ν•œ νŒ¨ν„΄μž…λ‹ˆλ‹€.