요약
- 반복자 패턴과 컴포지트 패턴
- 컴포지트 패턴으로 메뉴 디자인하기
- 메뉴 구성 요소 구현하기
- 메뉴 항목 구현하기
- 메뉴 구현하기
- 종업원 코드에 컴포지트 적용하기
- 디자인 도구상자 안에 들어가야 할 도구들
메모
컴포지트 패턴으로 메뉴 디자인하기
- 구성 요소 인터페이스를 먼저 만듦.
- 메뉴와 메뉴 항목 모두에 적용되는 공통 인터페이스 역할을 함.
- 메뉴와 메뉴 항목을 똑같은 방법으로 처리할 수 있음. → 같은 메소드를 호출할 수 있음.
- Waitress 클래스는 MenuComponent 인터페이스를 사용해서 Menu와 MenuItem에 모두 접근함.
- MenuComponent는 MenuItem과 Menu 모두에 적용되는 인터페이스임.
- MenuItem과 Menu에서 모두 print()를 오버라이드 함.
- MenuItem에서 쓰일 법한 메소드만 오버라이드하고, 나머지는 기본 구현을 그대로 사용함.
- ex) add() 메소드는 구성 요소를 추가하는 메소드이므로 MenuItem에서는 쓸 필요 없음.
- 구성 요소는 Menu에서만 추가할 수 있기 때문
- Menu에서도 역시 쓰일 법한 메소드만 오버라이드 함.
- 메뉴를 추가하거나 삭제하는 add(), remove() 메소드는 오버라이드해야 함.
메뉴 구성 요소 구현하기
- MenuComponenet는 잎과 복합 노드 모두에서 쓰이는 인터페이스 역할을 함.
- 모든 구성요소에서 MenuComponent 인터페이스를 구현해야만 함.
- 하지만 잎과 노드는 각각 역할이 다르므로 모든 메소드에 알맞는 기본 메소드는 구현이 불가능 함.
- 따라서 ,자기 역할에 맞지 않는 상황을 기준으로 예외를 던지는 코드를 기본 구현으로 함.
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();
}
}
- 모두 UnsupportedOperationExeption을 던지도록 하면, 자기 역할에 맞지 않는 메소드는 오버라이드하지 않고 기본 구현을 그대로 사용할 수 있음.
메뉴 항목 구현하기
- 컴포지트 다이어그램에서 잎에 해당하는 클래스임.
- 복합 객체의 원소에 해당하는 행동을 구현해야 함.
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());
}
)
- MenuComponent 인터페이스를 확장해야 함.
- 게터 메소든느 기존에 사용하던 것과 거의 같음.
- print() 메소드를 오버라이드함.
- MenuItem에서 이 메소드를 호출하면, 메뉴에 수록해야할 모든 내용이 출력됨.
메뉴 구현하기
- 복합 객체 클래스인 Menu만 준비하면 됨.
- 복합 객체에는 MenuItem은 물론, 다른 Menu도 저장할 수 있음.
public class Menu extends MenuComponent {
List<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("----------------------");
for (MenuComponent menuComponent : menuComponents) {
menuComponent.print();
}
}
}
- Menu에는 MenuComponent 형식의 자식을 몇개든 저장할 수 있음.
- print() 메소드의 경우, Menu에 있는 모든 구성요소를 대상으로 반복 작업을 처리할 수 있음.
종업원 코드에 컴포지트 적용하기
- 종업원 코드는 다른 모든 메뉴를 포함하고 있는 최상위 메뉴 구성 요소만 넘겨주면 됨.
public class Waitress {
MenuComponent allMenus;
public Waitress(MenuComponent allMenus) {
this.allMenus = allMenus;
}
public void printMenu() {
allMenus.print();
}
}
- 여기서 allMenus에 최상위 메뉴를 지정하면 됨.
- 이렇게 했을 때, 한 클래스에서 한 역할만 맡아야 한다고 했으나, 이 패턴에서는 한 클래스에 2가지 역할을 ㄴ헌는다고 생각할 수 있음.
- 계층 구조를 관리하는 일과, 메뉴 관련 작업을 처리해야 함.
- 컴포지트 패턴에서는 단일 역할 원칙을 깨는 대신, 투명성을 확보하는 패턴이라 할 수 있음.
- 투명성(transparency) → Componenet 인터페이스에 자식들을 관리하는 기능과 잎으로써의 기능을 전부 넣어서 클라이언트가 복합 객체와 잎을 똑같은 방식으로 처리할 수 있도록 만듦.
- 어떤 원소가 복합객체이고, 잎인지가 클라이언트에게 투명하게 보임.
- 물론, 두 종류의 기능이 모두 들어가기에 안전성은 떨어짐
- 이러한 문제를 디자인상의 결정 사항에 속한다고 함.
- 따라서, 상황에 따라 원칙을 적절하게 사용해야 함.
- 디자인 원칙에서 제시하는 가이드라인을 따르면 좋지만, 그 원칙이 디자인에 어떤 영향을 끼칠지 항상 고민하고 원칙을 적용해야 함.
-
디자인 도구상자 안에 들어가야 할 도구들
- 반복자 패턴
- 컬렉션의 구현 방법을 노출하지 않으면서 집합체 내의 모든 항목에 접근하는 방법을 제공함.
- 컴포지트 패턴
- 객체를 트리구조로 구성해서 부분-전체 계층 구조를 구현함. 컴포지트 패턴을 사용하면 클라이언트에서 개별 객체와 복합 객체를 또같은 방법으로 다룰 수 있음.