Iterator 패턴

 

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

 

시나리오

PancakeHouse 식당의 메뉴와, Diner 식당의 메뉴를 통합한다고 가정해본다. 이에따라 두개의 식당에서 사용할수 있는 메뉴에대한 모델을 MenuItem으로 합의를 봤다고 가정한다하지만PancakeHouse 식당에서는 메뉴들을 ArrayListDiner 식당의 메뉴는 배열형태로 담아서 돌려 준다면 ArrayList 별열 형태의 메뉴를 웨이터가 받아서 처리(프린트 ..)하기 위해서는 어떻게 해야할까?

 

메뉴에서 리턴되는 객체 컬랙션의 형식이 다르기때문에코드에대한 중복 문제가 발생한다. 이러게 바뀌는 부분을 어떻게 캡슐화 할수 있을까?

 

       // ArrayList 대한 처리

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

              MenuItem 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.size(); i++) {

              MenuItem menuItem = lunchItems[i];

              System.out.println(menuItem.getName() + " ");

              System.out.println(menuItem.getPrice() + " ");

              System.out.println(menuItem.getDescription() + " ");

       }

 

 

 

특정 구현이 아닌 인터페이스에 맞춰서 프로그래밍 한다는 원칙에따라서. 다음과 같이 메뉴에대한 인터페이스를 다음과 같이 정의 한다.

 

package headfirst.iterator.dinermerger;

 

public interface Menu {

       public Iterator createIterator();

}

 

 

 

그리고 PancakeHouse 식당의 메뉴와, Diner 식당의 메뉴 모두에서 MenuItem목록에대한 것을 Iterator형태로 리턴되도록 만든다.  다음과 같이 웨이터는 동일한 두식당의 메뉴를 동일하게 취급할 수가 있다.

 

package headfirst.iterator.dinermergercafe;

 

import java.util.Iterator;

 

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

 

              System.out.println("MENU\n----\nBREAKFAST");

              printMenu(pancakeIterator);

              System.out.println("\nLUNCH");

              printMenu(dinerIterator);

              System.out.println("\nDINNER");

              printMenu(cafeIterator);

       }

 

       private void printMenu(Iterator iterator) {

              while (iterator.hasNext()) {

                     MenuItem menuItem = (MenuItem) iterator.next();

                     System.out.print(menuItem.getName() + ", ");

                     System.out.print(menuItem.getPrice() + " -- ");

                     System.out.println(menuItem.getDescription());

              }

       }

 

}

 

 

 

다음은 식당에서 Menu인터페이스를 상속하여  메뉴 구성항목들을 Iterator형태로 되돌려주는  메뉴클래스 이다.

 

팬케익하우스 메뉴

 

package headfirst.iterator.dinermergercafe;

 

import java.util.ArrayList;

import java.util.Iterator;

 

public class PancakeHouseMenu implements Menu {

       ArrayList menuItems;

 

       public PancakeHouseMenu() {

              menuItems = new ArrayList();

 

              addItem("K&B's Pancake Breakfast", "Pancakes with scrambled eggs, and toast", true, 2.99);

              addItem("Regular Pancake Breakfast", "Pancakes with fried eggs, sausage", false, 2.99);

              addItem("Blueberry Pancakes", "Pancakes made with fresh blueberries, and blueberry syrup", true, 3.49);

              addItem("Waffles", "Waffles, with your choice of blueberries or strawberries", 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;

       }

 

       /* (non-Javadoc)

        * @see headfirst.iterator.dinermergercafe.Menu#createIterator()

        */

       @Override

       public Iterator createIterator() {

              return menuItems.iterator();

       }

       // other menu methods here

}

 

 

 

 

Diner 식당의 메뉴

 

package headfirst.iterator.dinermergercafe;

 

import java.util.Iterator;

 

public class DinerMenu implements Menu {

       static final int MAX_ITEMS = 6;

       int numberOfItems = 0;

       MenuItem[] menuItems;

 

       public DinerMenu() {

              menuItems = new MenuItem[MAX_ITEMS];

 

              addItem("Vegetarian BLT", "(Fakin') Bacon with lettuce & tomato on whole wheat", true, 2.99);

              addItem("BLT", "Bacon with lettuce & tomato on whole wheat", false, 2.99);

              addItem("Soup of the day", "Soup of the day, with a side of potato salad", false, 3.29);

              addItem("Hotdog", "A hot dog, with saurkraut, relish, onions, topped with cheese", false, 3.05);

              addItem("Steamed Veggies and Brown Rice", "A medly of steamed vegetables over brown rice", true, 3.99);

             addItem("Pasta", "Spaghetti with Marinara Sauce, and a slice of sourdough bread", true, 3.89);

       }

 

