본문 바로가기
DEV/디자인패턴

Iterator(반복자) 패턴

by 어쩌다개발 2024. 3. 6.
반응형

배열의 요소를 표시하려면 다음과 같이 for 문을 사용한다.

for(int i = 0; i < arr.length; i++) {
	System.out.println(arr[i]);
}

arr[i]에서 i에 주목해야 한다. 변수 i는 0으로 초기화되고 그 다음 1, 2, 3,...으로 증가하고, 그때마다 arr[i]의 값이 표시된다.
이렇게 i를 늘려 가다보면 배열 arr의 요소 전체를 처음부터 순서대로 검색하게 된다.
여기에서 사용되는 변수 i의 기능을 추상화하여 일반화한 것을 디자인 패턴에서 Iterator 패턴이라고 한다.

Iterator 패턴은 무엇인가 많이 모여 있을 때 이를 순서대로 가리키며 전체를 검색하고 처리를 반복하는 것이다.

이름 설명
Iterable<E> 집합체를 나타내는 인터페이스(java.lang 패키지)
Iterator<E> 처리를 반복하는 반복자를 나타내는 인터페이스(java.util.패키지)
Book 책을 나타내는 클래스
BookShelf 책장을 나타내는 클래스
BookSelfIterator 책장을 검색하는 클래스
Main 동작 테스트용 클래스

 

Iterable<E> 인터페이스
public interface Iterable<E> {
	public abstract Iterator<E> iterator();
}

Iterable<E> 인터페이스에는 iterator 메서드가 선언되어 있다.
이 메소드는 집합체에 대응하는 Iterator<E>를 만들기 위한 것이다.
집합체에 포함된 요소를 하나하나 처리해 나가고 싶을 때는 이 iterator 메소드를 사용해 Iterator<E> 인터페이스를 구현한 클래스의 인스턴스를 하나 만든다.

Iterator<E> 인터페이스

Iterator<E> 인터페이스는 하나하나의 요소 처리를 반복하기 위한 것으로 루프 변수와 같은 역할을 한다.

public interface Iterator<E> {
	public abstract boolean hasNext();
    	public abstract E next();
}

1) hasNext : '다음 요소'가 존재하는지 알아 보는 메소드.
- 다음 요소가 존재한다면 true를 반환한다. 존재하지 않으면(마지막 요소까지 도달) false를 반환
- 루프 종료 조건으로 사용하기 위한 것

2) next : '다음 요소'를 가져오는 메소드
- 반환값 형태가 파라미터 E(Element, 요소) 인 것에서 알 수 있듯이, 집합체의 요소를 1개 반환.
- 다음 요소를 반환할 수 있도록 내부 상태를 다음으로 진행시켜 놓은 역할이 뒤에 숨어 있음.

Book 클래스
//책
public class Book {

    //책 이름
    private String name;

    //이름을 정하는 생성자
    public Book(String name) {
        this.name = name;
    }

    //이름을 가져오는 메서드
    public String getName() {
        return name;
    }
}

해당 클래스는 책 이름을 getName 메소드를 얻는 것 뿐이다.

BookShelf 클래스
import java.util.Iterator;

//책장 implements 집합체를 나타내는 인터페이스
public class BookShelf implements Iterable<Book>{

    //책들, private 으로 선언하여 직접 접근을 방지
    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;
    }

    @Override
    public Iterator<Book> iterator() {
        return new BookShelfIterator(this);
    }
}

책장을 나타내는 클래스로 집합체를 다루기 위해 Iterable<Book> 인터페이스를 구현하고 있다.
또한, Iterable<Book> 인터페이스에 선언되어 있던 iterator 메소드의 실체가 적혀있다.
iterator 메서드는 BookShelf 클래스에 대응하는 Iterator로서, BookShelfIterator 클래스의 인스턴스를 생성하여 반환한다.
책장에 꽂혀 있는 책을 반복해서 처리하고 싶을 때 iterator 메서드를 호출한다.

BookShelfIterator 클래스
import java.util.Iterator;
import java.util.NoSuchElementException;

//책장검색 implements (처리를 반복하는 반복자를 나타내는 인터페이스)
public class BookShelfIterator implements Iterator<Book> {

    //책장
    private BookShelf bookShelf;
    //순서
    private int index;

    public BookShelfIterator(BookShelf bookShelf) {
        this.bookShelf = bookShelf;
        this.index = 0;
    }


    @Override
    public boolean hasNext() {
        if (index < bookShelf.getLength()) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public Book next() {
        if (!hasNext()) {
            throw new NoSuchElementException();
        }

        Book book = bookShelf.getBookAt(index);
        index++;
        return book;
    }
}

BookShelfIterator는 Iterator<Book>인터페이스를 구현하고 있으므로, Iterator<Book> 형으로 다룰 수 있다.
bookShelf 필드는 BookShelfIterator가 검색할 책장이고, index 필드는 현재 보고 있는 책을 가리키는 첨자이다.

생성자에서는 전달된 BookShelf 인스턴스를 bookShelf 필드에 저장하고 index를 0으로 지정한다.

hasNext 메서드는 Iterator<Book> 인터페이스에서 선언된 메서드를 구현한 것이다. ' 다음책'이 있는지 조사해서, 있으면 true 없으면 false를 반환한다. 다음 책이 있는지 없는지는 index가 책장에 꽂힌 책의 수보다 작은지 비교해서 판단한다.

next 메소드는 현재 주목하는 책을 반환하고, 다시 '다음'으로 진행시키는 메서드이다.
Iterator<Book> 인터페이스로 선언되어 있는데 반환값으로 돌려 줄 책을 book 변수에 저장해두고, index를 다음으로 진행시킨 후 book을 return 한다.  index를 다음으로 진행하는 처리는 for문의 i++에 해당하는 처리이다.

Main 클래스
import java.util.Iterator;

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를 사용하는 방법
        Iterator<Book> it = bookShelf.iterator();
        while (it.hasNext()) {
            Book book = it.next();
            System.out.println(book.getName());
        }

        System.out.println();

        for (Book book : bookShelf) {
            System.out.println(book.getName());
        }

        System.out.println();
    }
}

 

                                                     Collection

                                                      |               |

                                                     List        Set

                                              |                           |

                                       - ArrayList           - HashSet

                                       - LinkedList         - TreeSet

                                       - Stack                 - LinkedHashSet

                                       - Vector

컬렉션에는 여러종류가 있다.

구현이 다른 컬렉션도 forEach 하나로 조회 가능한데, iterorator패턴이 있기 때문이다.

컬렉션 요소를 정렬, 순서,중복여부 등 구현방식과 상관없이 조회에만 집중하는 인터페이스이다.

Iterator는 hasNext, next 두 개의 메서드로 이루어져 있다.

모든 컬렉션은 이터레이터를 구현하고 있다.

결론 : 
여러 종류의 컬렉션을 하나의 인터페이스로 조회하는 패턴으로, 이미 컬렉션에 iterator 패턴이 적용되어 있기때문에 컬렉션을 잘 가져다 쓰면 된다!!

 

관련 패턴

- Visitor 패턴
- Composite 패턴
- Factory Method 패턴

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

반응형

'DEV > 디자인패턴' 카테고리의 다른 글

01. UML에 대해서(클래스/시퀀스 다이어그램)  (0) 2024.02.27

댓글