CS

[CS] 이터레이터 패턴

펭귄코기 2022. 10. 20. 14:03

이터레이터(Iterator) 패턴 이란?

무언가 많이 모여있는 것을 하나씩 지정해서 순서대로 처리하는 패턴이다

주로 컬렉션의 요소들에 접근하는 디자인 패턴인데

여러가지 자료형의 구조와는 상관없이 이터레이터라는 하나의 인터페이스로 순회 가능하다

 

보통 프로그래밍에서 반복이 필요하면 for문을 사용한다

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

 

for문의 초기화문에서 흔히 사용되는 변수 i는 배열이 주어질 때

각각의 요소에 차례대로 접근하기 위해 사용한다

이때 변수 i의 역할을 추상화 한 것이 iterator 패턴이다

 

책꽂이 안에 책을 꽂고 다시 책을 하나씩 확인하는 예시를 들어보자

 

BookShelf 예시

Book : 한권의 책에 대한 정보를 가지고 있는 클래스

public class Book {
    private String name;

    public Book(String name) {
          this.name = name;
    }

    public String getName() {
        return name;
    }
}

 

Aggregate : 집합체를 의미하는 인터페이스

Iterator 역할을 만들어내는 인터페이스를 결정한다

public interface Aggregate {
    public abstract Iterator createIterator();
}

 

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);
    }
}

 

BookShelfIterator : BookShelf 클래스에서 검색을 수행하는 클래스

BookShelfIterator를 Iterator로 다루기 위해 Iterator 인터페이스를 상속받았다

Iterator 인터페이스는 Java에 이미 내장되어 있기 때문에 따로 구현하지 않았다

next()는 다음번 요소를 반환하고

hasNext()는 검색을 계속 수행해도 될지의 여부를 판별한다

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;
    }
}

 

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());
        }
    }
}

 

기능 요약

Iterator 패턴은 집합체의 요소를 순서대로 지정하면서 처리하는 방식

Iterator(반복자) : 요소를 순서대로 검색하는 인터페이스를 결정

ConcreteIterator(구체적인 반복자) : Iterator가 결정한 인터페이스를 실제로 구현한다. 검색하기 위해 필요한 정보를 가지고 있어야 한다

Aggregate(집합체) : Iterator 역할을 만드는 인터페이스를 결정한다

ConcreteAggregate(구체적인 집합체) : Aggregate 역할이 결정한 인터페이스를 실제로 구현한다

 

Iterator 패턴을 사용하는 이유와 장점

for문을 사용하면 동일하게 표현이 가능한데 굳이 Iterator패턴을 쓰는 이유가 뭘까

가장 큰 이유는 하나씩 꺼내서 처리하는 과정을 구현과 분리할 수 있기 때문이다

 

Iterator 패턴을 사용했을 때

while (it.hasNext()) {
    Book book = (Book) it.next();
    System.out.println(book.getName());
}

 

for문을 사용했을 때

for (int i = 0; i < bookShelf.getLength(); i++) {
    System.out.println(bookShelf.getBook(i).getName());
}

 

위에 두가지 코드는 Book 객체의 내용을 하나씩 꺼내서 보여주는 동일한 결과를 보여준다

하지만 Iterator패턴을 사용하면 Iterator메서드를 사용할 뿐 BookShelf의 구현에서

사용되고 있는 메서드는 호출되지 않고있다

 

즉 쉽게 말해서 Iterator패턴을 사용하면 BookShelf의 구현에 의존하지 않는다

BookShelf에 무언가 수정사항이 생기더라도 BookShelf가 올바른 Iterator를 반환하기만 한다면

while문이 사용되는 Main은 수정할 필요가 없다

하나의 클래스를 수정하더라도 다른 클래스에 영향을 주지않고 작은 수정만으로도 끝낼 수 있다