반복자 패턴과 컴포지트 패턴

임종혁·2025년 1월 9일

public class DinerMenu {
	
	static final int MAX_ITMES = 6;
	int numberOfItems = 0;
	MenuItem[] menuItems;
	
	
	public DinerMenu() {
		menuItems = new MenuItem[MAX_ITMES];
		
		addItem("채식주의자용 BLT", "통밀 위에 콩고기 베이컨, 상추, 토마토 얹은 메뉴", true, 2.99);
		addItem("BLT", "BLT", false, 2.99);
		addItem("핫도그", "핫도그", false, 3.29);
		addItem("스프", "스프", false, 3.05);
		
	}


	private void addItem(String name, String description, boolean vegetarain, double price) {
		MenuItem menuItem = new MenuItem(name, description, vegetarain, price);
		if(numberOfItems >= MAX_ITMES) {
			System.out.println("죄송합니다, 메뉴가 꽉 찼습니다. 더이상 추가 할 수 없습니다.");
		}else {
			menuItems[numberOfItems] = menuItem;
			numberOfItems = numberOfItems +1;
		}
	}
	
	public MenuItem[] getMenuItems() {
		return menuItems;
	}

}
import java.util.ArrayList;
import java.util.List;

class PancakeHouseMenu {
	
	List<MenuItem> menuItems;
	
	public PancakeHouseMenu() {
		
		menuItems = new ArrayList<MenuItem>();
		
		addItem("펜케이크 세트","스크램블",true, 2.99);
		addItem("레귤러 펜케이크 세트","소시지",false, 2.99);
		addItem("블루베리 펜케이크 세트","블루베리",true, 3.49);
		addItem("와플","와플",true, 3.59);
	}

	private void addItem(String name, String description, boolean vegetarian, double price) {
	
		MenuItem menuItem = new MenuItem(name, description, vegetarian, price)
		menuItems.add(menuItem);
	}
	
	public List<MenuItem> getMenuItems() {
		return menuItems;
	}

}

다음과 같이 두 메뉴 구현 방식이 틀린 코드를 합치고 싶을 때

PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
ArrayList<MenuItem> breakfastItems = pancakeHouseMenu.getMenuItems();

DinerMenu dinerMenu = new DinerMenu();
MenuItem[] lunchItems = dinerMenu.getMenuItems();


for(int i=0 < breakfastItems.size(); i++) {
....
}
for(int i=0; i<lunchItems.length; i++){
...
}

항상 두 메뉴를 사용하고 각 항목에 반복 작업을 수행하려면 2개의 순한문을 사용해야한다. 다른 레스토랑과 또 합병한다면 3개 순한문이 필요해지는 문제가 발생한다.

Iterator 사용하여 적용하기

Iterator iterator = breakfastMenu.createIterator();

while(iterator.hasNext()){
	MenuItem menuIte = iterator.next();
}

Iterator iterator = lunchMenu.createIterator();
while(iterator.hasNext()){
	MenuItem menuItem = iterator.next();
}
  • 반복 작업을 캡슐화
public interface Iterator{
	boolean hasNext();
    MenuItem next();
}

새로운 메뉴 추가

public class DinerMenu implements Menu {
	
	static final int MAX_ITMES = 6;
	int numberOfItems = 0;
	MenuItem[] menuItems;
	
	
	
	 public DinerMenu() {
		menuItems = new MenuItem[MAX_ITMES];
		
		addItem("채식주의자용 BLT", "통밀 위에 콩고기 베이컨, 상추, 토마토 얹은 메뉴", true, 2.99);
		addItem("BLT", "BLT", false, 2.99);
		addItem("핫도그", "핫도그", false, 3.29);
		addItem("스프", "스프", false, 3.05);
		
	}


	private void addItem(String name, String description, boolean vegetarain, double price) {
		MenuItem menuItem = new MenuItem(name, description, vegetarain, price);
		if(numberOfItems >= MAX_ITMES) {
			System.out.println("죄송합니다, 메뉴가 꽉 찼습니다. 더이상 추가 할 수 없습니다.");
		}else {
			menuItems[numberOfItems] = menuItem;
			numberOfItems = numberOfItems +1;
		}
	}
	
	public MenuItem[] getMenuItems() {
		return menuItems;
	}
	 
	
	public Iterator createIterator() {
		return new DinerMenuIterator(menuItems);
	}

}

전용 Iterator 적용

public class DinerMenuIterator implements Iterator{
	
	MenuItem[] items;
	int position = 0;
	
	

	public DinerMenuIterator(MenuItem[] items) {
		this.items = items;
	}
	

	@Override
	public boolean hasNext() {
		// TODO Auto-generated method stub
		if(position >= items.length || items[position] == null) {
			return false;
		}else {
			return true;
		}
	}

	@Override
	public MenuItem next() {
		// TODO Auto-generated method stub
		MenuItem menuItem = items[position];
		position = position +1;
		return menuItem;
	}

}

종업원

public class Waitress {
	
	Menu pancakeHouseMenu;
	Menu dinerMenu;
	
	public Waitress(PancakeHouseMenu pancakeHouseMenu, DinerMenu dinerMenu) {
		this.pancakeHouseMenu = pancakeHouseMenu;
		this.dinerMenu = dinerMenu;
	}
	
