새소식

design-pattern

[디자인패턴] 반복자(Iterator) 패턴

  • -

반복자(Iterator) 패턴

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

반복자 패턴은 컬렉션의 구현 방법을 노출하지 않으면서 집합체 내의 모든 항목에 접근하는 방법을 제공한다.

예시

아래와 같은 구조의 Book 클래스가 있다.

public class Book {

    private String name;

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

    public String getName() {
        return name;
    }
}

여러 Book 객체를 배열 형식으로 담은 ArrayBookShelf가 있다.

public class ArrayBookShelf {

    private static final int MAX_ITEMS = 5;

    private int numberOfItems = 0;
    private Book[] books;

    public ArrayBookShelf() {
        books = new Book[MAX_ITEMS];

        books[numberOfItems++] = new Book("알고리즘");
        books[numberOfItems++] = new Book("스프링");
        books[numberOfItems++] = new Book("스프링 배치");
        books[numberOfItems++] = new Book("JSP");
        books[numberOfItems++] = new Book("유닉스");
    }
}

그리고 여러 Book 객체들을 List 형식으로 담는 ListBookShelf 클래스가 있다.

public class ListBookShelf {

    private List<Book> books;

    public ListBookShelf() {
        books = new ArrayList<>();
        books.add(new Book("디자인 패턴"));
        books.add(new Book("자바"));
        books.add(new Book("파이썬"));
    }
}

두 가지의 BookShelf에 존재하는 모든 Book을 출력하고 싶을 때, 두 BookShelf는 컬렉션의 구현 방법이 다르기 때문에, 아래와 같이 각각의 컬렉션에 맞는 반복문을 통해 출력해야 한다.

public static void main(String[] args) {
    ArrayBookShelf arrayBookShelf = new ArrayBookShelf();
    ListBookShelf listBookShelf = new ListBookShelf();

    Book[] arrayBookShelfBooks = arrayBookShelf.getBooks();
    for (int i = 0; i < arrayBookShelfBooks.length; i++) {
        System.out.println(arrayBookShelfBooks[i]);
    }

    List<Book> listBookShelfBooks = listBookShelf.getBooks();
    for (int i = 0; i < listBookShelfBooks.size(); i++) {
        System.out.println(listBookShelfBooks.get(i));
    }
}

이러한 상황에서 반복 처리 방법을 캡슐화한다면 어떨까?

이 때 사용할 수 있는 디자인 패턴이 반복자(Iterator) 패턴이다.

반복자 패턴을 구현하기 위해 java.util 패키지에서 제공하는 Iterator 인터페이스를 이용하면 간단히 반복자 패턴을 구현할 수 있다.
Iterator 인터페이스는 컬렉션을 대상으로 반복 작업을 하는데 필요한 메서드들을 제공한다.
Iterator 인터페이스는 컬렉션 반복 처리 방법을 캡슐화 한 것으로 볼 수 있다.

public class ArrayBookShelfIterator implements Iterator<Book> {

    private ArrayBookShelf bookShelf;
    private int position = 0;

    public ArrayBookShelfIterator(ArrayBookShelf bookShelf) {
        this.bookShelf = bookShelf;
    }

    @Override
    public boolean hasNext() {
        return bookShelf.getBooks().length != position;
    }

    @Override
    public Book next() {
        return bookShelf.getBooks()[position++];
    }
}

public class ListBookShelfIterator implements Iterator<Book> {

    private ListBookShelf bookShelf;
    private int position = 0;

    public ListBookShelfIterator(ListBookShelf bookShelf) {
        this.bookShelf = bookShelf;
    }

    @Override
    public boolean hasNext() {
        return bookShelf.getBooks().size() != position;
    }

    @Override
    public Book next() {
        return bookShelf.getBooks().get(position++);
    }
}

위와 같이 컬렉션의 반복 처리를 캡슐화 할 수 있다.

기존 컬렉션을 갖는 객체에서 Iterator 객체를 만들 수 있도록 해야한다.
컬렉션을 갖는 객체는 XXXBookShelf 객체들이다.
따라서 해당 클래스를 통해 Iterator를 생성할 수 있도록 메서드를 추가하고, Iterator 인터페이스를 구현한 구현체 클래스를 작성하여 기능을 추가하면 된다.

java.lang 패키지는 Iterable 인터페이스를 제공한다.
Iterable 인터페이스는 iterator() 메서드를 제공하는데, 이는 Iterator를 반환한다.

public class ArrayBookShelf implements Iterable<Book> {

    private static final int MAX_ITEMS = 5;

    private int numberOfItems = 0;
    private Book[] books;

    public ArrayBookShelf() {
        books = new Book[MAX_ITEMS];

        books[numberOfItems++] = new Book("알고리즘");
        books[numberOfItems++] = new Book("스프링");
        books[numberOfItems++] = new Book("스프링 배치");
        books[numberOfItems++] = new Book("JSP");
        books[numberOfItems++] = new Book("유닉스");
    }

    public int getNumberOfItems() {
        return numberOfItems;
    }

    public Book[] getBooks() {
        return books;
    }

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

public class ListBookShelf implements Iterable<Book> {

    private List<Book> books;

    public ListBookShelf() {
        books = new ArrayList<>();
        books.add(new Book("디자인 패턴"));
        books.add(new Book("자바"));
        books.add(new Book("파이썬"));
    }

    public List<Book> getBooks() {
        return books;
    }

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

아래는 Iterator로 반복을 캡슐화 한 후의 출력 코드이다.

public static void main(String[] args) {
    ArrayBookShelf arrayBookShelf = new ArrayBookShelf();
    ListBookShelf listBookShelf = new ListBookShelf();

    printBooks(arrayBookShelf.iterator());
    printBooks(listBookShelf.iterator());
}

private static void printBooks(Iterator iterator) {
    while (iterator.hasNext()) {
        System.out.println(iterator.next());
    }
}

반복을 캡슐화하여 컬렉션이 어떻게 구현되었는지와 상관없이 공통적으로 처리할 수 있게 되었다.

[디자인패턴] 반복자(Iterator) 패턴

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.