[디자인 패턴] Iterator 패턴 (반복자 패턴)
Iterator 패턴은 무언가 많이 모여있는 것을 하나씩 지정해서 순서대로 처리하는 패턴이다.
iterator는 무언가를 반복한다
는 의미를 가지고 있다.
보통 프로그래밍을 하면서 반복이 필요할 때는 for 문을 사용한다.
for (int i = 0; i < n; i++) {
System.out.println(array[i]);
}
for문의 초기화문에서 흔히 사용되는 변수 i
는 배열이 주어질 때 각각의 요소에 차례대로 접근하기 위해 사용된다.
이렇게 사용되는 변수 i의 역할을 추상화 한 것이 Iterator 패턴이다.
BookShelf 예제
책꽂이 안에 책을 꽂고, 다시 책을 하나씩 확인하는 예제를 작성한다.
1) Book : 한권의 책에 대한 정보를 가지고 있는 클래스
public class Book {
private String name;
public Book(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
2) Aggregate : 집합체를 의미하는 인터페이스
Aggregate는 Iterator 역할을 만들어내는 인터페이스를 결정한다.
public interface Aggregate {
public abstract Iterator createIterator();
}
3) BookShelf : 책을 보관하는 책꽂이 역할을 하는 클래스
BookShelf는 이러한 Aggregate의 구현체이다.
실제로 책꽂이 안을 돌아다닐 Iterator를 생성하고 책꽂이를 관리하는 역할을 한다.
public class BookShelf implements Aggregate {
private Book[] books; // 책의 집합
private int last = 0; // 마지막 책이 꽂힌 위치
public BookShelf(int size) {
books = new Book[size];
}
public Book getBook(int index) {
return books[index];
}
public int getLength() {
return last;
}
// 책꽂이에 책을 꽂는다
public void appendBook(Book book) {
if (last < books.length) {
this.books[last] = book;
last++;
} else {
System.out.println("책꽂이가 꽉 찼습니다!");
}
}
@Override
public Iterator createIterator() {
return new BookShelfIterator(this);
}
}
4) BookShelfIterator : BookShelf 클래스에서 검색을 수행하는 클래스
BookShelfIterator를 Iterator로 다루기 위해 Iterator 인터페이스를 상속받았다.
Iterator 인터페이스는 Java에 이미 내장되어 있기 때문에 따로 구현하지 않았다.
public class BookShelfIterator implements Iterator<Book> {
private BookShelf bookShelf; // 검색을 수행할 책꽂이
private int index = 0; // 현재 처리할 책의 위치
public BookShelfIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
}
@Override
public boolean hasNext() {
return index < bookShelf.getLength();
}
@Override
public Book next() {
Book book = bookShelf.getBook(index);
index++;
return book;
}
}
next()는 다음번 요소를 반환하고, hasNext()는 검색을 계속 수행해도 될지의 여부를 판별한다.
Main 클래스에서 책꽂이에 책을 꽂고 책을 하나씩 검색해 이름을 출력한다.
public class Main {
public static void main(String[] args) {
BookShelf bookShelf = new BookShelf(10);
Book book1 = new Book("Bilbe");
Book book2 = new Book("Cinderella");
Book book3 = new Book("Daddy-Long-Legs");
bookShelf.appendBook(book1);
bookShelf.appendBook(book2);
bookShelf.appendBook(book3);
System.out.println("현재 꽂혀있는 책 : " + bookShelf.getLength() + "권");
Iterator it = bookShelf.createIterator();
while (it.hasNext()) {
Book book = (Book) it.next();
System.out.println(book.getName());
}
}
}
BookShelfIterator는 책꽂이에서 책을 한권씩 뽑아오는 역할을 한다.
검색할 책이 존재하는 동안 while문이 수행되며, it.next()
에 의해 한권씩 책을 꺼내 이름을 출력한다.
Iterator 패턴을 적용하면 어떤 장점이 있을까?
for문으로 동일한 표현이 가능한데 왜 굳이 코드를 늘려가며 Iterator 패턴을 사용해야 하는지 의문이 들 수 있다.
Iterator 패턴을 사용하는 가장 큰 이유는 하나씩 꺼내서 처리하는 과정을 구현과 분리할 수 있기 때문이다.
다음의 코드를 살펴보자.
1) Iterator 패턴을 사용했을 때
while (it.hasNext()) {
Book book = (Book) it.next();
System.out.println(book.getName());
}
2) for문을 사용했을 때
for (int i = 0; i < bookShelf.getLength(); i++) {
System.out.println(bookShelf.getBook(i).getName());
}
이 두가지 코드는 실행 시 Book 객체의 내용을 하나씩 꺼내서 보여주는 동일한 결과를 보여준다.
하지만 for문을 사용할 때와 달리 Iterator 패턴을 사용할 때는 어디까지나 Iterator의 메서드를 사용할 뿐 BookShelf의 구현에서 사용되고 있는 메서드는 호출되지 않고있다.
즉, 1번 방식은 BookShelf의 구현에 의존하지 않는다.
BookShelf에 무언가 수정사항이 생기더라도 BookShelf가 올바른 Iterator를 반환하기만 한다면 while문이 사용되는 Main은 수정할 필요가 없다. 하나의 클래스를 수정하더라도 다른 클래스에 큰 영향 없이 작은 수정 만으로도 끝낼 수 있다는 것이다.
요약
- Iterator 패턴은 집합체의 요소를 순서대로 지정하면서 처리하는 방식
- Iterator(반복자) : 요소를 순서대로 검색하는 인터페이스를 결정
- ConcreteIterator(구체적인 반복자) : Iterator가 결정한 인터페이스를 실제로 구현한다. 검색하기 위해 필요한 정보를 가지고 있어야 한다.
- Aggregate(집합체) : Iterator 역할을 만드는 인터페이스를 결정한다.
- ConcreteAggregate(구체적인 집합체) : Aggregate 역할이 결정한 인터페이스를 실제로 구현한다.
이를 클래스 다이어그램으로 표기하면 아래와 같다.
참고 도서
Java 언어로 배우는 디자인 패턴 입문
'if (study) > 디자인 패턴' 카테고리의 다른 글
[디자인 패턴] Adapter 패턴 (어댑터 패턴) (0) | 2019.10.30 |
---|