	public void printMenu() {
		Iterator pancakeIterator = pancakeHouseMenu.createIterator();
		Iterator dinerIterator = dinerMenu.createIterator();
		
		System.out.println("메뉴");
		printMenu(pancakeIterator);
		System.out.println("다음");
		printMenu(dinerIterator);
		
	}
	
	private void printMenu(Iterator iterator) {
		while(iterator.hasNext()) {
			MenuItem menuItem = (MenuItem) iterator.next();
			System.out.println(menuItem.getName());
			System.out.println(menuItem.getPrice());
			System.out.println(menuItem.getDescription());
		}
	}

}

테스트시


반복자 패턴 특징

  • 메뉴 구현 법이 캡슐화 되어 있다.
  • 반복자만 구현한다면 다형성을 활용해서 어떤 컬랙션이든 1개의 순환문으로 처리 가능하다
  • 종업원은 인터페이스만 알면 된다

반복자 패턴 정의

반복자 패턴은 컬렉션 구현방법을 노출하지 않으면서 집합체 내의 모든 항목에 접근하는 방법을 제공

단일 영향 원칙

어떤 클래스가 바뀌는 이유는 하나뿐이어야한다

interable

  • interator 같은 경우 hasNext() 로 반복처리
  • interable은 for 순화문 사용가능 허나 Collection 만 가능 (배열 불가)
for(MenuItem item: menu) {

}

Map 사용

public class CafeMenu implements Menu{
	
	Map<String, MenuItem> menuItems = new HashMap();
	public CafeMenu() {
		addItem("버거", "설명", true, 3.39);
	}
	
	public void addItem(String name, String description, boolean vegetarian, double price) {
		MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
		menuItems.put(name, menuItem);
	}

	@Override
	public Iterator<MenuItem> createIterator() {
		// TODO Auto-generated method stub
		return menuItems.values().iterator();
	}

}

컴포지트 패턴

컴포지트 패턴으로 객체를 트리구조로 구성해서 부분 - 전체 계층 구조를 구현 컴포지트 패턴 사용하면 클라이언트에서 개별 객체와 복합 객체를 같은 방식으로 다룰 수 있습니다.

  • 컴포지트 패턴을 사용하면 객체의 구성과 개별 객체를 노드로 가지는 트리 형태의 객체 구조를 만들 수 있다.
  • 이런 복합 구조를 사용하면 복합 객체와 개별 객체 대상으로 똑같은 작업을 작용할 수 있다.

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();
	}

}

menu

public class Menu extends MenuComponent{
	
	List<MenuComponent> menuComponents = new ArrayList();
	String name;
	String description;
	
	public Menu(MenuComponent menuComponent) {
		menuComponents.add(menuComponent);
	}

	public Menu(String name, String description) {
		this.name = name;
		this.description = description;
	}

	@Override
	public void add(MenuComponent menuComponent) {
		// TODO Auto-generated method stub
		menuComponents.add(menuComponent);
	}

	@Override
	public void remove(MenuComponent menuComponent) {
		// TODO Auto-generated method stub
		menuComponents.remove(menuComponent);
	}

	@Override
	public MenuComponent getChild(int i) {
		// TODO Auto-generated method stub
		return menuComponents.get(i);
	}

	@Override
	public String getName() {
		// TODO Auto-generated method stub
		return name;
	}

	@Override
	public String getDescription() {
		// TODO Auto-generated method stub
		return description;
	}


	@Override
	public void print() {
		System.out.println(getName());
		System.out.println(getDescription());
		
		for(MenuComponent menuComponent: menuComponents) {
			menuComponent.print();
		}
	}
}

menuItem

class MenuItem extends MenuComponent {
	String name;
	String description;
	boolean vegetarian;
	double price;
	
	public MenuItem(String name, String description, boolean vegetarian, double price) {
		super();
		this.name = name;
		this.description = description;
		this.vegetarian = vegetarian;
		this.price = price;
	}

	public String getName() {
		return name;
	}


	public String getDescription() {
		return description;
	}


	public boolean isVegetarian() {
		return this.vegetarian;
	}

	public double getPrice() {
		return price;
	}

	public void print() {
		System.out.println(getName());
		if(isVegetarian()) {
			System.out.println("v");
		}
		System.out.println(getPrice());
		System.out.println(getDescription());
	}

}

종업원

public class Waitress {
	
	MenuComponent menuComponent;
	
	public Waitress(MenuComponent allMenus) {
		this.menuComponent = allMenus;
	}
	
	public void printMenu() {
		menuComponent.print();
	}

}

테스트

public class MenuTestDrive {
	
	public static void main(String[] args) {
		MenuComponent pankaeHouseMenu = new Menu("케잌", "아침");
		MenuComponent dessertMenu = new Menu("디저트", "디저트");
		
		MenuComponent allMenu = new Menu("전체", "전체");
		
		allMenu.add(pankaeHouseMenu);
		
		dessertMenu.add(new MenuItem("애플파이", "바삭", true, 3.5));
		
		allMenu.add(dessertMenu);
		
		Waitress waitress = new Waitress(allMenu);
		waitress.printMenu();
	}

}
  • 컴포지트 패턴은 단일 역할 원칙을 깨는 대신 투명성을 확보하는 패턴
  • 계층 구조 관리, 메뉴 관련 작업

0개의 댓글