[Design Pattern] Composite Pattern

younghyun·2022년 10월 26일
0

Design Pattern

목록 보기
14/14
post-thumbnail

Composite Pattern 이란

단일 객체와 복합 객체를 같은 타입으로 취급하며, 트리 구조로 객체들을 엮는 패턴이다.

설계

imageimage

  • Component
    • Leaf와 Composite 가 구현해야하는 Interface
    • 모든 클래스에 공통적인 행위에 대해 기본 기능 operation() 을 구현할 수 있음
  • Leaf
    • 개별 객체로, Composite 의 부분(자식) 객체로 들어감
    • Component의 형태
  • Composite
    • 복합 객체로, Leaf 객체나 Composite를 부분(자식)으로 둠
    • Component의 형태

예시

식당 메뉴 저장 프로그램

  • 점심 메뉴 안에 디저트 메뉴가 포함되는 식당 메뉴를 만들기
image

1. 컴포짓 패턴을 활용한 설계

image
  • MenuItem: leaf에 해당하는 클래스 (개별 객체)
  • Menu: MenuComponent를 구현하면서, MenuComponent를 포함할 수 있음

2. 실제 구현

MenuComponent Component

public interface MenuComponent {

    // // add() 를 통해 자식으로 여러 개의 "Leaf" 를 가질 수 있음
    public default void add(MenuComponent menuComponent) {
        throw new UnsupportedOperationException();
    }

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

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

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

    public default double getPrice() {
        throw new UnsupportedOperationException();
    }

    public default boolean isVegetarian() {
        throw new UnsupportedOperationException();
    }

    public default void print() {
        throw new UnsupportedOperationException();
    }
}

MenuItem Leaf

public class MenuItem implements MenuComponent {

    String name;
    String description;
    boolean vegetarian;
    double price;

    public MenuItem(String name, String description,
                                    boolean vegetarian, double price) {
        this.name = name;
        this.description = description;
        this.vegetarian = vegetarian;
        this.price = price;
    }

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

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

    @Override
    public boolean isVegetarian() {
        return vegetarian;
    }

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

    @Override
    public void print() {
        System.out.print("  " + getName());
        if (isVegetarian()) {
            System.out.print("(v)");
        }
        System.out.println(", " + getPrice());
        System.out.println("     -- " + getDescription());
    }
}

Menu Composite

public class Menu implements MenuComponent {

    // 인터페이스 "MenuComponent" 를 구현하는 요소들을 관리하기 위한 리스트
    // menuComponents를 여러개 가질 수 있으므로 arraylist를 이용하여 담을 수 있게 함
    ArrayList<MenuComponent> menuComponents = new ArrayList<>();
    
    String name;
    String description;

    public Menu(String name, String description) {
        this.name = name;
        this.description = description;
    }
    
    @Override
    public void add(MenuComponent menuComponent) {
        menuComponents.add(menuComponent);
    }

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

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

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

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

    @Override
    public void print() {
        System.out.print("\n" + getName());
        System.out.println(", " + getDescription());
        System.out.println("--------------------------");
        
        // 메뉴 컴포넌트에 있는 것들을 하나씩 취급해서 출력하는 형태
        Iterator<MenuComponent> iterator = menuComponents.iterator();
        while (iterator.hasNext()) {
            MenuComponent menuComponent = iterator.next();
            menuComponent.print();
        }
    }
}

Waitron

public class Waitron {

    MenuComponent allMenus;
    
    // 어떤 메뉴를 전달할건지
    public Waitron(MenuComponent allMenus) {
        this.allMenus = allMenus;
    }
    
    // 메뉴 출력
    public void printMenu() {
        allMenus.print();
    }
}

Main

public class MenuTestDrive {
    public static void main(String[] args) {

        MenuComponent pancakeHouseMenu = new Menu("팬케이크 하우스 메뉴", "아침 메뉴");
        MenuComponent dinerMenu = new Menu("객체마을 식당 메뉴", "점심 메뉴");
        MenuComponent cafeMenu = new Menu("카페 메뉴", "저녁 메뉴");
        MenuComponent dessertMenu = new Menu("디저트 메뉴", "디저트를 즐기세요!");
        MenuComponent allMenus = new Menu("전체 메뉴", "전체 메뉴");
        
        allMenus.add(pancakeHouseMenu);
        allMenus.add(dinerMenu);
        allMenus.add(cafeMenu);

        dinerMenu.add(new MenuItem("파스타", "마리나라 소스 스파게티, 효모빵 포함", true, 3.89));
        dinerMenu.add(new MenuItem("애플 파이", "바삭바삭한 크러스트에 바닐라 아이스크림이 얹혀있는 애플 파이", true, 1.59));

        // dissertMenu가 MenuComponent를 구성하는 형태이므로 메뉴 항목인 것처럼 넣기 가능
        dinerMenu.add(dessertMenu);

        Waitron waitron= new Waitron(allMenus);
        waitron.printMenu();
    }
}

실행 화면

image
profile
🌱 주니어 백엔드 개발자입니당

0개의 댓글