본문 바로가기
-----ETC-----/C++ 고급 프로그래밍과 응용 프로젝트 시리즈

[C++ 고급 프로그래밍과 응용 프로젝트 시리즈] Day 12: 디자인 패턴 심화 - 데코레이터 패턴 (Decorator Pattern)

by cogito21_cpp 2024. 8. 1.
반응형

데코레이터 패턴 (Decorator Pattern)

데코레이터 패턴은 객체에 동적으로 새로운 기능을 추가할 수 있도록 하는 구조 패턴입니다. 이 패턴을 사용하면 서브클래싱 없이도 객체의 기능을 확장할 수 있습니다. 데코레이터 패턴은 기본 객체와 그 객체의 데코레이터를 동일한 인터페이스로 취급하여 기능을 확장합니다.

 

데코레이터 패턴의 특징

  1. 동적 기능 확장: 객체의 기능을 동적으로 확장할 수 있습니다.
  2. 개방-폐쇄 원칙: 기존 코드의 수정 없이 새로운 기능을 추가할 수 있습니다.
  3. 유연한 설계: 상속 대신 합성을 사용하여 기능을 확장합니다.

데코레이터 패턴의 구조

  1. Component (구성 요소): 기본 객체 인터페이스 또는 추상 클래스
  2. ConcreteComponent (구체적 구성 요소): 기본 객체의 구체적 구현
  3. Decorator (데코레이터): 기본 객체와 동일한 인터페이스를 구현하는 추상 클래스
  4. ConcreteDecorator (구체적 데코레이터): 데코레이터의 구체적 구현, 추가 기능을 정의

데코레이터 패턴 UML 다이어그램

+-----------------+      +---------------------+
|    Component    |<-----|   Decorator         |
+-----------------+      +---------------------+
| +operation()    |      | -component: Component|
+-----------------+      | +operation()        |
      /\                 +---------------------+
      ||                        /\
      ||                        ||
+------------+           +------------------+
|ConcreteComp|           |ConcreteDecorator |
|    onent   |           |                  |
+------------+           +------------------+
| +operation()|          | +operation()     |
+------------+           +------------------+

데코레이터 패턴 구현 예제

다음 예제는 데코레이터 패턴을 사용하여 기본 텍스트를 꾸미는 프로그램을 구현합니다.

 

Component 인터페이스 정의

#include <iostream>
#include <memory>

// 구성 요소 인터페이스
class Text {
public:
    virtual std::string getText() const = 0;
    virtual ~Text() = default;
};

 

ConcreteComponent 클래스 정의

// 구체적 구성 요소 클래스
class PlainText : public Text {
private:
    std::string text;

public:
    PlainText(const std::string& text) : text(text) {}

    std::string getText() const override {
        return text;
    }
};

 

Decorator 클래스 정의

// 데코레이터 클래스
class TextDecorator : public Text {
protected:
    std::unique_ptr<Text> textComponent;

public:
    TextDecorator(std::unique_ptr<Text> textComponent) : textComponent(std::move(textComponent)) {}

    std::string getText() const override {
        return textComponent->getText();
    }
};

 

ConcreteDecorator 클래스 정의

// 구체적 데코레이터 클래스: HTML 태그 추가
class HTMLDecorator : public TextDecorator {
public:
    HTMLDecorator(std::unique_ptr<Text> textComponent) : TextDecorator(std::move(textComponent)) {}

    std::string getText() const override {
        return "<html>" + textComponent->getText() + "</html>";
    }
};

// 구체적 데코레이터 클래스: 대문자로 변환
class UpperCaseDecorator : public TextDecorator {
public:
    UpperCaseDecorator(std::unique_ptr<Text> textComponent) : TextDecorator(std::move(textComponent)) {}

    std::string getText() const override {
        std::string result = textComponent->getText();
        for (char& c : result) {
            c = std::toupper(c);
        }
        return result;
    }
};

 

데코레이터 패턴 사용 예제

