컴포지트 패턴을 이용하면 객체들을 트리구조로 구성하여 계층구조로 나타낼 수 있습니다.
중요한 것은 트리구조에서 자식이 있는 노드와 자식이 없는 노드를 구분하지 않고
마치 하나처럼 사용 할 수 있다는 것입니다.
자식이 있는 것을 노드라고 부르고, 없는 것을 리프(leaf)라고 부르겠습니다.
컴포지트 패턴에서는 노드와 리프가 하나의 추상 클래스를 상속받습니다.
노드와 리프를 하나처럼(똑같은 방법) 다루기 위함이죠.. 이 말은 뒤에서 다시 언급해보겠습니다.
쇼핑몰을 위해서 카테고리를 만들려고 합니다.
일단 제일 상위에 "전체 카테고리"가 존재하고 그 밑으로
"의류"와 "전자제품" 카테고리가 있습니다. 그리고 "의류" 밑에는 "점퍼" 라는 하위 카테고리가 존재합니다.
public void add(CategoryComponent component) {
throw new UnsupportedOperationException();
}
public CategoryComponent getChild(int i) {
throw new UnsupportedOperationException();
}
public void print() {
throw new UnsupportedOperationException();
}
public void getDeparName() {
throw new UnsupportedOperationException();
}
}
카테고리와 그 밑으로 들어가게 될 상품 모두 이 CategoryComponent를 상속해야 합니다.
그래야 노드와 리프를 같은 객체처럼 다룰 수 있습니다.
그리고 메소드마다 exception을 던지는 이유는, 노드 같은 경우는 자식을 가질 수 있기 때문에 add 메소드를 사용하지만
리프 같은 경우는 자식을 가질 수 없기 때문에 add는 사용하지 않는 메소드입니다.
그래서 상위 클래스인 CategoryComponent에서 위와 같이 구현을 해 놓으면 이 클래스를 상속받는 클래스들은
자신이 사용 할 메소드만 구현하면 됩니다.
그러면 Category를 보겠습니다.
import java.util.ArrayList;
import java.util.Iterator;
public class Category extends CategoryComponent {
private String name;
private ArrayList <CategoryComponent> components = new ArrayList<CategoryComponent>();
public Category(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void add(CategoryComponent component) {
this.components.add(component);
}
public CategoryComponent getChild(int i) {
return components.get(i);
}
public void print() {
System.out.println("카테고리 이름 : " + getName());
System.out.println("-----------------------------");
Iterator<CategoryComponent> iterator = components.iterator();
while(iterator.hasNext()) {
CategoryComponent component = iterator.next();
component.print();
}
}
}
카테고리는 하위 자식을 가질 수 있습니다. 바로 상품들이죠.. 그리고 카테고리는 하위 카테고리도 자식으로 가질 수 있습니다.
노드나 리프나 모든 CategoryComponent를 상속합니다.
그리고, print를 보시면...
Iterator를 사용해서 재귀호출로 자기가 가진 자식들의 print()를 호출하고 있습니다.
스택을 그려놓고 저 재귀호출이 어떻게 작동하는지 보시는 것도
좋을 것입니다.
String name;
String price;
public Product(String name, String price) {
this.name = name;
this.price = price;
}
public String getName() {
return name;
}
public String getPrice() {
return price;
}
public void print() {
System.out.println("상품명 : " + getName());
System.out.println("가격 : " + getPrice());
System.out.println("-----------------------------");
}
}
리프를 구성 할 Product 클래스입니다. 마찬가지로, CategoryComponent를 상속받고 있습니다.
그리고 print()메소드에서는 자식이 없기 때문에 그냥 자기 자신의 정보를 print합니다.
위 Category에서 재귀호출로 불려진 print() 메소드는 자기가 가진 최하위 리프까지 호출되어와서
그 리프의 print() 메소드부터 다시 실행되며 처리 되는 것입니다.
테스트 클래스입니다.
public static void main(String[] args) {
CategoryComponent category = new Category("의류");
CategoryComponent category2 = new Category("전자제품");
CategoryComponent categoryAll = new Category("전체 카테고리");
categoryAll.add(category);
categoryAll.add(category2);
category.add(new Product("스웨터","10000"));
category.add(new Product("청바지","30000"));
category.add(new Product("면바지","2000"));
category.add(new Product("가디건","110000"));
//앗 점퍼 카테고리..
CategoryComponent category3 = new Category("점퍼");
category.add(category3);
category3.add(new Product("지오다노 패딩 점퍼","30000"));
category3.add(new Product("니 패딩 점퍼","50000"));
category2.add(new Product("아이팟터치","280000"));
category2.add(new Product("디오스 냉장고","2280000"));
category2.add(new Product("에어컨","1280000"));
categoryAll.print();
}
}
트리구조로 상품들을 넣고 있습니다.
전체 밑에 의류와 전자제품이 있고 의류 밑에는 점퍼라는 하위 카테고리가 존재합니다.
그리고 각각의 카테고리는 상품을 가지고 있습니다.
그런데 이것을 모두 출력하는데는 단 한줄의 코드만 있으면 됩니다.
입니다.
카테고리와 상품을 구분해서 출력할 필요도 없습니다.
출력해야 하는 정보는 자기 자신들이 각각 가지고 있을뿐이죠..
사실 Composite 패턴을 사용해서 트리를 제대로 구현하려면 이것보다 더 복잡합니다.