본문 바로가기
CS(Computer Science)지식/[C++] 디자인 패턴

장식자 패턴(Decorator Pattern) 이해하기 : 쉽게 설명한 디자인 패턴(C++)

by 엔지니어 청년 2024. 1. 28.

장식자 패턴은 객체의 결합을 통해 기능을 동적으로 유연하게 확장할 수 있게 해주는 디자인 패턴입니다. 이 패턴은 기본 객체에 추가적인 기능을 덧붙이는 데 사용되며, 이를 통해 기능을 계층적으로 확장할 수 있습니다. 장식자 패턴은 객체 지향 설계 원칙 중 하나인 개방-폐쇄 원칙(OCP, Open-Closed Principle)을 따릅니다. 이 원칙은 “소프트웨어 구성요소는 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다”는 원칙입니다.

이제 C++을 사용한 장식자 패턴의 예시를 살펴보겠습니다.

#include <iostream>
#include <string>

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

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

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

// Decorator
class BeverageDecorator : public Beverage {
public:
    explicit BeverageDecorator(Beverage* beverage) : beverage(beverage) {}
    ~BeverageDecorator() {
        delete beverage;
    }

protected:
    Beverage* beverage;
};

// ConcreteDecorator
class Milk : public BeverageDecorator {
public:
    explicit Milk(Beverage* beverage) : BeverageDecorator(beverage) {}

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

    double cost() const override {
        return 0.2 + beverage->cost();
    }
};

int main() {
    Beverage* beverage = new Coffee();
    std::cout << beverage->getDescription() << ": $" << beverage->cost() << std::endl;

    Beverage* beverage2 = new Milk(beverage);
    std::cout << beverage2->getDescription() << ": $" << beverage2->cost() << std::endl;

    delete beverage2;

    return 0;
}

위 코드를 실행하면 다음과 같은 출력 결과를 얻을 수 있습니다.

Coffee: $1
Coffee, Milk: $1.2

이 코드에서 Beverage는 Component 역할을 하며, Coffee는 ConcreteComponent, BeverageDecorator는 Decorator, Milk는 ConcreteDecorator 역할을 합니다. Milk 장식자를 사용하면 Beverage 객체에 우유를 추가하는 기능이 추가됩니다.

이 예제는 매우 간단하지만, 실제로는 더 많은 종류의 Beverage와 Decorator를 추가할 수 있습니다. 예를 들어, Tea, Sugar, Cream 등을 추가할 수 있습니다. 이렇게 하면 각각의 객체는 자신의 책임에 집중할 수 있으며, 새로운 기능을 추가하거나 기존 기능을 변경하는 것이 용이해집니다.

위 다이어그램에서 Component는 Beverage에 해당하고, ConcreteComponent는 Coffee에 해당합니다. Decorator는 BeverageDecorator에 해당하고, ConcreteDecorator는 Milk에 해당합니다. 이렇게 장식자 패턴을 사용하면 기능을 동적으로 추가하거나 확장하는 것이 매우 간단해집니다.