Menu
Coddy logo textTech

이터레이터 패턴

Coddy Java 여정의 객체 지향 프로그래밍 섹션에 포함된 레슨 — 87개 중 76번째.

반복자 패턴(Iterator Pattern)은 컬렉션의 내부 구조를 노출하지 않고 요소들에 순차적으로 접근할 수 있는 방법을 제공하는 행동 디자인 패턴입니다. 배열, 연결 리스트, 또는 트리 중 무엇을 사용하든, 반복자는 요소들을 순회하는 일관된 방식을 제공합니다.

이 패턴은 순회 로직을 컬렉션 자체에서 분리합니다. 여기에는 두 가지 주요 구성 요소가 포함됩니다: 요소를 순회하기 위한 메서드를 정의하는 Iterator 인터페이스와, 해당 컬렉션에 대한 반복자를 생성하는 Iterable(또는 Aggregate)입니다:

interface Iterator<T> {
    boolean hasNext();
    T next();
}

interface Container<T> {
    Iterator<T> createIterator();
}

class BookShelf implements Container<String> {
    private String[] books;
    private int count = 0;
    
    public BookShelf(int size) {
        books = new String[size];
    }
    
    public void addBook(String book) {
        books[count++] = book;
    }
    
    public Iterator<String> createIterator() {
        return new BookIterator();
    }
    
    private class BookIterator implements Iterator<String> {
        private int index = 0;
        
        public boolean hasNext() {
            return index < count;
        }
        
        public String next() {
            return books[index++];
        }
    }
}

이터레이터는 자신의 순회 상태를 유지하므로, 여러 이터레이터가 동일한 컬렉션을 독립적으로 순회할 수 있습니다. 클라이언트는 컬렉션이 데이터를 어떻게 저장하는지 알지 못해도 이터레이터를 사용합니다:

BookShelf shelf = new BookShelf(3);
shelf.addBook("Design Patterns");
shelf.addBook("Clean Code");

Iterator<String> iterator = shelf.createIterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

Java의 내장된 IterableIterator 인터페이스는 이 정확한 패턴을 따르며, 이것이 Iterable을 구현하는 모든 클래스에서 향상된 for 루프(enhanced for-loops)를 사용할 수 있는 이유입니다. 이터레이터 패턴(Iterator Pattern)은 여러 순회 방법을 제공해야 하거나 클라이언트 코드로부터 복잡한 내부 구조를 숨겨야 할 때 필수적입니다.

challenge icon

챌린지

쉬움

Iterator 패턴을 사용하여 플레이리스트 시스템을 구축해 봅시다! 노래를 저장하고 내부 저장 방식은 노출하지 않으면서 순차적으로 탐색할 수 있는 커스텀 반복자(iterator)를 제공하는 음악 플레이리스트를 만들 것입니다. 이는 Iterator 패턴의 완벽한 활용 사례로, 사용자가 깔끔하고 통일된 인터페이스를 통해 컬렉션을 탐색할 수 있게 해줍니다.

코드는 네 개의 파일로 구성됩니다:

  • Iterator.java: 두 개의 메서드를 가진 제네릭 Iterator<T> 인터페이스를 정의합니다: 더 많은 요소가 있는지 여부를 나타내는 불리언 값을 반환하는 hasNext()와, T 타입의 다음 요소를 반환하는 next()입니다.
  • Playlist.java: 노래들을 담는 집합체(aggregate) 클래스를 생성합니다. Playlist는 생성자에 전달된 고정된 용량의 String 배열에 노래를 저장하고, 추가된 노래의 개수를 추적해야 합니다. 플레이리스트에 노래를 추가하기 위한 addSong(String song) 메서드를 포함하세요.

    Playlist에는 Iterator<String>를 반환하는 createIterator() 메서드가 필요합니다. Iterator 인터페이스를 구현하는 PlaylistIterator라는 프라이빗 내부 클래스를 만들어 이를 구현하세요. 이 내부 반복자는 자신의 인덱스 위치를 유지하며 노래 배열을 탐색하여 각 노래를 순서대로 반환합니다.

  • Song.java: 노래 제목을 감싸는 간단한 Song 클래스를 생성합니다. 제목(String)을 받는 생성자, getTitle() 메서드, 그리고 "Playing: " + title을 반환하는 toString() 메서드가 있어야 합니다.
  • Main.java: 반복자 시스템을 하나로 합칩니다! 하나의 입력을 받게 됩니다: 쉼표로 구분된 노래 제목 목록입니다 (예: Bohemian Rhapsody,Stairway to Heaven,Hotel California).

    노래 10개를 담을 수 있는 용량의 Playlist를 생성하세요. 입력을 파싱하여 각 노래 제목을 플레이리스트에 추가합니다. 그런 다음 플레이리스트에서 반복자를 얻어 모든 노래를 탐색하며, 각 노래를 Song 객체로 감싸서 출력합니다 (출력 시 Playing: [title] 형식으로 표시됩니다).

    모든 노래를 반복한 후, 새 줄에 Playlist complete!를 출력하세요.

