package iterator;
public class MenuItem {
String name;
String description;
boolean vegetarian;
double price;
public String getName() {
return name;
}
public String getDescription() {
return description;
}
public boolean isVegetarian() {
return vegetarian;
}
public double getPrice() {
return price;
}
public MenuItem(String name, String description, boolean vegetarian, double price) {
this.name = name;
this.description = description;
this.vegetarian = vegetarian;
this.price = price;
}
}
이러한 클래스에 의거, 메뉴를 만든 식당이 두개 있다고 하자.
package iterator;
import java.util.ArrayList;
public class PancakeHouseMenu {
ArrayList menuItems;
public PancakeHouseMenu() {
menuItems = new ArrayList();
addItem("K&B Pancake Set", "Scrambled egg and Toast w/ pancake", true, 2.99);
addItem("Regular Pancake Set", "Scrambled egg and Sausage w/ pancake", false, 2.99);
addItem("BlueBerry Pancake Set", "BlueBerry and Syrup pancake", true, 3.49);
addItem("Waffle", "Choose Your Favourite Syrup", true, 3.59);
}
public void addItem(String name, String description, boolean vegetarian, double price) {
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
menuItems.add(menuItem);
}
public ArrayList getMenuItems() {
return menuItems;
}
}
package iterator;
import java.util.ArrayList;
public class DinerMenu {
static final int MAX_ITEM = 6;
int numberOfItems = 0;
MenuItem[] menuItems;
public DinerMenu() {
menuItems = new MenuItem[MAX_ITEM];
addItem("Veggie BLT", "BLT for Vegetarian", true, 2.99);
addItem("BLT", "BLT for MeatLover", true, 2.99);
addItem("Today's Soup", "Mushroom Potato Soup", true, 3.29);
addItem("HotDog", "Isotope HotDog", true, 3.05);
}
public void addItem(String name, String description, boolean vegetarian, double price) {
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
if (numberOfItems >= MAX_ITEM) {
System.out.println("Menu is full");
} else {
menuItems[numberOfItems] = menuItem;
numberOfItems++;
}
}
public MenuItem[] getMenuItems() {
return menuItems;
}
}
팬케이크 식당은 ArrayList를 사용했고, Diner식당은 array를 사용했다. 이 두 식당을 합병하려면 어떻게 해야할까?
굳이 방법을 찾자면 waitress 클래스에서 새로운 리스트를 만들고 양쪽의 내용물을 for 문으로 돌면서 전부 꺼내서 새로운 리스트에 넣는것이다.
근데 이러한 방식을 쓸 경우, 캡슐화가 되어있지도 않으며, 코드도 중복되고, 구현에 의존하는 코딩이 된다.
Iterator 패턴과 어댑터를 통해 반복을 캡슐화 할 수 있다.
package iterator;
public interface Iterator {
boolean hasNext();
Object next();
}
package iterator;
public class DinerMenuIterator implements Iterator{
MenuItem[] items;
int position = 0;
public DinerMenuIterator(MenuItem[] items) {
this.items = items;
}
@Override
public boolean hasNext() {
if (position >= items.length || items[position] == null) {
return false;
} else {
return true;
}
}
@Override
public Object next() {
MenuItem menuitem = items[position];
position++;
return menuitem;
}
}
iterator 인터페이스를 받아서 이를 구현할 수 있다.
이 구현한 클래스를 이제 DinerMenu에 적용하면 된다.
package iterator;
import java.util.ArrayList;
public class DinerMenu {
static final int MAX_ITEM = 6;
int numberOfItems = 0;
MenuItem[] menuItems;
public DinerMenu() {
}
public void addItem(String name, String description, boolean vegetarian, double price) {
}
public Iterator createIterator() {
return new DinerMenuIterator(menuItems);
}
}
이런식으로 get 부분을 iterator로 바꾸고 처리할 수 있다.
마찬가지로 ArrayList를 쓴 것도 처리가 가능하다.
package iterator;
import java.util.ArrayList;
public class PanCakeHouseMenuIterator implements Iterator{
ArrayList<MenuItem> items;
int position = 0;
public PanCakeHouseMenuIterator(ArrayList items) {
this.items = items;
}
@Override
public boolean hasNext() {
if (position >= items.size() || items.get(position) == null) {
return false;
} else {
return true;
}
}
@Override
public Object next() {
MenuItem menuitem = items.get(position);
position++;
return menuitem;
}
}
package iterator;
public class Waitress {
PancakeHouseMenu pancakeHouseMenu;
DinerMenu 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("Menu\n---\nMorning Menu");
printMenu(pancakeIterator);
System.out.println("\nLunch Menu");
printMenu(dinerIterator);
}
private void printMenu(Iterator iterator) {
while (iterator.hasNext()) {
MenuItem menuItem = (MenuItem)iterator.next();
System.out.print(menuItem.getName() + ", ");
System.out.print(menuItem.getDescription() + " -- ");
System.out.println(menuItem.getPrice());
}
}
}
각 메뉴객체에서 이터레이터를 만들고, 이 이터레이터의 hasNext기능을 통해서 하나씩 빼가면서 출력을 할 수 있다.
package iterator;
public class MenuTestDriver {
public static void main(String[] args) {
PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
DinerMenu dinerMenu = new DinerMenu();
Waitress waitress = new Waitress(pancakeHouseMenu, dinerMenu);
waitress.printMenu();
}
}
드라이버 코드를 돌려보면
Menu
---
Morning Menu
K&B Pancake Set, Scrambled egg and Toast w/ pancake -- 2.99
Regular Pancake Set, Scrambled egg and Sausage w/ pancake -- 2.99
BlueBerry Pancake Set, BlueBerry and Syrup pancake -- 3.49
Waffle, Choose Your Favourite Syrup -- 3.59
Lunch Menu
Veggie BLT, BLT for Vegetarian -- 2.99
BLT, BLT for MeatLover -- 2.99
Today's Soup, Mushroom Potato Soup -- 3.29
HotDog, Isotope HotDog -- 3.05
Process finished with exit code 0
메뉴판이 예쁘게 나온다.
이러한 기능을 통해서 우리가 얻은것은
이터레이터를 따로 구현해서 이쪽이 반복문 처리를 해준다. 따라서 각 메뉴는 그냥 가져다 쓰기만하고 (createIterator) 세부적인 내용은 몰라도 된다.
반복문을 한번만 써도 된다!
각자 다른 자료형이여도 통일이 가능하다. (심지어 메뉴 클래스가 달라도 구현가능하다)
가능하면 통일해서 쓰자...
public Iterator createIterator() {
return menuItems.iterator();
}
panCake의 경우 이미 ArrayList를 사용하고 있기때문에, 이렇게 createIterator 부분만 변경해주면 된다.
public void remove() {
if (position <= 0) {
throw new IllegalStateException();
}
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;
}
DinerMenu의 경우 array를 사용하고있었고 이를 처리하기위해 DinerMenuIterator라는 별도의 클래스를 사용하고 있었다. 따라서 이 클래스의 util.Iterator가 가지고있는 remove를 추가로 구현해주어야한다.
package composite;
import java.util.Iterator;
public interface Menu {
public Iterator createIterator();
}
그리고 이를 통합관리하는 Menu Interface를 만들면 된다.
package composite;
import java.util.Hashtable;
import java.util.Iterator;
public class CafeMenu implements Menu{
Hashtable menuItems = new Hashtable();
public CafeMenu() {
addItem("Veggie burger and Air-fried chips", "Veggie burger for veggies", true, 3.99);
addItem("Today's Soup", "Random Soup", false, 3.69);
addItem("Burrito", "Salsa, Guacamole", true, 4.29);
}
public void addItem(String name, String description, boolean vegetarian, double price) {
MenuItem menuItem = new MenuItem(name, description, vegetarian, price);
menuItems.put(menuItem.getName(), menuItem); // 이름을 키로 쓴다
}
@Override
public Iterator createIterator() {
return menuItems.values().iterator();
}
}
새로운 메뉴가 추가된다 하더라도 이런식으로 메뉴 클래스를 작성하고 (이번경우는 해시테이블을 사용한다.)
똑같이 createIterator해주면 된다.
public class Waitress {
Menu pancakeHouseMenu;
Menu dinerMenu;
Menu cafeMenu;
public Waitress(Menu pancakeHouseMenu, Menu dinerMenu, Menu cafeMenu) {
this.pancakeHouseMenu = pancakeHouseMenu;
this.dinerMenu = dinerMenu;
this.cafeMenu = cafeMenu;
}
public void printMenu() {
Iterator pancakeIterator = pancakeHouseMenu.createIterator();
Iterator dinerIterator = dinerMenu.createIterator();
Iterator cafeIterator = cafeMenu.createIterator();
package composite;
public class MenuTestDriver {
public static void main(String[] args) {
PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
DinerMenu dinerMenu = new DinerMenu();
CafeMenu cafeMenu = new CafeMenu();
Waitress waitress = new Waitress(pancakeHouseMenu, dinerMenu, cafeMenu);
waitress.printMenu();
}
}
이제 어떤 자료형을 쓰는 클래스가 와도 반복문 하나로 모두 출력할 수 있다.
프레임워크 컬렉션은 그냥 클래스와 인터페이스를 모아둔 것이다.
ArrayList, Vector, Stack, PQ 같은것들이 전부 모여있다. 이 프레임워크 안에서 Iterator를 사용하면, 안에 있는 인터페이스에만 의존하는 구조가 완성된다.