다시보는 디자인 패턴: Iterator

Ji Hoon Jeon·2020년 3월 22일
0
post-thumbnail

오늘은 디자인 패턴 중에 Iterator(반복자)에 대해 공부한 내용을 작성해 보려고 한다. 해당 포스팅은 "Java 언어로 배우는 디자인 패턴 입문" 이라는 책을 바탕으로 작성되었다. 참고로 이 책은 Gof 디자인 패턴을 읽기 전 입문서로 적합한 책이다.


들어가기 전에...

Iterator(반복자) 패턴 정의
무엇인가 많이 모여 있는 것들을 순서대로 지정하면서 전체를 검색하는 처리를 실행하기 위한 패턴

사실 딱 읽고 와 닿지 않는 말이다.

이 말을 조금 쉽게 이해하기 위해 먼저 아래 for문을 살펴보자. Java 언어에서 배열 arr의 모든 요소를 표시하기 위해서는 아래처럼 코드를 작성할 수 있다.

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

이제 코드를 위에 정의했던 내용에 적용시켜 보자.

"무엇인가 많이 모여 있는 것들" : arr 배열
"순서대로 지정하면서 전체를 검색" : i 변수

여기서 주목해봐야 하는 건 "i" 라는 변수이다. "i" 변수는 다음과 같은 기능을 한다. 1) 배열의 위치를 나타내 준다. 2) 값을 증가/감소 시켜 배열의 전체를 검색할 수 있도록 도와준다.

이 기능을 추상화하여 일반화 한 것을 Iteator(반복자) 패턴이라고 부른다.


예제를 통해...

요구사항

책(Book)을 관리해 주는 서가(BookShelf)와 같은 기능이 필요하다.

(관리 기능)
1. 서가(BookShelf)에 책(Book)을 넣을 수 있다.
2. 서가(BookShelf) 내부에 있는 모든 책(Book)을 순서대로 검색 혹은 표시 할 수 있다.

(제약 조건)
1. for loop 같은 반복문을 통해 배열에 직접 접근하지 않는다.
2. Java에서 기존에 제공하는 Iterator를 사용하지 않는다.

등장인물 소개

이름역할
Aggreate집합체를 나타내는 인터페이스
Iterator하나씩 나열하면서 검색을 실행하는 인터페이스
Book책을 나타내는 클래스
BookShelf서가를 나타내는 클래스
BookShelfIterator서가를 검색하는 클래스
Main테스트 클래스

등장인물 모습

등장인물 역할수행

Aggregate(집합체) 인터페이스

public interface Aggreate {
    public abstract Iterator iterator();
}

Aggregate 인터페이스를 통해 Iterator가 주어진 역할을 할 수 있도록 만들어 준다. (API 역할)

Iterator 인터페이스

public interface Iterator {
    public abstract boolean hasNext();
    public abstract Object next();
}

실제 요소를 순서대로 접근할 수 있는 메서드를 가지고 있는 인터페이스이다. 해당 인터페이스는 2개의 메서드를 가지고 있으며, 각각의 역할은 다음 요소가 존재하는 지 확인하는 hasNext() 메서드와 다음 요소를 가져오는 next()로 구성되어 있다. 사실 next() 메서드는 다음 요소를 가져올 수 있을 뿐 아니라 다음 요소로 이동하는 역할도 함께 수행되고 있다. (for loop "i++" 역할)

Book 클래스

public class Book {
    private String name;
    
    public Book(String name) {
        this.name = name;
    }
    
    public Strig getName() {
        return name;
    }
}

Book 클래스는 책을 나타내는 클래스이다. 이 클래스의 역할은 getName() 메서드를 통해 책의 이름을 제공해 준다.

BookShelf 클래스

public class BookShelf implements Aggregate {
    private Book[] books;
    private int last = 0;
    
    public BookShelf(int maxSize) {
        this.books = new Book[mazSize];
    }
    
    public Book getBookAt(int index) {
        return book[index];
    }
    
    public void appendBook(Book book) {
        this.books[last] = book;
    }
    
    public int getLength() {
        return last;
    }
    
    public Iterator iterator() {
        return new BookShelfIterator(this);
    }
}

Aggregate 인터페이스를 상속받은 걸로 보아 Aggregate 인터페이스에서 정의한 Iterator 기능을 가지고 있는 클래스라는 것을 눈치 챌 수 있다. 하지만 next(), hasNext()의 구체적인 기능은 보이지 않는다.
단지 BookShelfIterator 클래스를 생성하고 있다. 계속 살펴보자.

BookShelfIterator 클래스

public class BookShelfIterator implements Iterator {
    private BookShelf bookShelf;
    private int index;

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

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

    public Object next() {
        Book book = bookShelf.getBookAt(index);
        ++index;
        return book;
    }
}

드디어 Iterator의 구현부가 있는 클래스이다. hasNext() 메서드와 next() 메서드가 구현되어 있다. 위에서 잠깐 언급했듯이 next() 메서드에는 다음 요소도 이동도 포함이 되어 있다는 사실을 알 수 있다.

Main 클래스

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

위에서 살짝 언급한 내용이다.
Main 클래스에 이상한 부분이 하나 있다. 바로 Iterator it=bookShelf.iterator();여기다. BookShelfIterator 클래스의 모습은 어디에도 안 보인다. 이 클래스에 내 모든 핵심이 있는데...왜 안 보일까? 다시 BookShelf 클래스로 돌아가 보자. 맨 밑에 new BookShelfIterator(); BookShelf 클래스에서 이미 만들어 놓았다. 이미 만들어진 객체를 그냥 사용하기만 하면 된다. 심지어 Main 클래스는 BookShelfIterator의 존재를 전혀 알지 못해도 Iterator를 사용하는 데 아무 문제가 없다.

for문 보다 복잡한 걸 왜 쓰나?

사용 목적

  • 내부 구현을 노출 시키지 않고 집약(집합) 객체(배열과 같은)에 접근하고 싶은 경우
  • 집약(집합) 객체에 다양한 탐색 경로가 필요한 경우 (Iterator 인터페이스 상속 구조)
    역방향 탐색, 특정 인덱스 탐색 등
  • 서로 다른 집합 객체 구조에 대해서도 동일한 방법으로 접근하고 싶은 경우
    ArrayList, LinkedList와 같은 다른 자료 구조에서도 동일한 방법 사용 가능

책에는 없지만 내 마음대로

객체지향에서 많이 이야기하는 재 사용성에 대한 고민이 들어간 패턴이라는 생각이 든다. 사실 개발하기도 바쁜데, 재 사용성을 고려해서 코딩하기는 생각보다 쉽지는 않을 뿐더라 고민도 많이 해야 하는 부분이다. 어떻게 보면 재 사용성을 고려한다는 건 변화가 많이 일어날 가능성이 있는 부분과 그나마 변화가 작게 일어날 곳을 구분하는 것에서 출발한다고 생각한다. 재 사용을 할 수 있는 곳은 당연하게 변화가 적게 일어나는 곳에서 가능할 것이다. 위에 예제를 다시 보면 서가 관리에서 변화가 집중될 수 있는 BookShelf 클래스와 반복문을 분리함으로 반복문 재 사용 효과를 노려 본 패턴이라 할 수 있다.


References

  • Gof 디자인 패턴
  • Java 언어로 배우는 디자인 패턴 입문
profile
길을 아는 것과 그 길을 걷는 것은 다르다.

0개의 댓글