       public void addItem(String name, String description, boolean vegetarian, double price) {

              MenuItem menuItem = new MenuItem(name, description, vegetarian, price);

              if (numberOfItems >= MAX_ITEMS) {

                     System.err.println("Sorry, menu is full!  Can't add item to menu");

              } else {

                     menuItems[numberOfItems] = menuItem;

                     numberOfItems = numberOfItems + 1;

              }

       }

 

       public MenuItem[] getMenuItems() {

              return menuItems;

       }

 

       /* (non-Javadoc)

        * @see headfirst.iterator.dinermergercafe.Menu#createIterator()

        */

       @Override

       public Iterator createIterator() {

              return new DinerMenuIterator(menuItems);

              // return new AlternatingDinerMenuIterator(menuItems);

       }

 

       // other menu methods here

}

 

 

  

다음 코드는 위의 패턴을 테스트하는 코드이다.

 

package headfirst.iterator.dinermergercafe;

 

public class MenuTestDrive {

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

              waitress.printVegetarianMenu();

 

              System.out.println("\nCustomer asks, is the Hotdog vegetarian?");

              System.out.print("Waitress says: ");

              if (waitress.isItemVegetarian("Hotdog")) {

                     System.out.println("Yes");

              } else {

                     System.out.println("No");

              }

              System.out.println("\nCustomer asks, are the Waffles vegetarian?");

              System.out.print("Waitress says: ");

              if (waitress.isItemVegetarian("Waffles")) {

                     System.out.println("Yes");

              } else {

                     System.out.println("No");

              }

       }

}

 

 

  

 

Composite 패턴

위의 식당에서 식당 메뉴에 메뉴별 디저트가 추가로 필요하다면 어떻게 할까?

객체들을 트리구조로 구성하여 부분과 전체를 나타내는 계층구조로 만들수 있다. 패턴을 이용하면 클라이언트에서 개별 객체와 다른 객체들로 구성된 복합 객체(Composite) 같은 방법으로 다룰 있다.

 


 

ü  우선 구성요소(Component) 인터페이스 만드는 것부터 시작해야 한다. 인터페이스는 메뉴와 메뉴 항목 모두에 적용되는 공통 인터페이스 역할을 한다.


ü  인터페이스가 있어야 둘을 똑같은 방법으로 처리 할수 있다.


ü  이런 경우 Menu에서 사용하는 것이 MenuItem에서는 필요없을 수도 있고 MenuItem에서 필요로 하는 것이 Menu에서 필요 없을 수도 있는 문제와 잘못 오동작을 일으킬수 있는 문제가 있다.


ü  이를 위해서 구성요소 (Component) 인터페이스의 메소드에서는 기본적으로 UnsupportedOperationException 기본적으로 던지도록 한다. 이렇게 하면 자기 역할에 맞지 않는 메소드는 오버러이드하지 않고 기본 구현을 그대로 사용하면 된다.

 

 


 


다음은 메뉴 구성요소(Component) 코드이다.

 

package headfirst.composite.menu;

 

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 MenuItem코드이다. 클래스 모두메뉴 구성요소(Component) 상속하고 Menu 메뉴와 메뉴아이템을 모두 담아야 하기때문에 새로 Override하여 구현한다. MenuItem 기본 상위클래스에서 구현된 코드를 사용하게되면  UnsupportedOperationException 기본적으로 던지도록 설계 되었기 때문에 안전하다.

 

다음은 Menu코드이다.

 

package headfirst.composite.menu;

 

import java.util.Iterator;

import java.util.ArrayList;

 

public class Menu extends MenuComponent {

       ArrayList menuComponents = new ArrayList();

       String name;

       String description;

 

      // MenuItem이나 Menu 추가, 삭제할 있다.

       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 (MenuComponent) menuComponents.get(i);

       }

 

       public String getName() {

              return name;

       }

 

       public String getDescription() {

              return description;

       }

 

       public void print() {

              System.out.print("\n" + getName());

              System.out.println(", " + getDescription());

              System.out.println("---------------------");

 

             // Iterator패턴을 사용한다.

             // 메뉴와 메뉴항목에서 모두 print() 구현하기때문에

             // 그냥 print() 호출하고 나머지는 개체에서 알아서 처리하길 기다리면 된다.

              Iterator iterator = menuComponents.iterator();

              while (iterator.hasNext()) {

                     MenuComponent menuComponent = (MenuComponent) iterator.next();

                     menuComponent.print();

              }

       }

}

 

 

 

다음은 MenuItem코드이다.

 

package headfirst.composite.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.print("  " + getName());

              if (isVegetarian()) {

                     System.out.print("(v)");

              }

              System.out.println(", " + getPrice());

              System.out.println("     -- " + getDescription());

       }

}

 

 

 

