728x90

객체들 간에는 객체가 객체를 구성하는 관계도 있는데 이런 관계를 구성 관계 또는 부분-전체 관계라고도 합니다. 우리가 설계, 구현할 때 미리 구성해 놓는 경우도 있지만 동적으로 객체 여러 개를 관계를 맺어 하나로 사용하는 경우도 있습니다. 

 

파워포인트 같은 편집 프로그램을 사용할 때 등록되어 있는 도형들도 사용하긴 하지만 여러 도형들을 그룹화해서 사용하기도 합니다. 이렇게 새로 그룹화하여 만든 도형도 나중에 쓰려고 상단 메뉴에 등록할 수 있도록 하는 기능이 필요해질 수 있습니다. 상단 메뉴에 기본적으로 등록되어 있는 것처럼 처음부터 제공하는 도형(오브젝트)을 기본 객체라고 하고 새로 만들어 동적으로 등록하여 사용하려는 도형(오브젝트)을 구성 객체라고 부르겠습니다.

 

기본 객체들과 구성 객체들을 각각 클래스로 구성해볼 수 있습니다. 구성 객체들은 기본 객체들을 관리하기 위한 멤버를 가지고 있습니다. 기본 객체들과 구성 객체들은 모두 같은 자료형을 가지고 있어야 같은 인터페이스로 접근이 가능하기 때문에 같은 클래스로부터 상속을 받습니다. 아래와 같이 구성되어 있는 구조를 컴포지트 패턴이라고 합니다.

class Object {
    virtual void Draw() = 0;
    virtual void Add(Object* obj) { };
    virtual void Remove(Object* obj) { };
    virtual Object* getChild(int nth) { return 0; };
};
class Circle : public Object {
    void Draw() { cout << "Draw Circle"; }
};
class ComposedObject : public Object {
    void Draw() { 
    	cout << "Draw Composed Objects"; 
        list<Object*>::iterator iter1;
        for(iter1 = lComponent.begin(); iter1 != lComponent.end(); iter1++) {
        	(*iter1)->Draw();
        }
    }
    void Add(Object* obj) {  lComponet.push_front(obj); }
    void Remove(Object* obj) { 
    	list<Object*>::iterator iter1;
    	for(iter1 = lComponent.begin(); iter1 != lComponent.end(); iter1++) {
        	if (*iter1 == obj) { lComponent.erase(iter1); }
        }
    }
    Object* getChild(int nth) {
        list<Object*>::iterator iter1;
        for (int i = 0, iter1 = lComponent.begin(); iter1 != lComponent.end(); iter1++, i++) {
            if ( i == nth ) { return *iter1; }
        }
        return 0;
    }
private:
	list<Object*> lComponent;
};

int main()
{
    Circle c1, c2;
    ComposedObject cObj;
    cObj.Add(&c1);
    cObj.Add(&c2);
}

Add, Remove 함수 같은 경우는 ComposedObject만 사용하기 때문에 순수 가상 함수로 선언하면 다른 Object들에서도 선언을 해줘야 하므로 그냥 가상 함수로 최상위 클래스에서는 아무것도 수행하지 않습니다.

 

ComposedObject의 Draw에서는 가지고 있는 리스트에 담긴 Object들을 가져와서 Draw합니다. ComposedObject의 구성원으로 ComposedObject도 올 수 있기 때문에 ComposedObject는 트리 구조를 가지게 됩니다.

트리 구조를 가지기 때문에 자기 자신을 참조하는 객체를 찾기 위해서는 자신을 참조하는 객체에 대한 양방향 포인터를 가지게 할 수 있습니다. 그렇게 하기 위해서는 객체들은 자신을 참조하는 객체에 대한 포인터도 가지고 있어야 합니다.

만약 하나의 객체를 두 객체가 가지고 있다면 아래와 같은 구조를 가지게 됩니다.

이럴 경우에는 객체를 분리하여 아래와 같이 구성할 수 있습니다. 이런 방법을 Flyweight 패턴이라고 합니다.

정리하자면 컴포지트 패턴은 기본적으로 사용하고 있는 오브젝트들과 같은 인터페이스를 사용하면서 여러 오브젝트들을 결합하여 하나의 객체로써 사용할 수 있게 해주는 패턴이라고 할 수 있습니다.

728x90

'Programming > C++' 카테고리의 다른 글

C++ 퍼사드 패턴  (0) 2021.05.16
C++ 데코레이터 패턴  (0) 2021.05.16
C++ 브릿지 패턴  (0) 2021.05.15
C++ 어댑터 패턴  (0) 2021.05.09
C++ 싱글톤 패턴  (0) 2021.05.09

+ Recent posts