Tech & Programming/Pattern & Design

이터레이터 패턴 (Iterator Pattern) 이란?

소스코드 요리사 2019. 1. 10. 08:54

2019년은 리펙토링 및 소스코드의 품질, 설계 공부에 초점을 맞춰서 책을 읽고, 공부를 하고 있습니다.

그 중 Head First Design Patterns 책을 보고 패턴에 대해 공부한 내용들을 조금씩 정리해서 시리즈로 꾸준히 올려보도록 하겠습니다.



금일은 이터레이터(iterator) 패턴입니다.


이터레이터(iterator) 패턴은 컬렉션 구현방법을 노출 시키지 않으면서도 그 집합체 안에 들어 있는 모든 항목에 접근할 수 있게 해주는 방법을 제공해 줍니다.


이터레이터(iterator) 패턴을 사용하면 객체안의 항목 또는 컬렉션 객체 안에 들어있는 모든 항목에 접근하는 방식을 통일할 수 있습니다. 이렇게 접근 방식을 통일하게 되면 어떤 종류의 객체에 대해서도 사용할 수 있는 다형적인 코드를 만들 수 있습니다.





BreakfastMenu는 MenuItem의 관리를 ArrayList<MenuItem>로 하고 있고, AStoreLaunchMenu의 MenuItem의 관리를 배열로 관리를 하고 있습니다. 이 때 Iterator 패턴을 이용하지 않는다면, Waitress 객체에서 아침, 점심 메뉴를 모두 출력학기 위해서는 아래와 같이 printMenu 메소드를 작성해야합니다.


public void printMenu() {
	System.out.println("== 아침 메뉴 ==");
	printMenu(breakfastMenu.getMenuItems());
	System.out.println("== 점심 메뉴 ==");
	printMenu(launchMenu..getMenuItems());
}

public void printMenu(arrayList breakfastmenuItems) {
	
	for(int i = 0 ; i < breakfastmenuItems.size() ; i++ ) {
		MenuItem menuItem = breakfastmenuItems.get(i);
		System.out.println("== " + menuItem.getMenu() + " : " + menuItem.getPrice());
        System.out.println("  " + menuItem.getDescription());
	}
	
}

public void printMenu(MenuItem[] aStoreLaunchMenuItems ) {
	for(int i = 0 ; (i < aStoreLaunchMenuItems.length) && (aStoreLaunchMenuItems[i] != null) ; i++ ) {
		MenuItem menuItem = breakfastmenuItems[i];
		System.out.println("== " + menuItem.getMenu() + " : " + menuItem.getPrice());
        System.out.println("  " + menuItem.getDescription());
	}
}

즉, 리턴되는 메뉴 아이템들의 컬렉션의 유형이 다르기 때문에 중복코드가 발생하는 것을 볼 수 있습니다.

게다가 저녁메뉴가 추가 될 경우에는 동일한 순환문이 또 추가되어야 함을 알 수가 있습니다.


이러한 문제를 해결하기 위해서 이터레이터(iterator) 패턴을 사용합니다.

iterator 인터페이스를 보면 hasNext와 Next 메소드가 있습니다.

hasNext는 다음 반환할 객체가 있는지 판단하는 메소드이고, Next는 객체를 반환하는 메소드입니다.


이렇게 iterator인터페이스를 구체화하여, AStoreLaunchMenuIterator와 BraekfastMenuIterator 를 이용하면

아래와 같이 Waitress의 PrintMenu가 간단해집니다.

public void printMenu() {
    System.out.println("== 아침 메뉴 ==");
    printMenu(breakfastMenu.getIterator());
    System.out.println("== 점심 메뉴 ==");
    printMenu(launchMenu.getIterator());
}

public void printMenu(Iterator iterator) {
    while (iterator.hasNext()) {
        MenuItem menuItem = (MenuItem) iterator.next();
        System.out.println("== " + menuItem.getMenu() + " : " + menuItem.getPrice());
        System.out.println("  " + menuItem.getDescription());
    }
}

Waitress 객체에서는 인터페이스 객체를 반환 받아 출력만 할 뿐 실제 순환문이나 컬렉션의 아이템을 반환하기 위해 경계검사 따위는 하지 않아도 됩니다. 이러한 것은 각 iterator인터페이스를 구체화한 iterator에서 그 역할을 맡게 되고, Waitress 객체는 더 역할이 명확해지게 됩니다. Waitress 에서는 iterator 인터페이스만 알고 있으면 됩니다.





전체 예제 코드 : https://github.com/JunpilPark/DesignPatternStudy/tree/master/IteratorExmple

관련 패턴의 동영상 강의 :