다음은 웨이트리스 코드이다.

 

package headfirst.composite.menu;

 

public class Waitress {

       MenuComponent allMenus;

 

       public Waitress(MenuComponent allMenus) {

              this.allMenus = allMenus;

       }

 

       public void printMenu() {

              allMenus.print();

       }

}

 

 

 

다음은 테스트 코드이다.

 

package headfirst.composite.menu;

 

 

public class MenuTestDrive {

       public static void main(String args[]) {

              MenuComponent pancakeHouseMenu = new Menu("PANCAKE HOUSE MENU", "Breakfast");

              MenuComponent dinerMenu = new Menu("DINER MENU", "Lunch");

              MenuComponent cafeMenu = new Menu("CAFE MENU", "Dinner");

              MenuComponent dessertMenu = new Menu("DESSERT MENU", "Dessert of course!");

              MenuComponent coffeeMenu = new Menu("COFFEE MENU", "Stuff to go with your afternoon coffee");

 

              MenuComponent allMenus = new Menu("ALL MENUS", "All menus combined");

 

              allMenus.add(pancakeHouseMenu);

              allMenus.add(dinerMenu);

              allMenus.add(cafeMenu);

 

              pancakeHouseMenu.add(new MenuItem("K&B's Pancake ", "Pancakes with scrambled eggs, and toast", true, 2.99));

              pancakeHouseMenu.add(new MenuItem("Regular Pancake ", "Pancakes with fried eggs, sausage", false, 2.99));

              pancakeHouseMenu.add(new MenuItem("Blueberry Pancakes", "Pancakes made with fresh syrup", true, 3.49));

              pancakeHouseMenu.add(new MenuItem("Waffles", "Waffles, with your choice of blueberries", true, 3.59));

 

              dinerMenu.add(new MenuItem("Vegetarian BLT", "(Fakin') Bacon with lettuce & tomato", true, 2.99));

              dinerMenu.add(new MenuItem("BLT", "Bacon with lettuce & tomato on whole wheat", false, 2.99));

              dinerMenu.add(new MenuItem("Soup of the day", "A bowl of the soup of the day, with salad", false, 3.29));

              dinerMenu.add(new MenuItem("Hotdog", "A hot dog, with saurkraut, relish, onions and cheese", false, 3.05));

              dinerMenu.add(new MenuItem("Steamed Veggies and Brown Rice", "Steamed vegetables rice", true, 3.99));

 

              dinerMenu.add(new MenuItem("Pasta", "Spaghetti with Marinara Sauce", true, 3.89));

              dinerMenu.add(dessertMenu); 

              dessertMenu.add(new MenuItem("Apple Pie", "Apple pie with a flakey crust, topped icecream", true, 1.59));

              dessertMenu.add(new MenuItem("Cheesecake", "Creamy New York cheesecake, with a graham crust", true, 1.99));

              dessertMenu.add(new MenuItem("Sorbet", "A scoop of raspberry and a scoop of lime", true, 1.89));

 

              cafeMenu.add(new MenuItem("Veggie Burger and Air Fries", "Veggie burger on tomato, and fries", true, 3.99));

              cafeMenu.add(new MenuItem("Soup of the day", "A cup of the soup of the day, with a side salad", false, 3.69));

              cafeMenu.add(new MenuItem("Burrito", "A large burrito, with whole pinto beansguacamole", true, 4.29));

              cafeMenu.add(coffeeMenu); 

              coffeeMenu.add(new MenuItem("Coffee Cake", "Crumbly cake topped with cinnamon and walnuts", true, 1.59));

              coffeeMenu.add(new MenuItem("Bagel", "Flavors include sesame, poppyseed and pumpkin", false, 0.69));

              coffeeMenu.add(new MenuItem("Biscotti", "Three almond or hazelnut biscotti cookies", true, 0.89));

 

              Waitress waitress = new Waitress(allMenus);

 

              waitress.printMenu();

       }

}

 

 

Iterator 패턴코드

AlternatingDinerMenuIterator.java

CafeMenu.java

DinerMenu.java

DinerMenuIterator.java

Menu.java

MenuItem.java

MenuTestDrive.java

PancakeHouseMenu.java

Waitress.java

 

Composite 패턴코드

Menu.java

MenuComponent.java

MenuItem.java

MenuTestDrive.java

Waitress.java

 

 

 

 

'Java Design Pattern' 카테고리의 다른 글

디자인패턴의 분류  (0) 2013.02.22
State 패턴  (0) 2013.02.22
Template Method 패턴  (0) 2013.02.20
Factory 패턴  (0) 2013.02.20
Decorator 패턴  (0) 2013.02.20
Posted by Steven J.S Min
,