본문 바로가기

Design Pattern

[디자인패턴] Composite Pattern (컴포지트 패턴)

  • Usage of Composite Pattern
  • Composite Pattern VS Decorator Pattern
  • Composite Pattern with Iterator
  • Iterator for Composite
  • Things to Consider
  • Related Patterns

 


 

  • Usage of Composite Pattern

Purpose

    - 객체들을 계층구조로 만듦으로써 객체들이 같은 interface를 통해 독립적으로 다뤄지도록 한다.

      (개별 객체와 복합(composite)객체를 같은 방법으로 다룰 수 있도록 한다.)

 

Use When

    - 객체들을 계층구조로 나타내야 할 때

    - 객체와 복합 객체가 uniformly 다뤄져야 할 때 (called recursive composition)

 

 

Composite and Leaf

 

 

 


 

  • Composite Pattern VS Decorator Pattern

 

Composite Pattern
Decorator Pattern

 

 언뜻 보기에 Composite Pattern 은 Decorator Pattern과 다를게 없어 보인다. 하지만 중요한 차이가 존재한다.

Decorator Pattern은 composite하는 객체가 자기 자신의 상위 인터페이스를 1개 composite하지만

Composite Pattern은 composite하는 객체가 자기 자신의 상위 인터페이스를 *개 composite한다.

 

즉, Decorator Pattern에서는 1개의 recursive object를 갖지만 Composite Pattern에서는 Tree구조로써 하나의 부모노드가 여러개의 자식노드를 가질 수 있기 때문에 n개의 leaf와 m개의 composite객체를 recursive할 수 있다.

 

 

 


 

  • Composite Pattern with Iterator

 

Componenet Class
Leaf Class
Composite Class

이렇게 Component, Leaf, Composite Class가 정의되었을 때 Leaf class의 경우 자식노드가 없는 terminal 이기 때문에 print함수는 자신의 정보들을 출력한다. 반면, Composite class는 자식노드들이 있는 non-terminal 이기 때문에 iterator을 생성하여 iterator.next를 통해 자식노드들의 print함수가 하나씩 재귀적으로 호출되게끔 한다. 

이러한 Composite Pattern에서의 모든 노드(object)를 출력하는 코드는 다음과 같다.

Waitress

 루트 노드에 대해서 print함수를 호출하면 재귀적으로 자식노드로 내려가면서 print함수가 순차적으로 호출된다.

 

 

 


 

  • Iterator for Composite

isVegetarian 때문에 이렇게 복잡하게 stack을 운영..? print함수처럼 오버라이딩하면 안되나?

print()메소드처럼 특정 메소드를 재귀적으로 호출한다면 위에서의 Iterator처럼 내부 반복자를 이용해서 해결할 수 있다.

그러나 외부에서 isVegetarian처럼 특정 조건을 먼저 확인한 후 print()메소드를 호출하려 한다면,

즉, 외부에서 next(), hasnext() 같은 반복 작업을 직접 관리하려 한다면 외부 반복자인 복합 반복자를 구현해야 한다.

이런 복합 반복자(composite iterator)는 stack을 이용하여 운영한다.

 

Extending Iterator for Composite Iterator

stack 과 함께 외부반복자를 운영하기 위해 이제 Iterator에 관해서는 CompositeIterator를 create하도록 구현했다.

이때 Leaf object의 경우 Client입장에서 null조건을 검사하지 않고 Composite object와 같은 수준으로 createIterator를 볼 수 있도록 하기 위해 hasNext()에 대해 무조건 false를 리턴하는 NullIterator를 따로 구현하도록 한다.

NullIterator
CompositeIterator

위처럼 외부반복자인 CompositeIterator를 stack과 함께 구현하면 외부(Client)에서 hasNext와 next를 직접 관리할 수 있게된다. * CompositeIterator의 동작과정은 너무 길기 때문에 생략한다.

 

 

 


 

  • Things to Consider
  • composite object에서 자신의 부모노드에 대한 reference도 가지고 있어야 하나? 
  • => application에 따라 다르다. 만약 이 reference를 갖고 있다면 Chain of Responsibility Pattern을 지원할 수 있다.
  • 자식노드를 관리하기 위한 add(), remove(), getChild() 함수는 어디에서 선언해야 하나?
  • => 투명성 혹은 안전성에 따른 구현. Component에서 선언한다면 Leaf와 Composite object가 같은 수준, 방식으로 다뤄질 수 있어 투명성이 좋아지지만 Leaf에서는 단지 abstract로 구현되어 있는 add, remove, getChild 함수에 대해 Client가 무의미한 작업들을 할 수가 있으므로 안전성은 떨어지게 된다. 반면에 Composite에서만 선언한다면 방금 말했듯이 Leaf에서 혹시나 무의미하게 위 3가지 메소드를 호출하려하면 compile-time에서 잡히기 때문에 안전성이 좋아지지만 Composite과 이제 같은 방식으로 다뤄지지 않으므로 투명성이 떨어지게 된다.
  • Composite Pattern은 계층구조(자식노드) 관리, 자신의 작업 관리 이렇게 두 가지 책임을 갖고 있다. 이는 SRP에 위배되지만 Leaf와 Composite object를 모두 같은 방식, interface로 처리할 수 있게되어 투명성을 확보하였다. 대신 안전성은 떨어지게 된다.

 

 


 

  • Related Patterns
  • Composite VS Decorator : 둘다 recursive composition이라는 점이 같지만 Decorator는 object에 responsibility를 추가할 수 있게 하기 위해 설계된 것이고 Composite은 embellishment(꾸밈)이 아닌 representation에 초점을 맞춘 것이다.
  • Iterator : Composite Pattern에서 aggregate object의 underlying representation을 알지 않고도 요소들에 접근할 수 있는 방법을 제공해준다.