컬렉션을 사용하는 요소들에 대해서 순차적으로 접근하는 인터페이스를 제공하는 패턴으로 내부 자료구조를 노출하지 않은 상태로 접근기회를 제공해주는 패턴이다.
1. 여러개의 Book을 저장하는 Bookshelf 클래스가 있다.
class Bookshelf {
private ArrayList<String> books;
Bookshelf() {
books = new ArrayList<>();
books.add("Book 1");
books.add("Book 2");
books.add("Book 3");
}
public ArrayList<String> getBooks() {
return books;
}
}
2. Bookself의 book에 접근하기 위해 Client는 다음과 같은 코드로 작성할 수 있다.
public class Client {
public static void main(String[] args) {
Bookshelf bookshelf = new Bookshelf();
ArrayList<String> books = bookshelf.getBooks();
for (int i = 0; i < books.size(); i++) {
System.out.println(books.get(i));
}
}
}
위 상황에서 만약 Bookself의 자료구조가 ArrayList가 아닌 다른 자료구조로 바뀐다면, Client의 books.get() 메소드를 무조건 제공한다고 할 수 없다. 즉, Client의 코드를 바꾸어야 한다.
books를 순회하는 인터페이스를 제공하도록 Iterator 인터페이스를 통해 접근하도록 하자
1. Iterator
interface Iterator {
boolean hasNext();
Object next();
}
2. Bookshelf
class Bookshelf {
private ArrayList<String> books;
Bookshelf() {
books = new ArrayList<>();
books.add("Book 1");
books.add("Book 2");
books.add("Book 3");
}
public Iterator getIterator() {
return new BookIterator(this.books);
}
}
ConcreteCollection에 해당하는 Bookshelf로 이전의 getBooks대신 getIterator로 순회하기 위한 Iterator를 생성해 반환한다.
3. BookIterator
class BookIterator implements Iterator {
private ArrayList<String> books;
private int index;
public BookIterator(ArrayList<String> books) {
this.books = books;
}
@Override
public boolean hasNext() {
if(index < books.size()){
return true;
}
return false;
}
@Override
public Object next() {
if(this.hasNext()){
return books.get(index++);
}
return null;
}
}
4. Client
public class Client {
public static void main(String[] args) {
Bookshelf bookshelf = new Bookshelf();
Iterator iter = bookshelf.getIterator();
while (iter.hasNext()) {
String book = (String)iter.next();
System.out.println(book);
}
}
}
반복자 패턴이 적용되면 Bookself의 내부 자료구조가 ArrayList에서 다른 자료구조로 바뀌어도 실제 순회로직을 담당하는 BookIterator만 바꾸어주면 된다. 즉, Client에는 영향을 미치지 않는다.