The iterator pattern

2023. 10. 22. 02:54디자인패턴

728x90

문제배경

두 개의 서로다른 식당에서 메뉴를 구현한다고 가정.하나는 배열로 구현하고 다른 하나는 ArrayList로 구현한다.클라이언트가 두 메뉴를 사용하려고 할 때,각 메뉴에 들어있는 모든 항목을 출력하려면

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

위처럼 작성해야 한다. 각 메뉴에 대해 2개의 반복문을 써야한다.이후에 다른 컬렉션이 추가되면 더 많아질 것이다.반복을 캡슐화한다면 반복작업을 줄일 수 있지 않을까?
 
 

자바에서 제공하는 Iterator 적용하기

public interface Iterator{
	boolean hasNext();
	Object next();
}
public Iterator createIterator() { //menuItems에 대한 iterator 생성하는 메소드
	return menuItems.iterator();
}
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++];
        return MenuItem;
    }
    public boolean hasNext() {
    	if(position>=item.length || items[position]==null)
        	return false;
        else
        	return true;
    }
    public void remove() {
        if (position <= 0) {
        	throw new IllegalStateException
        		(“You can’t remove an item until you’ve done at least one 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 interface Menu { //클라이언트(Waitress)가 사용할 수 있는 반복자 인터페이스
	public Iterator createIterator();
}
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(“MENU\n----\nBREAKFAST”);
        printMenu(pancakeIterator);
        System.out.println(“\nLUNCH”);
        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());
        }
    }
    // other methods here
}

 

위처럼 내부 메뉴와 관련된 기능과 반복자용 메소드 기능을 분리한 이유가 뭘까?
통합해서 구현했다면
1. 다른 컬렉션을 사용하게 되면 그 클래스가 바뀌어야 하고
2. 반복자용 메소드가 바뀌었을때도 그 클래스를 바꿔야 한다.
 
 
여기서 디자인 원칙 하나가 나온다.
클래스를 바꾸는 이유는 오직 단 하나뿐이어야한다.
이 원칙에 따라 한 클래스에서는 한 역할만 맡아야 한다.
 
 

Iterator Pattern

: 컬렉션 구현 방법을 노출시키지 않으면서 그 집합체 안에 들어있는 모든 항목에
접근할 수 있는 방법을 제공한다.
이 패턴을 사용하면 모든 항목에 일일히 접근하는 작업을

컬렉션 객체가 아닌 반복자 객체에서 맡게 된다.
-> 컬렉션에 상관없이 접근방식을 통일시킬 수 있다.

-> Client 입장에서 어떤 컬렉션을 사용하고 있는지 알 필요가 없다.

 

728x90

'디자인패턴' 카테고리의 다른 글

State pattern  (0) 2023.10.31
composite pattern  (0) 2023.10.30
The Template Method Pattern  (0) 2023.10.22
The Adapter and Facade Patterns  (0) 2023.10.21
The Command Pattern  (1) 2023.10.21