본문 바로가기

Develop/Design Pattern

[Design Pattern] 반복자 (Iterator) 패턴

1. 개요

  • 반복자 디자인 패턴 (Iterator Design Pattern)은 컬렉션(Collection) 객체의 내부 구조에 상관없이 요소(element)들을 순차적으로 접근하고 싶을 때 사용되는 패턴이다.

2. 조건

  • 요소들을 반복적으로 탐색하고자 할 때
  • 컬렉션 객체의 내부 구조에 대한 세부 정보를 노출시키지 않고자 할 때

3. 다이어그램

iterator-default

3.1. 설명

  • Iterator
    • 요소에 순차적 접근을 허용하는 인터페이스를 정의한다.
    • 다음 요소를 반환하는 next() 메소드가 있다.
    • 다음 요소의 존재 여부를 확인하는 hasNext() 메소드가 있다.
  • ConcreteIterator
    • Iterator 인터페이스의 구현 클래스이다.
    • 컬렉션 객체의 내부 구조에 맞추어 요소들을 반복하고 관리한다.
  • Aggregate
    • Iterator를 생성하는 인터페이스를 정의한다.
    • 주로 컬렉션 객체를 표현하며, iterator() 메소드를 통해 해당 컬렉션 객체에 대한 Iterator를 생성한다.
  • ConcreteAggregate
    • Aggregate 인터페이스의 구현 클래스이다.
    • 실제 요소들을 저장하고, iterator() 메소드를 구현한다.

4. 구현

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

public class ConcreteIterator<T> implements Iterator<T> {
  private List<T> elements;
  private int position;

  public ConcreteIterator(List<T> elements) {
    this.elements = elements;
    this.position = 0;
  }

  @Override
  public boolean hasNext() { 
    return position < elements.size(); 
  }

  @Override
  public T next() {
    if (hasNext()) return elements.get(position++);
    throw new IndexOutOfBoundsException("No more elements");
  }
}
public interface Aggregate<T> { Iterator<T> iterator(); }

public class ConcreteAggregate<T> implements Aggregate<T> {
  private List<T> elements;

  public ConcreteAggregate() {
    this.elements = new ArrayList<>();
  }

  public void add(T element) {
    elements.add(element);
  }

  @Override
  public Iterator<T> iterator() {
    return new ConcreteIterator<>(elements);
  }
}
public class Client {
  public static void main(String[] args) {
    ConcreteAggregate<String> aggregate = new ConcreteAggregate<>();
    aggregate.add("Element 1");
    aggregate.add("Element 2");
    aggregate.add("Element 3");

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

출력결과

Element 1
Element 2
Element 3

5. 예시

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

public class StudentIterator implements Iterator<Student> {
  private List<Student> elements;
  private int position;

  public StudentIterator(List<Student> elements) {
    this.elements = elements;
    this.position = 0;
  }

  @Override
  public boolean hasNext() { 
    return position < elements.size(); 
  }

  @Override
  public Student next() {
    if (hasNext()) return elements.get(position++);
    throw new IndexOutOfBoundsException("No more elements");
  }
}
public interface Aggregate { 
  Iterator iterator();
}

public class StudentArray implements Aggregate { 
  private List<Student> students;

  public StudentArray() {
    students = new ArrayList<>();
  }

  public void add(Student student) {
    students.add(student);
  }

  @Override
  public StudentIterator iterator() {
    return new StudentIterator(students);
  }
}
public class Client {
  public static void main(String[] args) {
    StudentArray attendance = new StudentArray();
    attendance.add(new Student(0, "James"));
    attendance.add(new Student(1, "Jane"));
    attendance.add(new Student(2, "John"));

    StudentIterator iterator = attendance.iterator();

    while (iterator.hasNext()) {
      Student student = iterator.next();
      System.out.println(student);
    }
  }
}

출력결과

id: 0, name: James
id: 1, name: Jane
id: 2, name: John

6. 장단점

6.1. 장점

  • 클라이언트와 컬렉션 객체를 분리하여 구현할 수 있다.
  • 서로 다른 종류의 컬렉션 객체를 동일한 방식으로 접근할 수 있다.
  • 컬렉션 객체의 내부 구조를 노출하지 않아 데이터의 무결성과 보안을 보장할 수 있다.

6.2. 단점

  • 컬렉션 순회 중 요소를 수정할 수 없다.
  • 코드의 복잡성이 증가한다.
  • 요소들의 역순이나 임의의 순서의 접근이 제한된다.

관련 포스팅

728x90