Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

이터레이터 패턴 (Iterator) #136

Open
khyunjiee opened this issue Oct 31, 2021 · 0 comments
Open

이터레이터 패턴 (Iterator) #136

khyunjiee opened this issue Oct 31, 2021 · 0 comments

Comments

@khyunjiee
Copy link
Collaborator

이터레이터 패턴 (iterator pattern)

컬렉션 구현 방법을 노출시키지 않으면서 그 집합체 안에 들어있는 모든 항목에 접근할 수 있는 방법을 제공한다.

컬렉션 객체 안에 들어있는 모든 항목에 접근하는 방식이 통일되어 있으면 어떤 종류의 집합체에 대해서도 사용할 수 있는 다형적인 코드를 만들 수 있다.

이터레이터 패턴을 사용하면 모든 항목에 일일이 접근하는 작업을 컬렉션 객체가 아닌 반복자 객체에서 맡게 된다.

이렇게 하면 집합체의 인터페이스 및 구현이 간단해질 뿐 아니라, 집합체에서는 반복작업에서 손을 떼고 원래 자신이 할 일 (객체 컬렉션 관리) 에만 전념할 수 있다.

예제

두 개의 서로다른 식당이 있고 각각의 식당에서 메뉴를 구현한다고 가정해보자

public class MenuItem {
	String name;
	String description;
	String vegetarian;
	double price;

	public MenuItem(String name, String description, boolean vegetarian, double price){
		this.nae = 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 class PancakeHouseMenu {
	ArrayList<MenuItem> menuItems;
	
	public PancakeHouseMenu() {
		this.menuItems = new ArrayList();
		additem("K&B 팬케이크 세트","스크램블드 에그와 토스트가 곁들여진 펜케이크",true,2.99);
		additem("레귤러 팬케이크 세트","달걀 후라이와 소시지가 곁들여진 펜케이크",false,2.99);
		additem("블루베리 펜케이크","신선한 블루베리와 블루베리 시럽으로 만든 펜케이크",true,3.49);
		additem("와플","와플, 취향에 따라 블루베리나 딸기를 얹을 수 있습니다.",true,3.59);
	}
	
	public void additem(string name, String description, boolean vegetarian, double price) {
		MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
		menuItem.add(menuItem);
	}
	
	public ArrayList<MenuItem> getMenuItems() {
		return menuItems;	
	}

	//기타 메소드
}
public class DinerMenu {
	static final int MAX_ITEMS = 6;
	int numberOfItems = 0;
	MenuItem[] menuItems;
	
	public DinerMenu() {
		this.menuItems = new MenuItem[MAX_ITEMS];
		additem("채식주의자용 BLT","통밀 위에 (식물성)베이컨, 상추, 토마토를 얹은 메뉴",true,2.99);
		additem("BLT","통밀 위에 베이컨, 상추, 토마토를 얹은 메뉴",false,2.99);
		additem("오늘의 스프","감자 샐러드를 곁들인 오늘의 스프",false,3.29);
		additem("핫도그","사워크라우트, 갖은 양념, 양파, 치즈가 곁들여진 핫도그",false,3.05);
	}

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

	//기타 메소드

}

이 위의 두 개의 메뉴를 사용하는 클라이언트를 만들어보자.

클라이언트의 기능은 아래와 같다.

  1. printMenu() : 메뉴에 있는 모든 항목 출력
  2. printBreakfastMenu() : 아침 식사 항목만 출력
  3. printLunchMenu() : 점심 식사 항목만 출력
  4. printVegetarianMenu() : 채식주의자용 메뉴 항목만 출력
  5. isItemVegetarian(name) : name 항목이 채식주의자용 메뉴이면 true, 아니면 false
PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
ArrayList<MenuItem> breakfastItems = pancakeHouseMenu.getMenuItems();

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

for ( int i=0; i < breakfaseItems.size(); i++) }
	MenuItem menuItem = breakfastItems.get(i);
	System.out.println(menuItem.getName());
	System.out.println(menuItem.getPrice());
	System.out.println(menuItem.getDescription());
}

for ( int i=0; i < lunchItems.length; i++) {
	MenuItem menuItem = lunchItems[i];
	System.out.println(menuItem.getName());
	System.out.println(menuItem.getPrice());
	System.out.println(menuItem.getDescription());
}

메뉴의 모든 항목을 출력하려면 위와 같은 코드를 작성하게 될 것이다.

다른 메소드들도 결국 위의 코드와 비슷하게 작성될 것이다.

항상 두 메뉴를 이용하고, 각 아이템에 대해서 반복적인 작업을 수행하기 위해 두 개의 순환문을 써야 한다.

이후에 메뉴가 더 추가된다면? 이 상황이 계속 반복될 것이다.

