본문 바로가기

디자인 패턴

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

1. 정의

  • 객체를 트리구조로 구성해서 부분-전체 계층구조를 구현한다. 클라이언트에서 개별 객체복합 객체일관된 방법으로 다룰 수 있다.

💡 개별 객체리프 노드를 의미하고, 복합 객체일반 노드를 의미한다.

2. 예시

  • 식당 메뉴를 계층구조로 만든다.

3. 그림

4. 클래스 다이어그램

5. 코드

public class Client {
    public static void main(String[] args){
        MenuComponent breakfast = new Menu("아침 메뉴", "08:00~11:00");
        MenuComponent lunch = new Menu("점심 메뉴", "12:00~17:00");
        MenuComponent dinner = new Menu("저녁 메뉴", "18:00~23:00");

        MenuComponent all = new Menu("전체 메뉴", "00:00~23:59");

        all.add(breakfast);
        all.add(lunch);
        all.add(dinner);

        // 메인
        breakfast.add(new MenuItem("시리얼", "요거트", 4_000));
        breakfast.add(new MenuItem("식빵", "딸기쨈", 3_000));

        lunch.add(new MenuItem("팬케이크", "메이플 시럽", 6_000));

        dinner.add(new MenuItem("불고기", "국내산", 15_000));
        dinner.add(new MenuItem("비빔밥", "국내산", 10_000));

        // 디저트
        MenuComponent lunchDessert = new Menu("점심 디저트", "12:00~17:00");
        lunchDessert.add(new MenuItem("커피", "콜롬비아", 1_500));
        lunch.add(lunchDessert);

        MenuComponent dinnerDessert = new Menu("저녁 디저트", "18:00~23:00");
        dinnerDessert.add(new MenuItem("포도", "", 1_000));
        dinnerDessert.add(new MenuItem("딸기", "", 1_000));
        dinner.add(dinnerDessert);

        all.print();
    }
}
/* 출력
[전체 메뉴](00:00~23:59)
[아침 메뉴](08:00~11:00)
시리얼(요거트) : 4000
식빵(딸기쨈) : 3000
[점심 메뉴](12:00~17:00)
팬케이크(메이플 시럽) : 6000
[점심 디저트](12:00~17:00)
커피(콜롬비아) : 1500
[저녁 메뉴](18:00~23:00)
불고기(국내산) : 15000
비빔밥(국내산) : 10000
[저녁 디저트](18:00~23:00)
포도() : 1000
딸기() : 1000
*/
public abstract class MenuComponent {

    public void add(MenuComponent menuComponent){
        throw new UnsupportedOperationException();
    }

    public void remove(MenuComponent menuComponent){
        throw new UnsupportedOperationException();
    }

    public MenuComponent getChild(int i){
        throw new UnsupportedOperationException();
    }

    public String getName(){
        throw new UnsupportedOperationException();
    }

    public String getDescription(){
        throw new UnsupportedOperationException();
    }

    public int getPrice(){
        throw new UnsupportedOperationException();
    }

    public void print(){
        throw new UnsupportedOperationException();
    }
}
// Leaf Node
public class MenuItem extends MenuComponent{
    String name, description;
    int price;

    public MenuItem(String name, String description, int price) {
        this.name = name;
        this.description = description;
        this.price = price;
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getDescription() {
        return description;
    }

    @Override
    public int getPrice() {
        return price;
    }

    @Override
    public void print() {
        System.out.println(name + "("+ description +")" + " : " +price);
    }
}
import java.util.ArrayList;
import java.util.List;

// Node
public class Menu extends MenuComponent{
    List<MenuComponent> component = new ArrayList<>();
    String name;
    String description;

    public Menu(String name, String description) {
        this.name = name;
        this.description = description;
    }

    @Override
    public void add(MenuComponent menuComponent) {
        component.add(menuComponent);
    }

    @Override
    public void remove(MenuComponent menuComponent) {
        component.remove(menuComponent);
    }

    @Override
    public MenuComponent getChild(int i) {
        return component.get(i);
    }

    @Override
    public String getName() {
        return name;
    }

    @Override
    public String getDescription() {
        return description;
    }

    @Override
    public void print() {
        System.out.println("["+name+"]" + "("+ description +")");

        for (MenuComponent menuComponent : component) {
            menuComponent.print();
        }
    }
}

https://github.com/kang-seongbeom/design_pattern

💡 Github에서 디자인 패턴 코드를 볼 수 있습니다.

6. 설명

  • 모든 노드는 MenuComponet를 상속한 클래스로 부터 구현된다.
  • 즉, 모든 노드는 MenuComponet참조변수인 로 접근할 수 있다.
  • MenuItem은 리프 노드다.
  • Menu는 노드이면서 복합 객체다.
  • 💡 복합 객체 내부에 다른 노드가 존재할 수 있다.
  • 이와 같이 계층 구조를 이루면 일관된 방법으로 노드에 접근할 수 있다.

7. 참고

  • 컴포지트 패턴은 두 가지 책임이 있다.
    1. 계층구조 관리
    2. 메뉴 관련 작업처리
  • 이는 단일책임원칙(SRP, Single Responsibility Principle)에 위반다.
  • 대신 투명성(Transparency)을 지원한다.
  • 💡 투명성 : 일관된 방법으로 접근