ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [Design Pattern] 이터레이터 패턴 (Iterator Pattern)
    공부/디자인 패턴 2021. 6. 20. 20:18

    이터레이터 패턴은 컬렉션의 구현 방법을 노출시키지 않고 그 안에 존재하는 모든 항목에 접근할 수 있도록 하는 패턴입니다. 이터레이터 패턴을 사용하면 컬렉션 내에 구현이 어떤지 몰라도 해당 객체에 접근해 반복 작업을 처리할 수 있습니다.

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

    위에서 i는 arr이라는 배열의 위치를 나타내는 변수입니다. 이러한 기능을 추상화하여 일반화 한 것을 이터레이터 패턴이라 합니다.

    클래스 다이어그램

    iterator: 컬렉션의 요소들을 순서대로 검색하기 위한 인터페이스

    ConcreteInterface: iterator 인터페이스 구현체

    Aggregate: 여러 요소들로 구성된 컬렉션 인터페이스

    ConcreteAggregate: Aggregate 인터페이스 구현체

    예시

    여기서 책을 등록하고 순회하면서 등록한 책 목록을 출력한다고 가정합니다. 여기서 중요한 것은 자바 Iterator 라이브러리를 import하는 것이 아닌 직접 만든 Iterator 인터페이스를 import해야 합니다.

    public interface Iterator {
        boolean hasNext();
        Object next();
    }
    
    public interface Aggregate {
        Iterator CreateIterator();
    }
    
    public class Book {
        private final String name;
    
        public Book(String name) {
            this.name = name;
        }
    
        public String getName() {
            return name;
        }
    }
    
    public class BookShelfIterator implements Iterator {
        private final BookShelfAggregate bookShelfAggregate;
        private int index;
    
        public BookShelfIterator(BookShelfAggregate bookShelfAggregate) {
            this.bookShelfAggregate = bookShelfAggregate;
            this.index = 0;
        }
    
        @Override
        public boolean hasNext() {
            return index < bookShelfAggregate.getLength();
        }
    
        @Override
        public Object next() {
            Book book = bookShelfAggregate.getBookAt(index);
            index++;
            return book;
        }
    }
    
    public class BookShelfAggregate implements Aggregate {
        private final Book[] books;
        private int last = 0;
    
        public BookShelfAggregate(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 CreateIterator() {
            return new BookShelfIterator(this);
        }
    }
    
    BookShelfAggregate bookShelfAggregate = new BookShelfAggregate(3);
    
    bookShelfAggregate.appendBook(new Book("자바"));
    bookShelfAggregate.appendBook(new Book("파이썬"));
    bookShelfAggregate.appendBook(new Book("golang"));
    
    System.out.println("개수 : "+bookShelfAggregate.getLength());
    
    Iterator it = bookShelfAggregate.CreateIterator();
    
    while(it.hasNext()){
        Book book = (Book) it.next();
        System.out.println(book.getName());
    }
    
    //개수 : 3
    //자바
    //파이썬
    //golang

    서로 다른 집합 객체 구조에 대해서도 동일한 방법으로 접근하고 싶은 경우 사용하는 패턴이지만 실제로 쓰일지는 잘 모르겠습니다.

    파이썬

    파이썬의 경우, 매직 메소드로 iternext가 탑재되어 있어 자바보다 구현하기가 훨씬 간단합니다. BookShelf 클래스에서 Aggregate이자 Iterator 역할을 전부 처리할 수 있습니다. 만약 내부 구현이 복잡하게 들어가야 한다면 list를 상속받아 보다 쉽게 구현할 수도 있습니다.

    추가로 파이썬의 iterator는 자바의 hasNext 메소드와 동일한 기능을 가진 메소드가 존재하지 않아 아래에선 커스텀하게 구현한 모습입니다.

    class Book:
        def __init__(self, name):
            self._name = name
    
        @property
        def name(self):
            return self._name
    
    class BookShelf:
        def __init__(self):
            self._book = []
            self._last = 0
            self._current = 0
    
        def append(self, book):
            self._book.append(book)
            self._last += 1
    
        def length(self):
            return self._last
    
        def __iter__(self):
            return self
    
        def __next__(self):
            if self._current < self._last:
                r = self._current
                self._current += 1
                return self._book[r]
            else:
                raise StopIteration
    
        def has_next(self):
            return self._current < self._last
    
    book_shelf_iterator = BookShelf()
    
    book_shelf_iterator.append(Book('자바'))
    book_shelf_iterator.append(Book('파이썬'))
    book_shelf_iterator.append(Book('golang'))
    
    print('개수: ', book_shelf_iterator.length())
    
    for book in book_shelf_iterator:
        print(book.name)
    
    // 또는 has_next를 사용해 while문을 구현한 모습
    // while book_shelf_iterator.has_next():
    //     book = book_shelf_iterator.__next__()
    //     print(book.name)
    
    개수:  3
    자바
    파이썬
    golang

    댓글