2023. 10. 30. 11:05ㆍ디자인패턴
composite pattern
: 객체들을 트리 구조로 구성하여 부분과 전체를 나타내는 계층구조로 만든다.
클라이언트에서 개별 객체와 다른 객체들과 구성된 복합 객체를 똑같은 방법으로 다룰 수 있다.

메뉴, 서브메뉴를 똑같은 구조에 넣어 부분-전체 계층구조를 생성.

composite pattern을 메뉴에 적용시켜 보기

한 클래스에서 한 역할만 맡아야 하지만 이 패턴은 계층구조를 관리하는 일과
메뉴 관련 작업을 처리하는 일 2가지를 처리한다.
composite pattern에서는 단일 역할 원칙을 깨는 대신 투명성을 확보하기 위한 패턴이다.
-> composite과 leaf를 똑같은 방식으로 처리하도록 할 수 있다.
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();
}
}
예외처리를 하는 이유: 어떤 메서드는 MenuItem에서만 쓸 수 있고,
또 어떤 메서드는 Menu에서만 쓸 수 있기 때문에 기본적으로 예외처리
-> 자기 역할에 맞지 않는 메서드는 오버라이딩을 하지 않고 기본 구현을 그대로 사용할 수 있다.
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.println(getName());
if(isVegetarian()) System.out.println("(v)");
System.out.println(getPrice());
System.out.println(getDescription());
}
}
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);
}
public String getName(){
return name;
}
public String getDescription() {
return description;
}
public void print() {
System.out.println(getName());
System.out.println(getDescription());
System.out.println("----------------------------------------------------");
Iterator<MenuComponent> iterator = menuComponents.iterator(); //Menu 정보 뿐아니라 Menu안의 아이템까지 출력
while(iterator.hasNext()){
MenuComponent menuComponent = iterator.next();
menuComponent.print();
}
}
}
public class Waitress {
MenuComponent allMenus;
public Waitress(menuComponent allMenus) {
this.allMenus = allMenus;
}
public void printMenu(){
allMenus.print();
}
}
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(dessertMenu);
dessertMenu.add(new menuItem("애플 파이", "바삭바삭한 크러스트에 바닐라아이스크림이", true, 1.59));
Waitress waitress = new Waitress(allMenus);
waitress.printMenu();
}
}
composite pattern 내에서 iterator pattern을 활용해 보기
public class Menu extends MenuComponent {
//나머지 코드는 그대로
public Iterator<MenuComponent> createIterator() {
return new CompositeIterator(menucomponents.iterator());
}
}
public class CompositeIterator implements Iterator {
Stack<Iterator<MenuComponent>> stack = new Stack();
public CompositeIterator(Iterator iterator){
stack.push(iterator);
}
public MenuComponent next() {
Iterator<MenuComponent> iterator = stack.peek();
MenuComponent component = iterator.next();
if(component instanceof Menu) stack.push(((Menu)component).iterator());
return component;
}
public boolean hasNext() {
if(stack.empty()) return false;
Iterator<MenuComponent> iterator = stack.peek();
if(!iterator.hasNext()){
stack.pop();
return hasNext();
} else {
return true;
}
}
public void remove() {
throw new UnsupporttedOperationException();
}
}
public class MenuItem extends MenuComponent {
//나머지 코드는 그대로
public Iterator<MenuComponent> createIterator() {
return new NullIterator() //널 반복자.
}
}
MenuItem 클래스에 대해서는 반복작업을 할 대상이 없다.
구현 1. 그냥 null 리턴.
-> 클라이언트에서 리턴값이 null인지 아닌지 판단하기 위해 조건문을 써야 하는 단점이 있다.
구현 2. hasNext()가 호출되었을 때 무조건 false를 리턴하는 반복자 리턴.
-> 이렇게 아무 일도 하지 않는 반복자를 NullIterator라 한다.
-> 반복자이기 때문에 클라이언트에서 리턴된 객체가 null 인지 신경 쓸 필요가 없다.
public class NullIterator implements Iterator<Object>{
@Override
public boolean hasNext() {
return false;
}
@Override
public Object next() {
return null;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}
public class Waitress {
MenuComponent allMenus;
public Waitress(MenuComponent allMenus) {
this.allMenus = allMenus;
}
public void printMenu() {
allMenus.print();
}
public void printVegetarianMenu(){
Iterator<MenuComponent> iterator = allMenus.createIterator();
while(iterator.hasNext()){
Menucomponent menuComponent = iterator.next();
try{
if(menuComponent.isVegetarian()) menuComponent.print();
}catch(UnsupportedOpoerationException e) {}
}
}
}
'디자인패턴' 카테고리의 다른 글
| Proxy Pattern (0) | 2023.11.07 |
|---|---|
| State pattern (0) | 2023.10.31 |
| The iterator pattern (0) | 2023.10.22 |
| The Template Method Pattern (0) | 2023.10.22 |
| The Adapter and Facade Patterns (0) | 2023.10.21 |