디자인 패턴 - 컴포지트 패턴

컴포지트 패턴

  • 정의

    • 객체들을 트리 구조로 구성하여 전체와 부분을 계층구조로 만들 수 패턴
  • 사용하는 이유 및 특징

    • 객체들을 모아서 모두 똑같은 방식으로 다루고 싶을 때 사용한다. 이러한 점은 클라이언트를 단순화시킬 수 있는 장점이 있다. 
    • 하나의 메소드에서 전체 구조에 대해서 반복 작업을 처리할 때 자주 사용한다.
  • 방법

    • 실제 적용 UML

레거시 코드

  • 디렉토리 구조
  • 레거시 코드의 문제점
    • 새로운 메뉴가 추가될 때 마다 Waitress 클래스 생성자 및 Print 변경 -> 확장성 문제
    • Menu 객체에 새로운 서브 Menu 추가 번거로움 -> 확장성 문제
public class MenuTestDrive {
    public static void main(String[] args) {
        PancakeMenu pancakeMenu = new PancakeMenu();
        DinerMenu dinerMenu = new DinerMenu();
        CafeMenu cafeMenu = new CafeMenu();

        Waitress waitress = new Waitress(pancakeMenu, dinerMenu, cafeMenu);
        waitress.printMenu();
    }
}
  • Waitress
public class Waitress {
    PancakeMenu pancakeHouseMenu;
    DinerMenu dinerMenu;
    CafeMenu cafeMenu;

    public Waitress(PancakeMenu pancakeHouseMenu, DinerMenu dinerMenu, CafeMenu cafeMenu) {
        this.pancakeHouseMenu = pancakeHouseMenu;
        this.dinerMenu = dinerMenu;
        this.cafeMenu = cafeMenu;
    }

    public void printMenu() {
        Iterator pancakeIterator = pancakeHouseMenu.createIterator();
        Iterator dinerIterator = dinerMenu.createIterator();
        Iterator cafeIterator = cafeMenu.createIterator();
        System.out.println("메뉴\n---\n아침메뉴");
        printMenu(pancakeIterator);
        System.out.println("\n점심메뉴");
        printMenu(dinerIterator);
        System.out.println("\n저녁메뉴");
        printMenu(cafeIterator);
    }

    private void printMenu(Iterator iterator) {
        while (iterator.hasNext()) {
            MenuItem menuItem = (MenuItem) iterator.next();
            System.out.print(menuItem.getName() + ", ");
            System.out.print(menuItem.getPrice() + ", ");
            System.out.println(menuItem.getDescription());
        }
    }
}

개선 코드

  • TEST CODE

    public class MenuTest {
       public static void main(String[] args) throws OperationNotSupportedException {
           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);
    
           // 메뉴 항목을 추가하는 부분
           pancakeHouseMenu.add(new MenuItem("팬케이크","블루베리 팬케이크",true,2.99));
    
           dinerMenu.add(new MenuItem("파스타", "올리브 파스타",true, 3.89));
    
           dinerMenu.add(dessertMenu);
    
           dessertMenu.add(new MenuItem("애플 파이", "바닐라 아이스크림이 얹혀있는 애플 파이", true, 1.59));
    
           Waitress waitress = new Waitress(allMenus);
    
           waitress.printMenu();
    
       }
    }
  • Waitress (root node)

public class Waitress {
    MenuComponent allMenus;

    public Waitress(MenuComponent allMenus) {
        this.allMenus = allMenus;
    }

    public void printMenu() throws OperationNotSupportedException {
        allMenus.print();
    }
}
  • MenuComponent 추가
    • 모든 객체에 있는 메소드 정의 및 예외처리 -> 통합 관리
public abstract class MenuComponent {

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

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

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

    public String getName() throws OperationNotSupportedException{
        throw new OperationNotSupportedException();
    }

    public String getDescription() throws OperationNotSupportedException{
        throw new OperationNotSupportedException();
    }

    public double getPrice() throws OperationNotSupportedException{
        throw new OperationNotSupportedException();
    }

    public boolean isVegetarian() throws OperationNotSupportedException{
        throw new OperationNotSupportedException();
    }

    public void print() throws OperationNotSupportedException {
        throw new OperationNotSupportedException();
    }

}
  • Menu 구현체
public class Menu extends MenuComponent{
    ArrayList<MenuComponent> menuComponents = new ArrayList<>();
    String name;
    String description;

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

    public void add(MenuComponent menuComponent) {
        menuComponents.add(menuComponent);
    }

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

    public MenuComponent getChild(int i) {
        return menuComponents.get(i);
    }

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

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

    public void print() throws OperationNotSupportedException {

        System.out.print("\n" + getName());
        System.out.println(", " + getDescription());
        System.out.println("---------------------");

        Iterator iterator = menuComponents.iterator();
        while (iterator.hasNext()) {
            MenuComponent menuComponent = (MenuComponent) iterator.next();
            menuComponent.print();
        }
    }
}
  • MenuItem 구현체
public class MenuItem extends 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;
    }

    public void print() {
        System.out.print("   " + getName());
        if (isVegetarian()) System.out.print(" (v)");
        System.out.println(", " + getPrice());
        System.out.println("      -- " + getDescription());
    }
}
  • Q&A
    • Menu 및 MenuItem을 인터페이스를 사용해서 팩토리 메소드 방식으로 개선할 수 없을까??

좋은 웹페이지 즐겨찾기