그렇면 반복을 분리해 Iterator 라는 객체를 만들자

Iterator<MenuItem> iterator = breakfastMenu.createIterator();
while(iterator.hasNext()){
	MenuItem menuItem = iterator.next();
}

Iterator<MenuItem> iterator = lunchMenu.createIterator();
while(iterator.hasNext()){
	MenuItem menuItem = iterator.next();
}

사용자 정의 Iterator 인터페이스를 만들어도 되지만, java.util.Iterator 인터페이스를 사용해서 적용해보자

public interface Menu {
	public Iterator<MenuItem> createIterator();
}
public class PancakeHouseMenu **implements Menu** {
	ArrayList<MenuItem> menuItems;
	
	public PancakeHouseMenu() {
		this.menuItems = new ArrayList();
		additem("K&B 팬케이크 세트","스크램블드 에그와 토스트가 곁들여진 펜케이크",true,2.99);
		additem("레귤러 팬케이크 세트","달걀 후라이와 소시지가 곁들여진 펜케이크",false,2.99);
		additem("블루베리 펜케이크","신선한 블루베리와 블루베리 시럽으로 만든 펜케이크",true,3.49);
		additem("와플","와플, 취향에 따라 블루베리나 딸기를 얹을 수 있습니다.",true,3.59);
	}
	
	public void additem(string name, String description, boolean vegetarian, double price) {
		MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
		menuItem.add(menuItem);
	}
	
	public ArrayList<MenuItem> getMenuItems() {
		return menuItems;	
	}

	**@Override
	public Iterator<MenuItem> createIterator() {
		// ArrayList 컬렉션에 반복자를 리턴하는 iterator() 메소드 활용
		return menuItems.iterator();
	}**
}
public class DinerMenuIterator implements Iterator<MenuItem> {
	Menuitem[] list;
	int position = 0;

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

	@Override	
	public MenuItem next() {
		MenuItem menuItem = list[position];
		position += 1;
		return menuItem;
	}

	@Override
	public boolean hasNext() {
		if(position >= list.length || list[position] == null) return false;
		else return true;
	}
	
	// 반드시 기능을 제공하지 않아도됨 그렇다면 java.lang.UnsupportedOperationException을 던지도록 하면됨
	@Override
	public void remove() { 
		if(position <= 0) 
			Throw new IllegalStateException("next()가 한번도 호출되지 않음.");
		
		if(list[position-1] != null){
			for(int i=position-1; i<(list.length-1); i++){
				list[i] = list[i+1];
			}
			list[list.length-1] = null;
		}
	}

}
public class DinerMenu **implements Menu** {
	static final int MAX_ITEMS = 6;
	int numberOfItems = 0;
	MenuItem[] menuItems;
	
	public DinerMenu() {
		this.menuItems = new MenuItem[MAX_ITEMS];
		additem("채식주의자용 BLT","통밀 위에 (식물성)베이컨, 상추, 토마토를 얹은 메뉴",true,2.99);
		additem("BLT","통밀 위에 베이컨, 상추, 토마토를 얹은 메뉴",false,2.99);
		additem("오늘의 스프","감자 샐러드를 곁들인 오늘의 스프",false,3.29);
		additem("핫도그","사워크라우트, 갖은 양념, 양파, 치즈가 곁들여진 핫도그",false,3.05);
	}

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

	**@Override
	public Iterator<MenuItem> createIterator() {
		return new DinerMenuIterator(menuItems);
	}**

}
public class Waitress {
	ArrayList<Menu> menus;
	
	public Waitress(ArrayList<Menu> menus) {
		this.menus = menus;
	}
	
	public void printMenu() {
		Iterator menuIterator = menus.iterator();

		while(menuIterator.hasNext()){
			Menu menu = menuIterator.next();			
			printMenu(menu.createIterator());
		}
	}
	
	private void printMenu(Iterator<MenuItem> iterator) {
		while(iterator.hasNext()) {
			MenuItem menuItem = iterator.next();
			System.out.println(menuItem.getName());
			System.out.println(menuItem.getPrice());
			System.out.println(menuItem.getDescription());		
		}
	}
}
public class MenuTestDrive {
	public static void main(String args[]) {
		ArrayList<Menu> menuList = new ArrayList();
		menuList.add(new PancakeHouseMenu());
		menuList.add(new DinerMenu());
		
		Waitress waitress = new Waitress(menuList);
		waitress.printMenu();
	}
}

위와 같이 Iterator 로 분리한 후에 Waitress 클래스에서 컬렉션을 받아서 프린트하는 메소드를 작성하면 main 에서는 내부 로직을 신경쓰지 않고 모든 항목들에 대해 반복 작업을 수행할 수 있게 된다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant