반복자(Iterator) 패턴
반복자 패턴은 무엇인가 많이 모여 있을 때 이를 순서대로 가리키며 전체를 검색하고 처리를 반복하는 패턴이다.
반복자 패턴은 컬렉션의 구현 방법을 노출하지 않으면서 집합체 내의 모든 항목에 접근하는 방법을 제공한다.
예시
아래와 같은 구조의 Book
클래스가 있다.
public class Book {
private String name;
public Book(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
여러 Book
객체를 배열 형식으로 담은 ArrayBookShelf
가 있다.
public class ArrayBookShelf {
private static final int MAX_ITEMS = 5;
private int numberOfItems = 0;
private Book[] books;
public ArrayBookShelf() {
books = new Book[MAX_ITEMS];
books[numberOfItems++] = new Book("알고리즘");
books[numberOfItems++] = new Book("스프링");
books[numberOfItems++] = new Book("스프링 배치");
books[numberOfItems++] = new Book("JSP");
books[numberOfItems++] = new Book("유닉스");
}
}
그리고 여러 Book
객체들을 List
형식으로 담는 ListBookShelf
클래스가 있다.
public class ListBookShelf {
private List<Book> books;
public ListBookShelf() {
books = new ArrayList<>();
books.add(new Book("디자인 패턴"));
books.add(new Book("자바"));
books.add(new Book("파이썬"));
}
}
두 가지의 BookShelf
에 존재하는 모든 Book
을 출력하고 싶을 때, 두 BookShelf
는 컬렉션의 구현 방법이 다르기 때문에, 아래와 같이 각각의 컬렉션에 맞는 반복문을 통해 출력해야 한다.
public static void main(String[] args) {
ArrayBookShelf arrayBookShelf = new ArrayBookShelf();
ListBookShelf listBookShelf = new ListBookShelf();
Book[] arrayBookShelfBooks = arrayBookShelf.getBooks();
for (int i = 0; i < arrayBookShelfBooks.length; i++) {
System.out.println(arrayBookShelfBooks[i]);
}
List<Book> listBookShelfBooks = listBookShelf.getBooks();
for (int i = 0; i < listBookShelfBooks.size(); i++) {
System.out.println(listBookShelfBooks.get(i));
}
}
이러한 상황에서 반복 처리 방법을 캡슐화한다면 어떨까?
이 때 사용할 수 있는 디자인 패턴이 반복자(Iterator) 패턴이다.
반복자 패턴을 구현하기 위해 java.util
패키지에서 제공하는 Iterator
인터페이스를 이용하면 간단히 반복자 패턴을 구현할 수 있다.
Iterator
인터페이스는 컬렉션을 대상으로 반복 작업을 하는데 필요한 메서드들을 제공한다.
Iterator
인터페이스는 컬렉션 반복 처리 방법을 캡슐화 한 것으로 볼 수 있다.
public class ArrayBookShelfIterator implements Iterator<Book> {
private ArrayBookShelf bookShelf;
private int position = 0;
public ArrayBookShelfIterator(ArrayBookShelf bookShelf) {
this.bookShelf = bookShelf;
}
@Override
public boolean hasNext() {
return bookShelf.getBooks().length != position;
}
@Override
public Book next() {
return bookShelf.getBooks()[position++];
}
}
public class ListBookShelfIterator implements Iterator<Book> {
private ListBookShelf bookShelf;
private int position = 0;
public ListBookShelfIterator(ListBookShelf bookShelf) {
this.bookShelf = bookShelf;
}
@Override
public boolean hasNext() {
return bookShelf.getBooks().size() != position;
}
@Override
public Book next() {
return bookShelf.getBooks().get(position++);
}
}
위와 같이 컬렉션의 반복 처리를 캡슐화 할 수 있다.
기존 컬렉션을 갖는 객체에서 Iterator
객체를 만들 수 있도록 해야한다.
컬렉션을 갖는 객체는 XXXBookShelf
객체들이다.
따라서 해당 클래스를 통해 Iterator
를 생성할 수 있도록 메서드를 추가하고, Iterator
인터페이스를 구현한 구현체 클래스를 작성하여 기능을 추가하면 된다.
java.lang
패키지는 Iterable
인터페이스를 제공한다.
Iterable
인터페이스는 iterator()
메서드를 제공하는데, 이는 Iterator
를 반환한다.
public class ArrayBookShelf implements Iterable<Book> {
private static final int MAX_ITEMS = 5;
private int numberOfItems = 0;
private Book[] books;
public ArrayBookShelf() {
books = new Book[MAX_ITEMS];
books[numberOfItems++] = new Book("알고리즘");
books[numberOfItems++] = new Book("스프링");
books[numberOfItems++] = new Book("스프링 배치");
books[numberOfItems++] = new Book("JSP");
books[numberOfItems++] = new Book("유닉스");
}
public int getNumberOfItems() {
return numberOfItems;
}
public Book[] getBooks() {
return books;
}
@Override
public Iterator<Book> iterator() {
return new ArrayBookShelfIterator(this);
}
}
public class ListBookShelf implements Iterable<Book> {
private List<Book> books;
public ListBookShelf() {
books = new ArrayList<>();
books.add(new Book("디자인 패턴"));
books.add(new Book("자바"));
books.add(new Book("파이썬"));
}
public List<Book> getBooks() {
return books;
}
@Override
public Iterator<Book> iterator() {
return new ListBookShelfIterator(this);
}
}
아래는 Iterator
로 반복을 캡슐화 한 후의 출력 코드이다.
public static void main(String[] args) {
ArrayBookShelf arrayBookShelf = new ArrayBookShelf();
ListBookShelf listBookShelf = new ListBookShelf();
printBooks(arrayBookShelf.iterator());
printBooks(listBookShelf.iterator());
}
private static void printBooks(Iterator iterator) {
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
반복을 캡슐화하여 컬렉션이 어떻게 구현되었는지와 상관없이 공통적으로 처리할 수 있게 되었다.