Mobile & App Stack/Java Software Architecture & Patterns

이터레이터 패턴: 데이터 구조와 상관없이 일관되게 순회하는 법

임베디드 친구 2025. 1. 6. 08:33
728x90
반응형

배열로 저장된 데이터를 리스트(List)나 세트(Set)로 변경했을 때, 데이터를 출력하는 모든 for 루프 코드를 수정해야 했던 경험이 있으신가요? 데이터 저장 방식이 바뀌더라도 출력하는 코드는 그대로 유지할 수 있다면 얼마나 편할까요?

오늘은 컬렉션의 구현 방법을 노출하지 않으면서도 집합체 안에 들어있는 모든 항목에 접근할 수 있게 해주는 이터레이터 패턴(Iterator Pattern)에 대해 알아보겠습니다.

Generated by Gemini AI.


1. 이터레이터 패턴이란?

이터레이터 패턴은 컬렉션(집합체)의 내부 구조를 노출하지 않고 요소들을 순차적으로 탐색할 수 있는 인터페이스를 제공하는 디자인 패턴입니다.

이 패턴을 사용하면 내부가 배열로 되어 있든, 트리 구조로 되어 있든 상관없이 클라이언트는 오직 hasNext()와 next()라는 동일한 방법으로 요소에 접근할 수 있습니다.


2. 왜 이터레이터 패턴을 써야 할까? (장점)

  • 집합체와 알고리즘의 분리: 집합체의 내부 구조(배열, 리스트 등)가 변경되어도 순회하는 코드는 영향을 받지 않습니다.
  • 단일 책임 원칙(SRP): 집합체 클래스는 데이터를 관리하는 일에만 집중하고, 순회 로직은 이터레이터가 담당합니다.
  • 일관된 인터페이스: 어떤 컬렉션이라도 동일한 인터페이스로 순회할 수 있어 코드의 가독성이 높아집니다.

3. 이터레이터 패턴의 구조

패턴을 구성하는 4가지 핵심 요소는 다음과 같습니다.

  1. Iterator (인터페이스): 요소를 탐색하기 위한 인터페이스 (hasNext, next)
  2. ConcreteIterator: 인터페이스를 구현하며 탐색 중인 현재 위치를 추적합니다.
  3. Aggregate (인터페이스): 이터레이터 객체를 생성하는 메서드를 정의합니다.
  4. ConcreteAggregate: 실제 데이터 리스트를 보유하며, 그에 맞는 이터레이터를 생성하여 반환합니다.

4. Java 실무 예제: 커스텀 컬렉션 순회하기

자바의 표준 Iterator 인터페이스를 활용하여 간단한 도서 목록 시스템을 구현해 보겠습니다.

Step 1. 데이터 객체와 집합체 정의

Java
 
import java.util.Iterator;
import java.util.NoSuchElementException;

// 데이터 객체
class Book {
    private String title;
    public Book(String title) { this.title = title; }
    public String getTitle() { return title; }
}

// ConcreteAggregate: 도서 목록 컬렉션
class BookShelf implements Iterable<Book> {
    private Book[] books;
    private int last = 0;

    public BookShelf(int size) { this.books = new Book[size]; }

    public void addBook(Book book) {
        if (last < books.length) { books[last++] = book; }
    }

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

    // ConcreteIterator: 내부 클래스로 구현하여 컬렉션 데이터에 접근
    private class BookShelfIterator implements Iterator<Book> {
        private int index = 0;

        @Override
        public boolean hasNext() {
            return index < last;
        }

        @Override
        public Book next() {
            if (!hasNext()) throw new NoSuchElementException();
            return books[index++];
        }
    }
}

Step 2. 실행 및 테스트

Java
 
public class IteratorDemo {
    public static void main(String[] args) {
        BookShelf shelf = new BookShelf(4);
        shelf.addBook(new Book("Clean Code"));
        shelf.addBook(new Book("Design Patterns"));
        shelf.addBook(new Book("Java Optimization"));

        // 이터레이터를 통한 일관된 순회
        Iterator<Book> it = shelf.iterator();
        while (it.hasNext()) {
            System.out.println("도서명: " + it.next().getTitle());
        }

        // Iterable을 구현했으므로 for-each 구문도 사용 가능!
        System.out.println("\n-- for-each 구문 사용 --");
        for (Book book : shelf) {
            System.out.println(book.getTitle());
        }
    }
}

5. 실생활 비유: TV 채널 돌리기

우리가 TV 내부 회로가 어떻게 구성되어 있는지 몰라도 리모컨의 '다음' 버튼(next)만 누르면 채널을 순차적으로 넘길 수 있는 것과 같습니다. 리모컨이 바로 TV 채널이라는 집합체에 대한 이터레이터 역할을 하는 셈이죠.

결론

이터레이터 패턴은 "컬렉션의 구현을 숨기면서 요소를 꺼내는 방법을 표준화"하는 강력한 도구입니다. 자바 개발자라면 Iterable 인터페이스와 함께 이 패턴을 이해함으로써 더 유연하고 객체지향적인 코드를 작성할 수 있습니다.


도움이 되셨다면 공감과 구독 부탁드립니다! 디자인 패턴에 대해 더 궁금한 점이 있다면 댓글로 남겨주세요.

반응형