-
구조패턴 > 컴포지트 *기타/디자인패턴 2021. 2. 11. 10:09
객체들을 트리 구조로 구성해 부분과 전체를 나타내는 계층 구조로 만들 수 있다.
컴포지트 패턴을 이용하면 클라이언트에서 개별 객체와 다른 객체들로 구성된 복합객체(composite) 를 똑같은 방법으로 다룰 수 있다.
부분-전체 계층구조란, 부분들이 모여있지만, 모든 것을 하나로 묶어서 전체로 다룰 수 있는 구조를 말한다.
컴포지트 패턴을 이용하면 객체의 구성과 개별 객체를 노드로 가지는 트리 형태로 객체를 구축할 수 있다.
이런 복합 구조를 사용하면 복합 객체와 개별 객체에 대해 똑같은 작업을 적용할 수 있다.
즉, 대부분의 경우에 복합 객체와 개별 객체를 구분할 필요가 없어진다.
팬케이크하우스, 객체식당, 카페 식당이 병합되고 객체식당의 메뉴 내에 디저트 메뉴도 새로 만들어 끼워넣는다고 해보자.
객체식당 - Menu (복합객체,Composite)
파스타,피자,리조또 - MenuItem (잎,Leaf)
public class MenuTestDrive { public static void main(String[] args) { MenuComponent pancakaHouse= new Menu("팬케이크하우스 메뉴", "아침"); MenuComponent cafe = new Menu("카페 메뉴", "저녁"); MenuComponent dessert = new Menu("디저트 메뉴", "디저트"); dessert.add(new MenuItem("초코 퍼지","케익",true,8000)); MenuComponent diner = new Menu("객체식당 메뉴", "점심"); diner.add(new MenuItem("파스타","토마토 소스",true,2000)); //객체 식당에 디저트 메뉴를 추가! diner.add(dessert); MenuComponent allmenus = new Menu("전체 메뉴","전체"); allmenus.add(pancakaHouse); allmenus.add(diner); allmenus.add(cafe); Waitress waitress = new Waitress(allmenus); waitress.printMenu(); } } /* name= 전체 메뉴, desc= 전체 name= 팬케이크하우스 메뉴, desc= 아침 name= 객체식당 메뉴, desc= 점심 파스타, 토마토 소스 name= 디저트 메뉴, desc= 디저트 **** 객체식당에 추가된 디저트 출력됨 초코 퍼지, 케익 name= 카페 메뉴, desc= 저녁 */
public abstract class MenuComponent { /** * MenuComponent 를 추가, 삭제, get 하기 위한 메서드 * */ public void add(MenuComponent menuComponent){ } public void remove(MenuComponent menuComponent){ } public MenuComponent getChild(int i){ throw new UnsupportedOperationException(); } /** * menuItem 에서 작업 처리하기 위한 메서드 * menu 에서도 사용사능 */ public String getName(){ throw new UnsupportedOperationException(); } public String getDesc(){ throw new UnsupportedOperationException(); } public double getPrice(){ throw new UnsupportedOperationException(); } public boolean isVegetarian(){ throw new UnsupportedOperationException(); } public void print(){} }
public class MenuItem extends MenuComponent{ String name; String desc; boolean vegetarian; double price; public MenuItem(String name,String desc,boolean vegetarian,double price){ this.name = name; this.desc = desc; this.vegetarian = vegetarian; this.price = price; } @Override public String getName() { return name; } @Override public String getDesc() { return desc; } @Override public boolean isVegetarian() { return vegetarian; } @Override public double getPrice() { return price; } @Override public void print() { System.out.println(getName() + ", " + getDesc()); } }
public class Menu extends MenuComponent { List<MenuComponent> menuComponents = new ArrayList(); String name; String desc; public Menu(String name, String desc){ this.name = name; this.desc = desc; } 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) menuComponents.get(i); } @Override public String getName() { return name; } @Override public String getDesc() { return desc; } @Override public void print() { System.out.println("name= " + name + ", desc= " + desc); //전체 메뉴 // 전체 메뉴 내 팬케이크하우스, 객체식당-디저트, 카페 메뉴 출력 Iterator<MenuComponent> it = menuComponents.iterator(); while(it.hasNext()){ MenuComponent menu = (MenuComponent) it.next(); menu.print(); } } }
베지테리안 메뉴만 출력하고 싶다면?
Waitress 에 printVegetarian 을 추가해보자.
가지고 있는 allmenus 에 있는 menuitem 에서 isVegitarian 확인을 위해 iterator 를 받을 필요가 있어진다.
public class Waitress { MenuComponent allmenus; public Waitress(MenuComponent allmenus){ this.allmenus = allmenus; } public void printMenu(){ allmenus.print(); } public void printVegeratian(){ Iterator it = allmenus.createIterator(); while(it.hasNext()){ MenuComponent menu = (MenuComponent) it.next(); if(menu.isVegetarian()){ menu.print(); } } } }
public class CompositeIterator implements Iterator{ Stack<Iterator<MenuComponent>> stack = new Stack<>(); public CompositeIterator(Iterator iterator){ stack.push(iterator); } @Override public boolean hasNext() { if(stack.isEmpty()) { return false; } else { Iterator<MenuComponent> it = stack.peek(); if(it.hasNext()) { return true; } else { stack.pop(); return hasNext(); } } } @Override public Object next() { Iterator<MenuComponent> it = stack.peek(); MenuComponent component = (MenuComponent) it.next(); if(component instanceof Menu){ stack.push(component.createIterator()); } return component; } }
public class Menu extends MenuComponent{ List<MenuComponent> menuComponents = new ArrayList(); String name; String desc; public Menu(String name, String desc){ .. } public void add(MenuComponent menuComponent){ .. } @Override public void remove(MenuComponent menuComponent) {.. } @Override public MenuComponent getChild(int i) { .. } @Override public String getName() { .. } @Override public String getDesc() { .. } @Override public void print() { .. } @Override public Iterator<MenuComponent> createIterator() { return new CompositeIterator(menuComponents.iterator()); } }
public static void main(String[] args) { MenuComponent pancakaHouse= new Menu("팬케이크하우스 메뉴", "아침"); MenuComponent cafe = new Menu("카페 메뉴", "저녁"); MenuComponent dessert = new Menu("디저트 메뉴", "디저트"); MenuComponent diner = new Menu("객체식당 메뉴", "점심"); MenuComponent allmenus = new Menu("전체 메뉴","전체"); allmenus.add(pancakaHouse); allmenus.add(diner); allmenus.add(cafe); diner.add(new MenuItem("파스타","토마토 소스",true,2000)); diner.add(new MenuItem("스테이크","소고기",false,2000)); diner.add(dessert); // dessert.add(new MenuItem("초코 퍼지","케익",true,8000)); 이렇게 하면 베지테리안에 초코퍼지 2번 나옴! diner.add(new MenuItem("초코 퍼지","케익",true,8000)); Waitress waitress = new Waitress(allmenus); System.out.println("전체"); waitress.printMenu(); System.out.println("채식"); waitress.printVegeratian(); }
출처 :
Head First Design Patterns
반응형'기타 > 디자인패턴' 카테고리의 다른 글
행위패턴 > 이터레이터 * (0) 2021.02.11 행위패턴 > 커맨드 * (0) 2021.02.11 구조패턴 > 퍼싸드 * (0) 2021.02.11 구조패턴 > 데코레이터 * (0) 2021.02.11 구조패턴 > 어댑터 * (0) 2021.02.11