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 |