OOP 에서 컴포지트는 하나 이상의 유사한 객체를 구성으로 설계된 객체로 모두 유사한 기능을 나타낸다.
이를 통해 객체를 그룹 조작하는 것처럼, 단일 객체를 조작할 수 있다.
컴포지트 패턴은 클라이언트가 복합 객체
나 단일 객체
를 동일하게 취급하는 것을 목적으로 한다.
여기서 컴포지트의 의도는 트리 구조로 작성해 전체-부분 (whole- part) 관계를 표현하는 것이다.
트리 구조를 다룰 때, 프로그래미는 리프 노드와 브랜치를 구별해야한다.
여기서 코드는 많은 복잡성을 만들어 많은 에러를 초래한다.
이를 해결하기 위해, 복잡하고 원시적인 객체를 동일하게 취급하기 위한 인터페이스를 작성할 수 있다.
결과적으로 컴포지트 패턴은 인터페이스 본연의 컴포지트의 개념을 활용한다.
복합 객체와 단일 객체의 처리 방법이 다르지 않을 경우, 전체-부분 관계로 정의가 가능하다.
전체-부분 관계의 대표적인 예는 Directory-File이 존재한다.
이러한 전체-부분 관계를 효율적으로 정의할 때 유용하다.
- 전체-부분 관계를 트리 구조로 표현하고 싶을 경우
- 전체-부분 관계를 클라이언트에서 부분, 관계 객체를 균일하게 처리하고 싶을 경우
UML 다이어그램이 뜻하는 의미는 다음과 같다
Client 클래스는 Leaf와 Composite 클래스를 직접 참조하지 않고, 공통 인터페이스인 Component를 참조하는 것을 볼 수 있다.
Leaf 클래스는 Component 인터페이스를 구현한다.
Composite 클래스는 Component 객체 자식들을 유지하고, operation()과 같은 요청을 통해 자식들에게 전달한다.
각각을 조금 더 코드 관점에서 보면
- Component
- 모든 컴포넌트들을 위한 추상화된 개념으로써 Leaf, Composite 클래스의 인터페이스이다.
- Leaf
- Component 인터페이스를 구현하고, 구체 클래스를 나타낸다.
- Composite
- Component 인터페이스를 구현하고, 구현되는 자식들을 가지고, 이러한 자식들을 관리하기 위한 메소드를 구현한다. 또한 일반적으로 인터페이스에 작성된 메소드는 자식에게 위임하는 처리를 한다.
- Composite.operation() => Leaf.operation() 자세한 이해는 아래 예제를 통해 할 수 있다.
이를 기반으로, 객체 다이어그램을 보면 이해가 쉽다.
Client에서 트리 구조에서의 top-level에 존재하는 Composite에 요청을 보낸다. 그러면 Component 인터페이스를 구현한 객체들은 트리 구조를 토대로 위에서 아래 방향으로 모든 자식 요소에게 전달하게 된다.
이것은 실제로 런타임에서 일어나는 행위라고 보면 된다.