컴포지트 패턴이란?
복합 객체(Composite) 와 단일 객체(Leaf)를 동일한 컴포넌트로 취급하여, 클라이언트에게 이 둘을 구분하지 않고 동일한 인터페이스를 사용하도록 하는 구조 패턴입니다. 컴포지트 패턴은 전체-부분의 관계를 갖는 객체들 사이의 관계를 트리 계층 구조로 정의해야 할때 유용합니다.
컴포지트 패턴 사용 방법
- Component : Leaf와 Compsite 를 묶는 공통적인 상위 인터페이스
- Composite : 복합 객체로서, Leaf 역할이나 Composite 역할을 넣어 관리하는 역할을 한다.
- Component 구현체들을 내부 리스트로 관리한다
- add 와 remove 메소드는 내부 리스트에 단일 / 복합 객체를 저장
- Component 인터페이스의 구현 메서드인 operation은 복합 객체에서 호출되면 재귀 하여, 추가 단일 객체를 저장한 하위 복합 객체를 순회하게 된다.
- Leaf: 단일 객체로서, 단순하게 내용물을 표시하는 역할을 한다.
- Component 인터페이스의 구현 메서드인 operation은 단일 객체에서 호출되면 적절한 값만 반환한다
- Client : 클라이언트는 Component를 참조하여 단일 / 복합 객체를 하나의 객체로서 다룬다.
자식 객체들을 관리하는 함수들 선언 위치
For Uniformity
일관성을 추구하는 방식으로, 자식을 관리하기 위한 메소드들을 Composite가 아닌 Component에 정의하여 Client는 Leaf와 Composite를 일관되게 취급할 수 있다.
하지만 Client에서 Leaf 객체가 자식을 다루는 메서드를 호출할 수 있기 때문에, 타입에 대한 안정성을 잃게 된다.
interface Component {
void operation();
void add(Component c);
void remove(Component c);
List<Component> getChild();
}
class Leaf implements Component {
@Override
public void operation() {
System.out.println(this + " 호출");
}
}
class Composite implements Component {
// Leaf 와 Composite 객체 모두를 저장하여 관리하는 내부 리스트
List<Component> components = new ArrayList<>();
@Override
public void add(Component c) {
components.add(c); // 리스트 추가
}
@Override
public void remove(Component c) {
components.remove(c); // 리스트 삭제
}
@Override
public void operation() {
System.out.println(this + " 호출");
// 내부 리스트를 순회하여, 단일 Leaf이면 값을 출력하고,
// 또다른 서브 복합 객체이면, 다시 그 내부를 순회하는 재귀 함수 동작이 된다.
for (Component component : components) {
component.operation(); // 자기 자신을 호출(재귀)
}
}
@Override
public List<Component> getChild() {
return components;
}
}
class Client {
public static void main(String[] args) {
// 1. 최상위 복합체 생성
Component composite1 = new Composite();
// 2. 최상위 복합체에 저장할 Leaf와 또다른 서브 복합체 생성
Component leaf1 = new Leaf();
Component composite2 = new Composite();
// 3. 최상위 복합체에 개체들을 등록
composite1.add(leaf1);
composite1.add(composite2);
// 4. 서브 복합체에 저장할 Leaf 생성
Component leaf2 = new Leaf();
Component leaf3 = new Leaf();
Component leaf4 = new Leaf();
// 5. 서브 복합체에 개체들을 등록
composite2.add(leaf2);
composite2.add(leaf3);
composite2.add(leaf4);
// 6. 최상위 복합체의 모든 자식 노드들을 출력
composite1.operation();
}
}
For Type Safety
타입의 안정성을 추구하는 방식이다.
이 방식은 자식을 관리하기 위한 add(), remove()와 같은 메서드들이 오직 Composite에만 정의되었다.
이로 인해 Client는 Leaf와 Composite를 다르게 취급한다.
하지만 Client에서 Leaf 객체가 자식을 다루는 메서드를 호출할 수 없기 때문에, 타입에 대한 안정성을 얻게 된다.
interface Component {
void operation();
}
class Leaf implements Component {
@Override
public void operation() {
System.out.println(this + " 호출");
}
}
class Composite implements Component {
// Leaf 와 Composite 객체 모두를 저장하여 관리하는 내부 리스트
List<Component> components = new ArrayList<>();
public void add(Component c) {
components.add(c); // 리스트 추가
}
public void remove(Component c) {
components.remove(c); // 리스트 삭제
}
@Override
public void operation() {
System.out.println(this + " 호출");
// 내부 리스트를 순회하여, 단일 Leaf이면 값을 출력하고,
// 또다른 서브 복합 객체이면, 다시 그 내부를 순회하는 재귀 함수 동작이 된다.
for (Component component : components) {
component.operation(); // 자기 자신을 호출(재귀)
}
}
public List<Component> getChild() {
return components;
}
}
class Client {
public static void main(String[] args) {
// 1. 최상위 복합체 생성
Composite composite1 = new Composite();
// 2. 최상위 복합체에 저장할 Leaf와 또다른 서브 복합체 생성
Leaf leaf1 = new Leaf();
Composite composite2 = new Composite();
// 3. 최상위 복합체에 개체들을 등록
composite1.add(leaf1);
composite1.add(composite2);
// 4. 서브 복합체에 저장할 Leaf 생성
Leaf leaf2 = new Leaf();
Leaf leaf3 = new Leaf();
Leaf leaf4 = new Leaf();
// 5. 서브 복합체에 개체들을 등록
composite2.add(leaf2);
composite2.add(leaf3);
composite2.add(leaf4);
// 6. 최상위 복합체의 모든 자식 노드들을 출력
composite1.operation();
}
}
패턴 사용시기
- 전체-부분 관계를 트리 구조로 표현하고 싶을 때
- 전체-부분 관계를 클라이언트에서 부분, 관계 객체를 균일하게 처리하고 싶을 때
패턴 장점
- 단일체와 복합체를 동일하게 여기기 때문에 묶어서 연산하거나 관리할 때 편리하다.
- 다형성 재귀를 통해 복잡한 트리 구조를 보다 편리하게 구성 할 수 있다.
- 수평적, 수직적 모든 방향으로 객체를 확장할 수 있다.
- 새로운 Leaf 클래스를 추가하더라도 클라이언트는 추상화된 인터페이스 만을 바라보기 때문에 개방 폐쇄 원칙(OCP)Visit Website을 준수 한다. (단일 부분의 확장이 용이)
패턴 단점
- 재귀 호출 특징 상 트리의 깊이(depth)가 깊어질 수록 디버깅에 어려움이 생긴다.
- 설계가 지나치게 범용성을 갖기 때문에 새로운 요소를 추가할 때 복합 객체에서 구성 요소에 제약을 갖기 힘들다.
- 예를들어, 계층형 구조에서 leaf 객체와 composite 객체들을 모두 동일한 인터페이스로 다루어야하는데, 이 공통 인터페이스 설계가 까다로울 수 있다.
- 복합 객체가 가지는 부분 객체의 종류를 제한할 필요가 있을 때
- 수평적 방향으로만 확장이 가능하도록 Leaf를 제한하는 Composite를 만들때
참고
'CS > Design Pattern' 카테고리의 다른 글
옵서버 패턴 (Observer Pattern) (0) | 2024.04.18 |
---|---|
싱글톤 패턴 (Singleton Pattern) (0) | 2024.04.18 |
브리지 패턴 (Bridge Pattern) (0) | 2024.04.17 |
퍼싸드 패턴 (Facade Pattern) (0) | 2024.04.17 |
어댑터 패턴 (Adaptor Pattern) (0) | 2024.04.03 |