들어가며
이 포스팅의 내용은 <<Java 언어로 배우는 디자인 패턴>> 1장 Iterator 패턴을 정리한 것입니다. 여기 나오는 코드는 JDK 1.8에서 작성, 테스트 되었습니다. 이 포스팅에 있는 코드는 여기 서 확인하실 수 있습니다. 코드 또한 책에서 예제로 나온 코드를 조금 변경한 것입니다.
Iterator 패턴
Java 언어에서 배열의 모든 요소를 표시하기 위해서는 다음과 같이 for 문을 사용합니다.
for (int i =0; i<arr.length; i++) {
System.out.println(arr[i]);
}for 문에서 i를 하나씩 증가시키면,현재 보고 있는 배열의 요소를 차례대로 검색하게 됩니다. 여기서 사용하는 변수 i를 추상화 한 것을 디자인 패턴에서는 이터레이터 패턴이라고 합니다.
예제 프로그램
여기서 확인할 예제 프로그램은 서가(BookShelf) 안에 책(Book)을 넣고, 그 책의 이름을 차례대로 표시하는 프로그램입니다.
Aggregate 인터페이스
Aggregate 인터페이스는 요소들이 나열되어 있는 집합체를 나타냅니다. 이 인터페이스를 구현한 클래스는, 배열과 같이 무엇인가가 많이 모여 있습니다. Aggregate 인터페이스는 다음과 같이 구성됩니다.
public interface Aggregate {
Iterator iterator();
}전체적인 다이어그램은 다음과 같습니다.
- Aggregate : 이터레이터를 만들어내는 인터페이스를 결정합니다. 이 인터페이스를 구현하면, 구현체에서 이터레이터를 생성할 수 있습니다.
- 위의 다이어그램에서는 BookShelf, BookShelfList 가 구현체입니다.
- 각각 ArrayList, LinkedList를 이용하여 BookShelf를 구현했습니다.
- BookShelfInterface : BookShelf를 만들기 위해 필요한 메서드의 인터페이스입니다. Aggregate를 implement하여, 책장에 필요한 인터페이스를 추가합니다.
- Iterator : 요소를 순서대로 검색해가는 인터페이스를 결정합니다. hasNext와 next 메소드를 결정합니다.
- hasNext : 다음 요소가 있는지 확인하는 인터페이스입니다.
- next : 다음 요소를 얻어오는 인터페이스입니다.
- BookShelfIterator : Iterator에서 결정한 인터페이스를 실제로 구현한 구현체입니다.
특징
배열이라면 for 문을 돌리면 될 것을, 왜 이터레이터 패턴을 사용하는 것일까요? 사용하는 이유와, 이터레이터 패턴에서 조심해야 될 특징들은 다음과 같습니다.
구현에 상관 없이 Iterator를 사용할 수 있다
실제로 BookShelf에서 어떻게 내부에 데이터를 저장하는지 관계 없이, Iterator를 사용하여 데이터를 순회할 수 있습니다.
while (iterator.hasNext()) {
Book book = (Book)iterator.next();
result.add(book.getName());
}실제로 순회하는 코드는 위와 같은데, 위의 코드에서는 Iterator의 내부 구현에 의존하는 메서드가 없기 때문입니다. 그래서 순회하는 코드에 영향을 주지 않고, 내부 자료구조를 변경할 수 있습니다. 위의 다이어그램에서도, ArrayList와 LinkedList로 데이터를 저장하는 BookShelf, BookShelfList를 따로 만들었습니다만, while루프를 변경하지 않고도 테스트 하는데 문제가 없었습니다.
Aggregate와 Iterator의 대응
BookShelfInterface와 Iterator는 쌍을 이루고 있기 때문에, 그것들의 구현체인 BookShelf와 BookShelfIterator 또한 서로 쌍을 이루고 있습니다. 만약, BookShelfInterface의 getBookAt을 변경한다면, BookShelfIterator또한 변경해야 합니다.
next와 hasNext
- next : 현재의 요소를 반환하면서, 다음 위치로 진행
- hasNext : 다음에 next 메서드를 호출해도 괜찮은지 체크하는 메서드
복수의 Iterater
앞에서 복수의 BookShelf 구현체를 생성했던 것처럼, 다수의 Iterator 객체를 만들 수도 있습니다. 이는 BookShelf 구현체 내부에서 어떤 Iterator를 사용할지 결정할 수 있기 때문입니다. Iterator 또한 다양한 종류가 있을 수가 있습니다. 대표적인 예는 다음과 같습니다.
- 뒤에서 시작해서 역방향으로 진행한다.
- 정방향으로도, 역방향으로도 진행한다(next 메서드 뿐만 아니라, previous 메서드가 있을 수도 있습니다.)
- 번호를 지정해서 갑자기 그곳으로 점프한다.
이처럼 다수의 Iterator를 구현한 후, BookShelf 에서 new를 할때 이 객체를 사용할 수도 있습니다.
관련 패턴
Visitor 패턴
이터레이터 패턴은 집합체에서 하나씩 요소를 꺼내서 세는 것입니다. 그러나, 인터페이스 내에 그 요소들을 어떻게 처리할지 서술하고 있지는 않습니다. 하나씩 세면서 동시에 처리를 하는 일은 자주 발생하는 일입니다. Visitor 패턴은 바로 이것을 위한 디자인 패턴입니다.
Composite 패턴
Composite 패턴은 재귀적인 구조를 갖는 패턴입니다.
Factory Method 패턴
iterator 메서드가 Iterator 인스턴스를 만들 때, Factory Method 패턴이 사용되는 경우가 있습니다.