int main() {
    std::unique_ptr<Text> plainText = std::make_unique<PlainText>("Hello, World!");
    std::unique_ptr<Text> htmlText = std::make_unique<HTMLDecorator>(std::move(plainText));
    std::unique_ptr<Text> upperCaseHTMLText = std::make_unique<UpperCaseDecorator>(std::move(htmlText));

    std::cout << upperCaseHTMLText->getText() << std::endl;

    return 0;
}

 

이 예제에서 PlainText 클래스는 기본 텍스트를 제공하며, HTMLDecoratorUpperCaseDecorator는 텍스트에 HTML 태그를 추가하고 대문자로 변환하는 기능을 각각 추가합니다.


데코레이터 패턴의 응용

데코레이터 패턴은 다양한 응용 분야에서 사용할 수 있습니다. 몇 가지 예를 들어보겠습니다.

  1. GUI 구성 요소: 버튼, 창, 스크롤바 등 GUI 구성 요소를 꾸미는 데 사용됩니다.
  2. 입출력 스트림: Java의 java.io 패키지와 같이 입출력 스트림을 꾸미는 데 사용됩니다.
  3. 로깅 기능: 기존 클래스에 로깅 기능을 추가하는 데 사용됩니다.

실습 문제

문제 1: 데코레이터 패턴을 사용하여 메시지에 다양한 형식을 추가

데코레이터 패턴을 사용하여 기본 메시지에 다양한 형식을 추가하는 프로그램을 작성하세요. 예를 들어, "Hello" 메시지에 별표(*)를 추가하고, 대괄호([])를 추가하는 기능을 구현하세요.

 

해설:

Component 인터페이스 정의

#include <iostream>
#include <memory>

// 구성 요소 인터페이스
class Message {
public:
    virtual std::string getMessage() const = 0;
    virtual ~Message() = default;
};

 

ConcreteComponent 클래스 정의

// 구체적 구성 요소 클래스
class PlainMessage : public Message {
private:
    std::string message;

public:
    PlainMessage(const std::string& message) : message(message) {}

    std::string getMessage() const override {
        return message;
    }
};

 

Decorator 클래스 정의

// 데코레이터 클래스
class MessageDecorator : public Message {
protected:
    std::unique_ptr<Message> messageComponent;

public:
    MessageDecorator(std::unique_ptr<Message> messageComponent) : messageComponent(std::move(messageComponent)) {}

    std::string getMessage() const override {
        return messageComponent->getMessage();
    }
};

 

ConcreteDecorator 클래스 정의

// 구체적 데코레이터 클래스: 별표 추가
class StarDecorator : public MessageDecorator {
public:
    StarDecorator(std::unique_ptr<Message> messageComponent) : MessageDecorator(std::move(messageComponent)) {}

    std::string getMessage() const override {
        return "*" + messageComponent->getMessage() + "*";
    }
};

// 구체적 데코레이터 클래스: 대괄호 추가
class BracketDecorator : public MessageDecorator {
public:
    BracketDecorator(std::unique_ptr<Message> messageComponent) : MessageDecorator(std::move(messageComponent)) {}

    std::string getMessage() const override {
        return "[" + messageComponent->getMessage() + "]";
    }
};

 

데코레이터 패턴 사용 예제

int main() {
    std::unique_ptr<Message> plainMessage = std::make_unique<PlainMessage>("Hello");
    std::unique_ptr<Message> starMessage = std::make_unique<StarDecorator>(std::move(plainMessage));
    std::unique_ptr<Message> bracketStarMessage = std::make_unique<BracketDecorator>(std::move(starMessage));

    std::cout << bracketStarMessage->getMessage() << std::endl;

    return 0;
}

 

이 예제에서 PlainMessage 클래스는 기본 메시지를 제공하며, StarDecoratorBracketDecorator는 메시지에 별표와 대괄호를 추가하는 기능을 각각 추가합니다.

 

이제 12일차의 학습을 마쳤습니다. 데코레이터 패턴의 다양한 구현 방법과 활용 사례에 대해 상세히 학습하고 실습 문제를 풀어보았습니다.

 

질문이나 피드백이 있으면 언제든지 댓글로 남겨 주세요. 내일은 "커맨드 패턴"에 대해 학습하겠습니다.

반응형