composite pattern

2023. 10. 30. 11:05디자인패턴

728x90

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) {}

}

}

}

 

728x90

'디자인패턴' 카테고리의 다른 글

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