단일객체와 복합객체를 동일하게 취급하기 위해서
객체를 트리구조로 구성하여 부분-전체 계층구조로 구현하여
단일객체와 복합객체를 똑같은 방법으로 다룰 수 있게 해주는 디자인 패턴
public abstract class MenuComponent {
// 여기서부터는 메뉴판 클래스가 사용할 메소드
public void add(MenuComposite menuComponent) {
throw new UnsupportedOperationException();
}
public void remove(MenuComposite menuComponent) {
throw new UnsupportedOperationException();
}
// 여기서부터는 메뉴 클래스가 사용할 메소드
public String getName() {
throw new UnsupportedOperationException();
}
public double getPrice() {
throw new UnsupportedOperationException();
}
public String getDescription() {
throw new UnsupportedOperationException();
}
}
메뉴판(복합객체)와 메뉴(단일객체)를 모두 하나의 메뉴컴포넌트로 인식하여
똑같은 방법으로 처리할 수 있게 만드는 것이 컴포지트 패턴의 핵심이다.
해당 메뉴컴포넌트 추상 클래스를 상속받아서 메뉴판과 메뉴 클래스를 구현하면
두 클래스 모두 메뉴 컴포넌트로 사용할 수 있다.
이때, 각 메소드가 UnsupportedOperationException 예외를 발생시키는 이유는 복합객체와 단일객체가 필요한 메소드들이 각각 다를 수 있기 때문에
자기 역할에 맞지 않는 메소드에 대해 예외를 던지는 코드를 추상 클래스에서 미리 구현하여, 서브클래스에서 재정의(Override)하지 않을 경우 예외를 발생시키는 것이다.
public class MenuComposite extends MenuComponent {
List<MenuComponent> menus;
public MenuTable() {
this.menus = new ArrayList<>();
}
public void add(MenuComponent menuComponent) {
this.menus.add(menuComponent);
}
public void remove(MenuComponent menuComponent) {
this.menus.remove(menuComponent);
}
}
public class Menu extends MenuComponent {
String name;
String description;
double price;
public Menu(String name, String description, double price) {
this.name = name;
this.description = description;
this.price = price;
}
public String getName() {
return this.name;
}
public String getDescription() {
return this.description;
}
public double getPrice() {
return this.price;
}
}
메뉴판(메뉴테이블), 메뉴 클래스 모두 MenuComponent를 상속받아서
필요한 메소드만 재정의하여 구현하고 나면
아래와 같이 사용이 가능하다.
public static void main(String[] args) {
MenuComponent menu1 = new Menu("김치찌개", "얼큰한 김치찌개", 8000.0);
MenuComponent menu2 = new Menu("돈까스", "배터지는 돈까스", 8500.0);
MenuComponent menu3 = new Menu("김치볶음밥", "든든한 김치볶음밥", 7000.0);
MenuComponent menu4 = new Menu("김밥", "우영우 김밥", 3500.0);
MenuComponent menus = new MenuTable();
menus.add(menu1);
menus.add(menu2);
menus.add(menu3);
menus.add(menu4);
}
컴포지트 패턴을 조금 정리하자면 아래와 같다.
메뉴(단일객체, Leaf)
메뉴판(Component, Leaf와 Composite가 구현해야 하는 추상 클래스 혹은 인터페이스로 Leaf와 Composite 모두 이 Component타입으로 인식된다.)
메뉴컴포지트(복합객체, Composite, Component 추상클래스 혹은 인터페이스 타입의 요소를 자식으로 갖는 클래스)
이처럼 컴포지트 패턴은 비슷한 기능들을 갖고 있는 단일객체와 복합객체들을
하나의 구조로 인식하여 처리할 수 있게 하는 것인데
이 패턴은 어느 것이 복합객체(Composite)인지 단일객체(Leaf)인지 알 수 없는 투명성을 확보하고, 같은 방식으로 두객체 모두 처리할 수 있도록 하지만
이러한 방법은 단일 책임 원칙(SRP)을 깨는 행위이다.