ios dev kangwook.

12. Composite Pattern 본문

Design Pattern

12. Composite Pattern

kangwook 2022. 9. 4. 23:13

Composite Pattern은 여러 개의 객체들로 구성된 복합 객체와 단일 객체를 클라이언트에서 같은 타입으로 취급하며, 트리 구조로 객체들을 엮는 패턴

Composite Pattern

Composite Pattern을 사용하면 객체를 트리 구조로 합성하여 부분 - 전체 계층을 나타낼 수 있다. 이를 통해 클라이언트는 단일 객체와 복합 객체의 구성을 균일하게 처리할 수 있다.

  • Component : Leaf와 Composite가 구현해야하는 클래스 또는 인터페이스로, Leaf와 Composite 모두 Component라는 같은 타입으로 다뤄짐
  • Leaf : 단일 객체로 Composite의 부분 객체로 들어가게 됨. 이 때 Component 형태로 들어가게 됨
  • Composite : 집합 객체로 Leaf 객체나 Composite을 부분으로 둠. 이 때 Component의 형태로 들어감 → Client는 이 Composite을 통해 부분 객체를 다룰 수 있음

Composite Pattern 예제

  • 식당 메뉴를 출력하는 예제
    • Composite 인 Menu를 계속 늘릴 수 있기 때문에 확장하기 쉬움

  • Component : MenuComponent 클래스
    • MenuItem과 Menu 클래스가 구현해야하는 클래스
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 double getPrice() {
        throw new UnsupportedOperationException();
    }
     
    public boolean isVegetarian() {
        throw new UnsupportedOperationException();
    }
     
    public void print() {
        throw new UnsupportedOperationException();
    }
}
  • Leaf : MenuItem 클래스
    • 단 객체인 MenuItem 클래스로 Component형태로 Composite 클래스에 부분 객체로 들어감
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;
    }
 
    public String getName() {
        return name;
    }
 
    public String getDescription() {
        return description;
    }
 
    public double getPrice() {
        return price;
    }
 
    public boolean isVegetarian() {
        return vegetarian;
    }
     
    public void print() {
        System.out.print(" " + getName());
        if (isVegetarian()) {
            System.out.print("(v)");
        }
        System.out.println(", " + getPrice());
        System.out.println("     -- " + getDescription());
    }
}
  • Composite : Menu 클래스
    • 집합 객체로 Leaf나 Composite을 Component 형태로 둠
    • Composite의 역할은 하위 요소가 있는 구성 요소의 동작을 정의하고 하위 요소의 속성을 저장하는 것
public class Menu extends MenuComponent {
    ArrayList<MenuComponent> menuComponents = new ArrayList<MenuComponent>();
    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);
    }
 
    public String getName() {
        return name;
    }
     
    public String getDescription() {
        return description;
    }
         
    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();
        }
    }
}
  • Client : Waitress 클래스
    • Client는 Component 클래스를 참조해서 메소드를 호출
public class Waitress {
    MenuComponent allMenus;
 
    public Waitress(MenuComponent allMenus) {
        this.allMenus = allMenus;
    }
 
    public void printMenu() {
        allMenus.print();
    }
}
  • Main 함수 
    • Component에 Menu, MenuItem 등등 추가
      • **Component에 Menu와 MenuItem을 add하는 과정
        • 이러한 과정들은 클린코드 측면에서 봤을 때 별로 좋지 않다고 생각
        • Component에 따라서 add하는 부분을 각각 메소드나 객체에서 처리하는 것으로 하면 더 깔끔하지 않았을까 하는 생각..
public class MenuTest {
    public static void main(String [] argS){
        MenuComponent pancakeHouseMenu = new Menu("PANCAKE HOUSE MENU", "Breakfast");
        MenuComponent dinerMenu = new Menu("DINER MENU", "Lunch");
        MenuComponent cafeMenu = new Menu("CAFE MENU", "Dinner");
        MenuComponent dessertMenu = new Menu("DESSERT MENU", "Dessert of course!");
         
        MenuComponent allMenus = new Menu("ALL MENUS", "All menus combinded");
         
        allMenus.add(pancakeHouseMenu);
        allMenus.add(dinerMenu);
        allMenus.add(cafeMenu);
 
        pancakeHouseMenu.add(new MenuItem("K&B's Pancake Breakfast", "Pancakes with scrambled eggs, and toast",true, 2.99));
        pancakeHouseMenu.add(new MenuItem("Regular Pancake Breakfast", "Pancakes with fried eggs, sausage", false, 2.99));
        pancakeHouseMenu.add(new MenuItem("Blueberry Pancakes", "Pancakes made with fresh blueberries", true, 3.49));
        pancakeHouseMenu.add(new MenuItem("Waffles", "Waffles, with your choice of blueberries or strawberries", true, 3.59));
 
        dinerMenu.add(new MenuItem("Vegetarian BLT", "(Fakin') Bacon with lettuce & tomato on whole wheat", true, 2.99));
        dinerMenu.add(new MenuItem("BLT", "Bacon with lettuce & tomato on whole wheat", false, 2.99));
        dinerMenu.add(new MenuItem("Soup of the day", "Soup of the day, with a side of potato salad", false, 3.29));
        dinerMenu.add(new MenuItem("Hotdog", "A hot dog, with saurkraut, relish, onions, topped with cheese", false, 3.05));
        dinerMenu.add(new MenuItem("Pasta", "Spaghetti with Marinara Sauce, and a slice of sourdough bread", true, 3.89));
        dinerMenu.add(dessertMenu);
 
        cafeMenu.add(new MenuItem("Burrito", "A large burrito, with whole pinto beans, salsa, guacamole", true, 4.29));
 
        dessertMenu.add(new MenuItem("Apple Pie", "Apple pie with a flakey crust, topped with vanilla icecream", true, 1.59));
 
        Waitress waitress = new Waitress(allMenus);
        waitress.printMenu();
    }
}
  • 출력 결과

  • 트리 구조로 객체들의 구성을 표현

결론

  • 객체들의 관계를 트리 구조로 구성하여 부분 - 전체 계층을 표현하는 구조 패턴(Structural Pattern)
  • 장점
    • 객체들이 모두 같은 타입으로 취급되기 때문에 새로운 클래스 추가가 용이
    • 단일 객체, 집합 객체를 구분하지 않고 코드 작성이 가능
  • 단점
    • 설계를 일반화시켜 객체간의 구분이나 제약이 힘들 수 있음

 

 


본문은 Head First Design Pattern (2004) 를 참고하여 공부하고 작성하였음


 

'Design Pattern' 카테고리의 다른 글

14. Proxy Pattern  (0) 2022.09.06
13. State Pattern  (0) 2022.09.05
11. Iterator Pattern  (0) 2022.08.31
10. Template Method Pattern  (0) 2022.08.29
09. Facade Pattern  (0) 2022.08.27
Comments