2 분 소요


컴포짓 패턴

정의

Composite design pattern comes under Structural Patterns is one of the most important and usable part of design pattern. Developers, in his daily life uses this pattern in somehow but he doesn’t know that its really a Composite Design Pattern.

-GoF design pattern

컴포짓 패턴은 클라이언트로 하여금 각 객체와 객체의 묶음을 동일하게 다룰 수 있게 해준다. 복합 객체와 단일 객체를 동일하게 취급하고 싶을 때 사용한다.

구조

img1

Component

Leaf와 Composite을 묶는 상위 인터페이스이다. 여기서 Leaf는 단일 객체, Composite은 복합 객체라고 생각하면 된다.

Composite

다른 Component 를 포함할 수 있는 Component로서 개별 객체 또는 다른 복합 객체를 포함시키 수 있다.
Component 구현체들을 내부에 리스트로 가지고 있다.
add(), remove(), getChild() 메소드는 내부 리스트에 단일, 복합 객체를 저장, 삭제, 조회한다.
operation()를 복합 객체에서 호출하게 되면 재귀적으로 추가 단일 객체를 저장한 하위 복합 객체를 순회한다.

Leaf

단일 객체로서, 다른 Component를 포함할 수 없다.

-> 결국 Composite 패턴의 핵심은 단일 객체와 복합 객체를 Client 입장에서 동일한 인터페이스를 통해서 다룰 수 있게 해준다는 것이다. 예시 코드를 보면서 이해해보자!

예제

저녁에 너무 배고파서 식당에 왔다. 식당 메뉴를 보니 정말 다양하다. 펜케이크 메뉴, 디저트 메뉴, 카페 메뉴, …
그리고 한 메뉴 안에서도 또 메뉴가 있다.

img1

이런 경우에 중첩되어 있는 메뉴(Menu)와 메뉴 항목(Menus)을 동일한 구조로 처리할 수 있다.

예제 코드

img4

우선 Component부터 살펴보자. Menu와 Menus를 묶는 역할을 한다. 공통으로 사용하는 operation으로는 getName(), getDescription(), getPrice()가 있다. 이제 이를 구현한 Leaf와 Composite을 살펴보자.

img1

단일 객체인 Menu를 살펴보자. Component에서 정의된 operation들을 구현한 것을 확인할 수 있다. 여기서는 getPrice() 함수가 단순히 자신의 맴버 변수인 price를 리턴한다는 것을 보고 넘어가자. 자세한 것은 아래서 설명하겠다.

img1

클래스가 길어서 두번에 나눠서 설명하겠다.
이 부분을 보면 Component 리스트를 저장하고 있는 것을 볼 수 있다.
위에서 설명한 것처럼 복합 객체는 단일 객체, 복합 객체를 모두 가지고 있을 수 있다.
그것들이 리스트 형태로 저장된 것이라고 생각하면 된다.
리스트에 추가(add), 삭제(remove), 조회(getChild)하는 기능이 들어가 있는 것도 확인할 수 있다.

img1

이제 가장 주요하게 살펴봐야 하는 getPrice() 메소드를 보자.
이 부분에 어떻게 복합 객체와 단일 객체를 동일하게 사용할 수 있는지에 대한 실마리가 있다. -> 바로 재귀이다.
getPrice() 메소드는 Component 리스트를 순회하면서 각 Component의 getPrice() 메소드를 호출하게 된다.
만약 Component 안의 객체가 단일 객체라면 Menu 클래스에 있는 getPrice() 메소드처럼 단순하게 맴버 변수인 price를 리턴할 것이다.
그런데 복합 객체라면 Menus의 getPrice() 메소드가 호출되며 제귀적으로 자신의 Component 리스트를 순회하면서 또getPrice()를 호출할 것이다.

-> 재귀 동작을 통해서 하위 객체에게 작업을 위임하게 함으로써 복합 객체와 단일 객체에 동일한 작업을 적용할 수 있게 돼서 이 둘을 구분할 필요가 없어지게 된다.

정리

다시 한번 말하지만 컴포짓 패턴은 단일 객체, 복합 객체를 동일한 인터페이스로 처리하고 싶을 때 사용하면 좋다는 것을 기억하면 좋겠다.


참고