Composite Pattern

개념

여러 개의 객체를 하나의 큰 객체처럼 묶어서 처리할 수 있게 해주는 설계 패턴이다. 트리 구조를 이용해서 복잡한 계층을 만들 수 있고, 이 구조에서는 하나의 메뉴 항목(리프)이나 여러 개의 항목을 가진 메뉴(복합 객체)를 동일한 방식으로 다룰 수 있다.

예시

예를 들어 메뉴를 생각해보면 팝업 메뉴 안에 일반 메뉴 항목뿐만 아니라 또 다른 팝업 메뉴도 넣을 수 있다. 사용자는 이게 단일 항목인지 또 다른 메뉴 묶음인지 몰라도 되며, 그냥 command()만 호출하면 알아서 잘 동작한다.

이 방식의 장점은, 메뉴가 하나든 여러 개든 똑같은 인터페이스(BaseMenu*)로 다룰 수 있다는 점이다. 코드 짜는 입장에서 신경 쓸 게 줄어들며 유지보수도 쉬워진다.

구조 클래스 설계

  class BaseMenu {
public:
    virtual void command() = 0;
    virtual void add(BaseMenu* p) {}
    virtual ~BaseMenu() {}
};

class MenuItem : public BaseMenu {
    std::string name;
public:
    MenuItem(const std::string& n) : name(n) {}
    void command() override {
        std::cout << name << " 메뉴 선택됨" << std::endl;
    }
};

class PopupMenu : public BaseMenu {
    std::string name;
    std::vector<BaseMenu*> items;
public:
    PopupMenu(const std::string& n) : name(n) {}
    void add(BaseMenu* p) override {
        items.push_back(p);
    }
    void command() override {
        std::cout << "[ " << name << " ]" << std::endl;
        for (size_t i = 0; i < items.size(); ++i) {
            std::cout << i + 1 << ". ";
            items[i]->command();
        }
    }
};
  

장점 요약

  • 계층적 구조 표현이 쉬움
  • 새로운 항목 추가가 쉬움 (객체 간 관계 설정만으로 확장)
  • 클라이언트는 BaseMenu* 타입만 다루면 됨 (단일/복합 객체 구분 필요 없음)

Decorator Pattern

개념

객체에 동적으로 기능을 추가할 수 있는 설계 패턴이다. 상속이 아닌 포함을 이용하며, 중복 기능 확장에 유리하다.

특징

  • 컴파일 시점이 아닌 런타임에 기능 추가
  • 상속보다 조합 기반 설계를 지향

예시

  struct IDraw {
    virtual void draw() = 0;
    virtual ~IDraw() {}
};

class Rectangle : public IDraw {
public:
    void draw() override {
        std::cout << "Rectangle" << std::endl;
    }
};

class ColorDecorator : public IDraw {
    IDraw* inner;
public:
    ColorDecorator(IDraw* d) : inner(d) {}
    void draw() override {
        std::cout << "[Color] ";
        inner->draw();
    }
};

class ShadowDecorator : public IDraw {
    IDraw* inner;
public:
    ShadowDecorator(IDraw* d) : inner(d) {}
    void draw() override {
        std::cout << "[Shadow] ";
        inner->draw();
    }
};
  
  int main() {
    IDraw* rect = new Rectangle;
    IDraw* colored = new ColorDecorator(rect);
    IDraw* shadowed = new ShadowDecorator(colored);
    shadowed->draw();
    delete shadowed; // 내부도 소멸 필요 시 smart pointer 권장
}
  

실제 사례

  • C#의 Stream 체인 구성 예:

    • FileStream → CryptoStream → ZipStream → Write()

비교 요약

패턴특징사용 목적
Composite트리 구조, 복합 객체 구성구조를 계층적으로 표현
Decorator런타임 기능 확장, 중첩 가능기능을 유연하게 조합

이처럼 두 패턴은 구조(Composite)와 기능(Decorator) 측면에서 각각 객체지향의 유연성을 극대화하는 방식이다.