[디자인 패턴] Chapter 1. 이터레이터(Iterator) 패턴
01. Iterator 패턴
for 문의 변수 i의 역할
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
- 변수 i: 여러 원소가 모여있는 배열의 각 원소를 차례대로 선택하는 사용된다.
Iterator 패턴
- 변수 i의 기능을 추상화해서 일반화 시켰음
- 무엇인가 많이 모여있는 것 중에서 하나씩 끄집어내서 열거하면서 전체를 처리하는 일을 할 때 이 패턴을 적용한다.
02. 예제 프로그램
책꽂이(BookShelf)에 책(Book)을 넣은 후, 순서대로 다시 끄집어 내서 책 이름을 표시하는 프로그램
각 클래스와 인터페이스 설명
이름 | 해설 |
---|---|
Aggregate | 집합체를 나타내는 인터페이스 |
Iterator | 하나씩 열겨하면서 스캔을 실행하는 인터페이스 |
Book | 책을 나타내는 클래스 |
BookShelf | 서가를 나타내는 클래스 |
BookShelfIterator | 서가를 검색하는 클래스 |
Main | 동작 테스트용 클래스 |
각 인터페이스나 클래스를 클릭하여 코드와 기능을 상세하게 볼 수 있습니다.
Aggregate 인터페이스
Aggregate 인터페이스 (sample/Aggregate.java)
- iterator(): 집합체에 대응하는 Iterator 한 개를 생성하는데 사용될 메소드
- 어떤 집합체 원소를 하나씩 열거하거나 조사하고자 할 때 이 메소드를 사용해서 Iterator 인터페이스를 구현한 클래스의 인스턴스를 한 개 얻어온다.
public interface Aggregate {
public abstract Iterator iterator();
}
Iterator 인터페이스
Iterator 인터페이스 (sample/Iterator.java)
- 집합체의 원소를 하나하나 끄집어내는 루프 변수와 같은 역할을 한다.
- hasNext(): 다음 원소가 존재하는지 조사할 때 사용하는 메소드
- 반환형은 boolean (마지막 원소에 도달하면 false를 반환함)
- next(): 다음 원소를 얻어올 때 사용하는 메소드
- 반환형은 Object
public interface Iterator {
public abastract boolean hasNext();
publci abstract Object next();
}
Book 클래스
Book 클래스(sample/Book.java)
- 책을 나타내는 클래스
- name 속성: 책이름을 저장하는 변수
- getName(): 책의 이름을 얻어올 때 호출하는 메소드
public class Book {
private String name;
public Book(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
BookShelf 클래스
BookShelf 클래스 (sample/BookShelf.java)
- 책꽂이를 나타내는 클래스 = 집합체(aggregate)
- Aggregate 인터페이스를 구현하였다.
- BookShelf 클래스는, iterator() 메소드의 구현 부분을 제공한다.
- BookShelf 클래스는, 이 외에 다른 추가의 메소드도 제공한다.
- books 필드: Book의 배열
- 이 배열의 크기는, 생성자(BookShelf()) 호출 시 지정된다.
- private으로 선언된 이유는, 클래스 외부에서 이 필드를 변경하지 못하게 하기 위해서이다.
- appendBook(): 책 한권을 서가에 추가하는 메소드
- getLength(): 현재 책꽂이에 있는 책의 개수를 반환하는 메소드
- iterator(): 책꽂이의 책 하나하나를 끄집어내는 일을 하는 BookShelfIterator를 생성하는 메소드
public class BookShelf implements Aggregate {
private Book[] books;
private int last = 0;
public BookShelf(int maxsize) {
this.books = new Book[maxsize];
}
public Book getBookAt(int index) {
return books[index];
}
public void appendBook(Book book) {
this.books[last] = book;
last++;
}
public int getLength() {
return last;
}
public Iterator iterator() {
return new BookShelfIterator(this);
}
}
BookShelfIterator 클래스
BookShelfIterator 클래스 (sample/BookShelfIterator.java)
- 책꽂이(BookShelf)에 있는 책들을 하나씩 끄집어내는 일을 하는 클래스
- Iterator 인터페이스를 구현하였다.
- hasNext()와 next() 메소드를 구현함.
- BookShelf 필드: BookShelfIterator가 검색할 책꽂이를 가리키는 변수(생성자에서 넘겨받은 BookShelf의 인스턴스를 가지고 있음)
- index 필드: 책꽂이에서의 현재 책을 가리키는 변수
- hasNext(): 다음 책이 있으면 true, 없으면 false를 반환함
- next(): 현재 가리키고 있는 책을 반환하고, 다음 책을 가리키는 메소드
public class BookShelfIterator implements Iterator {
private BookeShelf bookShelf;
private int index;
public BookShelfIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
this.index = 0;
}
public boolean hasNext() {
if (index < bookShelf.getLength()) {
return true;
} else {
return false;
}
}
public Object next() {
Book book = bookShelf.getBookAt(index);
index++;
return book;
}
}
Main 클래스
Main 클래스 (sample/Main.java)
- main()
- 책 4권을 책꽂이 넣는다.
- 책꽂이의 책을 하나씩 끄집어낼 Iterator를 얻는다.
Iterator it = bookShelf.iterator();
- Iterator의 hasNext()와 next() 메소드를 이용하여 책을 하나씩 끄집어내서 책의 이름을 출력한다.
import java.util.*;
public class Main {
public static void main(String[] args) {
BookShelf bookShelf = new BookShelf(4);
bookShelf.appendBook(new Book("Around the World in 80 Days"));
bookShelf.appendBook(new Book("Bible"));
bookShelf.appendBook(new Book("Cinderella"));
bookShelf.appendBook(new Book("Daddy-Long-Legs"));
Iterator it = bookShelf.iterator();
while (it.hasNext()) {
Book book = (Book)it.next();
System.out.println(book.getName());
}
}
}
03. Iterator 패턴에 등장하는 역할
Iterator(반복자)의 역할
- 원소를 하나씩 끄집어낼 때 사용할 공통된 메소드를 선언한 인터페이스
- hasNext()와 next() 메소드 선언
ConcreteIterator(구체적인 반복자)의 역할
- Iterator 인터페이스를 실제로 구현하는 클래스
- BookShelfIterator가 이 역할을 담당하였다.
Aggregate(집합체)의 역할
- Iterator를 만들어내는 인터페이스를 제공함.
- iterator(): 내가 가지고 있는 각 원소들을 차례로 검색해 줄 사람을 만들어내는 메소드
ConcreteAggregate(구체적인 집합체)의 역할
- Aggregate 인터페이스를 구현하는 클래스
- ConcreteIterator(구체적인 반복자) 객체를 생성한다.
- BookShelf가 이 일을 담당하였다.
04. 독자의 사고를 넓혀주는 힌트
왜 Iterator 패턴이 유용한가?
- 집합체가 원소를 어떻게 구현하고 있든지 상관없이, 집합체의 원소를 차례로 끄집어내고자 하면, Iterator의 hasNext()와 next() 메소드를 사용하면 된다.
- while루프는 BookShelf 구현과 무관
- 예: BookShelf가 Book을 배열에 저장하지 않고, vector에 저장하도록 수정하더라도, Main 클래스의 main() 메소드의 다음 부분을 변경하지 않아도 된다.
-
while루프는 변경하지 않음
while (it.hasNext() ) { Book book = (Book)it.next(); System.out.println("" + book.getName()); }
-
while루프는 변경하지 않음
- 집합체의 각 원소를 끄집어내는 방법이 집합체 구현과 무관하다.
- 하나를 수정했을 때, 수정해야 할 부분을 저게 하는 것이 중요하다.
코드의 어떤 부분을 수정했을 때, 그것으로 인해 수정해야 할 코드 부분을 적게 하는 것이 중요하다.
가능한 한 추상 클래스나 인터페이스를 자주 사용해라
- 구체적인 클래스만으로 프로그래밍하는 것은 것은 좋지 않다.
Aggregate와 Iterator의 대응 관계
- Aggregate 클래스와 Iterator 클래스는 밀접하게 관련이 있다.
- 예: BookShelf의 getBookAt() 메소드의 이름을 getBookFrom() 으로 바꾸면, BookShelfIterator의 next()도 수정해야 한다.
여러 종류의 Iterator를 만들 수 있다
- 예: 역방향으로 책을 끄집어내는 BookShelfIteratorBackward를 만든 후, BookShelf의 iterator() 메소드를 다음과 같이 변경하면, 다른 종류의 Iterator를 생성할 수 있다.
return new BookShelfIteratorBackward(this);
05. Iterator 패턴과 관련된 패턴
Visitor 패턴
Composite 패턴
Factory Method 패턴
06. 이 장에서 학습한 내용
집합체의 원소를 일관된 방법으로 하나하나 세는 Iterator 패턴
연습문제
BookShelf 클래스에서는 Book을 배열에 저장하기 때문에 책꽂이의 크기를 초과해서 책을 넣을 수 없다
- java.util.ArrayList 클래스를 사용하면, 크기 제한없이 책을 계속해서 넣을 수 있다. 기존의 코드를 이렇게 바꾸시오.
Iterator 보충
클라이언트가, 출석부에 있는 학생을 차례대로 끄집어내 오려면, 출석부의 iterator() 메소드를 호출하여 출석부Iterator를 얻어온다. 그리고 나서, 출석부Iterator의 hasNext()와 next()를 이용하여 각 학생을 가져온다.
댓글남기기