9. 이터레이터와 컴포지트 패턴

  1. 9. 이터레이터와 컴포지트 패턴
    1. Iterator 패턴

Iterator 패턴

  • 컬렉션의 구현을 드러내지 않으면서도 컬렉션에 있는 모든 객체들에 대해 반복작업을 할 수 있다.

h3.MenuItem.java

{code}
public class MenuItem{
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 boolean isVegetarian(){
return vegetarian;
}

public double getPrice(){
return price;
}
}

|

h3.PancakeHouseMenu.java
||①||②||③||
|

public class PancakeHouseMenu {
ArrayList menuItems;

public PancakeHouseMenu(){
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);
menuItems.add(menuItem);
}

public ArrayList getMenuItems(){
return menuItems;
}
}

|

public class PancakeHouseMenu {
ArrayList menuItems;

public PancakeHouseMenu(){
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);
menuItems.add(menuItem);
}

public ArrayList getMenuItems(){
return menuItems;
}
public Iterator createIterator() {
return new PancakeHouseMenuIterator(menuItems);
}
}

|

public class PancakeHouseMenu {
ArrayList menuItems;

public PancakeHouseMenu(){
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);
menuItems.add(menuItem);
}

public ArrayList getMenuItems(){
return menuItems;
}
public Iterator createIterator() {
return menuItems.iterator();
}
}

|

h3.DinerMenu.java
||①||②||
|