하나의 입력을 받습니다: 쉼표로 구분된 노래 제목 문자열입니다.

예를 들어, 입력이 Yesterday,Imagine,Let It Be인 경우, 출력은 다음과 같습니다:

Playing: Yesterday
Playing: Imagine
Playing: Let It Be
Playlist complete!

Main 클래스가 내부 배열 구조에 대해 전혀 알지 못한 채 반복자의 hasNext()next() 메서드를 사용하여 플레이리스트를 탐색하는 방식에 주목하세요. 반복자는 모든 탐색 로직을 캡슐화하여 컬렉션의 내부 표현을 클라이언트 코드로부터 숨깁니다!

치트 시트

반복자 패턴(Iterator Pattern)은 컬렉션의 내부 구조를 노출하지 않고 요소들에 순차적으로 접근할 수 있는 방법을 제공하는 행동 디자인 패턴입니다.

이 패턴은 두 가지 주요 구성 요소를 사용하여 컬렉션 자체에서 순회 로직을 분리합니다:

  • Iterator 인터페이스: 요소들을 순회하기 위한 메서드들을 정의합니다.
  • Iterable/Container: 자신의 컬렉션을 위한 반복자(iterator)를 생성합니다.

기본 구조

interface Iterator<T> {
    boolean hasNext();
    T next();
}

interface Container<T> {
    Iterator<T> createIterator();
}

구현 예시

class BookShelf implements Container<String> {
    private String[] books;
    private int count = 0;
    
    public BookShelf(int size) {
        books = new String[size];
    }
    
    public void addBook(String book) {
        books[count++] = book;
    }
    
    public Iterator<String> createIterator() {
        return new BookIterator();
    }
    
    private class BookIterator implements Iterator<String> {
        private int index = 0;
        
        public boolean hasNext() {
            return index < count;
        }
        
        public String next() {
            return books[index++];
        }
    }
}

사용법

BookShelf shelf = new BookShelf(3);
shelf.addBook("Design Patterns");
shelf.addBook("Clean Code");

Iterator<String> iterator = shelf.createIterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}

반복자는 자신의 순회 상태를 스스로 유지하므로, 여러 개의 반복자가 동일한 컬렉션을 독립적으로 순회할 수 있습니다. Java의 내장 IterableIterator 인터페이스는 이 패턴을 따르며, 향상된 for 루프(enhanced for-loops)를 사용할 수 있게 해줍니다.

직접 해보기

import java.util.Scanner;

class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        String input = scanner.nextLine();
        
        // TODO: 10곡을 담을 수 있는 용량의 Playlist 생성
        
        // TODO: 입력값(쉼표로 구분됨)을 파싱하고 각 노래 제목을 playlist에 추가
        
        // TODO: createIterator()를 사용하여 playlist에서 반복자(iterator)를 가져오기
        
        // TODO: 반복자를 사용하여 모든 노래를 순회
        // 각 노래에 대해, Song 객체로 감싸고 출력
        
        // TODO: 모든 노래를 순회한 후 "Playlist complete!" 출력
    }
}
quiz icon실력 점검

이 레슨에는 짧은 퀴즈가 포함되어 있습니다. 레슨을 시작해 문제를 풀고 진행 상황을 기록하세요.

객체 지향 프로그래밍의 모든 레슨