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() 메서드를 사용하여 플레이리스트를 탐색하는 방식에 주목하세요. 반복자는 모든 탐색 로직을 캡슐화하여 컬렉션의 내부 표현을 클라이언트 코드로부터 숨깁니다!

직접 해보기

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실력 점검

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

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