public class DinerMenu {
static final int MAM_ITEMS = 6;
int numberOfItems = 0;
MenuItem[] menuItems;

public DinerMenu(){
menuItems = new MenuItem[MAM_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(numberOfItems >= MAM_ITEMS){
System.err.println("죄송합니다. 메뉴가 꽉 찼습니다. 더 이상 추가할 수 없습니다");
}else{
menuItems[numberOfItems] = menuItem;
numberOfItems = numberOfItems + 1;
}
}

public MenuItem[] getMenuItems(){
return menuItems;
}
}

|

public class DinerMenu {
static final int MAM_ITEMS = 6;
int numberOfItems = 0;
MenuItem[] menuItems;

public DinerMenu(){
menuItems = new MenuItem[MAM_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(numberOfItems >= MAM_ITEMS){
System.err.println("죄송합니다. 메뉴가 꽉 찼습니다. 더 이상 추가할 수 없습니다");
}else{
menuItems[numberOfItems] = menuItem;
numberOfItems = numberOfItems + 1;
}
}

public Iterator createIterator(){
return new DinerMenuIterator(menuItems);
}
}

|

h3.Waitress.java
||①||②||③|
|

import java.util.ArrayList;

public class Waitress {
PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
DinerMenu dinerMenu = new DinerMenu();

public Waitress(PancakeHouseMenu pancakeHouseMenu, DinerMenu dinerMenu) {
this.pancakeHouseMenu = pancakeHouseMenu;
this.dinerMenu = dinerMenu;
}

public void printMenu() {

ArrayList breakfastItems = pancakeHouseMenu.getMenuItems();

for (int i = 0; i < breakfastItems.size(); i++) {
MenuItem menuItem = (MenuItem)breakfastItems.get(i);
System.out.print(menuItem.getName());
System.out.println("\t\t" + menuItem.getPrice());
System.out.println("\t" + menuItem.getDescription());
}

MenuItem[] lunchItems = dinerMenu.getMenuItems();

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

|

public class Waitress {
PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu();
DinerMenu dinerMenu = new 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("메뉴\n


\n아침메뉴");
printMenu(pancakeIterator);

System.out.println("메뉴\n


--\n저녁메뉴);
printMenu(dinerIterator);
}

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

|

import java.util.Iterator;
public class Waitress {
Menu pancakeHouseMenu;
Menu dinerMenu;

public Waitress (Menu pancakeHouseMenu, Menu dinerMenu){
this.pancakeHouseMenu = pancakeHouseMenu;
this.dinerMenu = dinerMenu;
}

public void printMenu(){
Iterator pancakeIterator = pancakeHouseMenu.createIterator();
Iterator dinerIterator = dinerMenu.createIterator();

System.out.println("메뉴\n


--\n아침메뉴");
printMenu(pancakeIterator);

System.out.println("메뉴\n


--\n저녁메뉴");
printMenu(dinerIterator);
}

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

}
}
}

|


h3.PancakeHouseMenuIterator.java

||②||
|

mport java.util.ArrayList;

public class PancakeHouseMenuIterator implements Iterator{
ArrayList menuItems;
int position = 0;

public PancakeHouseMenuIterator(ArrayList menuItems){
this.menuItems = menuItems;
}

public Object next(){
Object menuItem = menuItems.get(position);
position =position +1;
return menuItem;
}

public boolean hasNext(){
if(position >= menuItems.size() || menuItems.get(position) == null){
return false;
}else{
return true;
}
}
}

|

h3.DinerMenuIterator.java

||② ||③||
|

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

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

public Object next(){
MenuItem menuItem = items[position];
position =position +1;
return menuItem;
}

public boolean hasNext(){
if(position >= items.length || items[position] == null){
return false;
}else{
return true;
}
}
}

|

import java.util.Iterator;

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

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

public Object next(){
MenuItem menuItem = items[position];
position =position +1;
return menuItem;
}

public boolean hasNext(){
if(position >= items.length || items[position] == null){
return false;
}else{
return true;
}
}

public void remove() {
if (position <= 0) {
throw new IllegalStateException
("next()를 한번도 호출하지 않은 상태에서는 삭제할 수 없습니다.");
}
if (items[position-1] != null) {
for (int i = position-1; i < (items.length-1); i++) {
items[i] = items[i+1];
}
items[items.length-1] = null;
}
}
}

|



|*변경전 결과*
!init1.JPG!|*변경후 결과*
!init2.JPG!|

!2.JPG!
* <<② 소스 UML>>


h4.인터페이스를 개선해 봅시다.
* PancakeHouseMenu 와 DinerMenu의 인터페이스가 완전히 똑같으므로 통일 시킨다.
* ArryList에서는 별도의 Iterator를 구현할 필요가 없다. iterator() 메소드가 있기 때문이다.
* 배열에서는 iterator() 메소드가 없기에 배열 반복자를 만들어야 한다.

!1.JPG!
* << ③ 소스 UML>>


h2. Composite 패턴
* 클라이언트에서 객체 컬렉션과 개별 객체를 똑같은 식으로 처리할 수 있다.
* p394 참고


!4.JPG!



||MenuComponent.java||Waitress.java||
|

import java.util.*;

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 abstract Iterator createIterator();

public void print() {
throw new UnsupportedOperationException();
}
}

|

public class Waitress {
MenuComponent allMenus;

public Waitress(MenuComponent allMenus) {
this.allMenus = allMenus;
}

public void printMenu() {
allMenus.print();
}
}

|

||MenuItem.java||Menu.java||
|

import java.util.Iterator;

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 Iterator createIterator() {
return new NullIterator();
}

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

|

import java.util.Iterator;
import java.util.ArrayList;

public class Menu extends MenuComponent {
ArrayList 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 (MenuComponent)menuComponents.get(i);
}

public String getName() {
return name;
}

public String getDescription() {
return description;
}

//추가 부분
public Iterator createIterator() {
return new CompositeIterator(menuComponents.iterator());
}

public void print() {
System.out.print("\n" + getName());
System.out.println(", " + getDescription());
System.out.println("






-");

//이터레이터 패턴, 메뉴의 모든 구성요소 반복작업처리
Iterator iterator = menuComponents.iterator();
while (iterator.hasNext()) {
MenuComponent menuComponent =
(MenuComponent)iterator.next();
menuComponent.print();
}
}
}

|




h3. 이터레이터

!3.JPG!

|

public class Menu extends MenuComponent {

//나머지 코드는 그대로..

public Iterator createIterator() {
return new CompositeIterator(menuComponents.iterator());
}
}

public class MenuItem extends MenuComponent {

//나머지 코드는 그대로..

public Iterator createIterator() {
return new NullIterator();
}
}

|

h3. 복합 반복자

* MenuItem에 대해 반복작업을 할 수 있게 해주는 기능을 제공한다.


||CompositeIterator.java||
|

import java.util.*;

public class CompositeIterator implements Iterator {
Stack stack = new Stack();

public CompositeIterator(Iterator iterator) {
stack.push(iterator);
}

public Object next() {
if (hasNext()) {
Iterator iterator = (Iterator) stack.peek();
MenuComponent component = (MenuComponent) iterator.next();
if (component instanceof Menu) {
stack.push(component.createIterator());
}
return component;
} else {
return null;
}
}

public boolean hasNext() {
if (stack.empty()) {
return false;
} else {
Iterator iterator = (Iterator) stack.peek();
if (!iterator.hasNext()) {
stack.pop();
return hasNext();
} else {
return true;
}
}
}

public void remove() {
throw new UnsupportedOperationException();
}
}

|

h3. 널 반복자

||NullIterator.java||
|

import java.util.Iterator;

public class NullIterator implements Iterator {

public Object next() {
return null;
}

public boolean hasNext() {
return false;
}

public void remove() {
throw new UnsupportedOperationException();
}
}

|

h3. 채식주의자용 메뉴


||Waitress.java||
|

import java.util.Iterator;

public class Waitress {
MenuComponent allMenus;

public Waitress(MenuComponent allMenus) {
this.allMenus = allMenus;
}

public void printMenu() {
allMenus.print();
}

//추가부분
public void printVegetarianMenu() {
Iterator iterator = allMenus.createIterator();

System.out.println("\nVEGETARIAN MENU\n


");
while (iterator.hasNext()) {
MenuComponent menuComponent = (MenuComponent)iterator.next();
try {
if (menuComponent.isVegetarian()) {
menuComponent.print();
}
} catch (UnsupportedOperationException e) {}
}
}
}

|


h2. 문서에 대하여
* 최초작성자 : [임영미]
* 최초작성일 : 2008년 04월 11일
* 이 문서는 [HeadFirst Design Patterns|http://book.naver.com/bookdb/book_detail.php?bid=1882446]을 정리한 내용 입니다.
* 이 문서는 [오라클클럽|http://www.gurubee.net] [자바 웹개발자 스터디|제3차 자바 웹개발자 스터디] 모임에서 작성하였습니다.
* 이 문서를 다른 블로그나 홈페이지에 퍼가실 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.~^\^