자바 디자인 패턴 용어 사전
행위 패턴이터레이터

Iterator

내부 구조를 노출하지 않고 순회하는 패턴. 방문자는 반대로 연산을 구조 밖으로 뺀다.

컬렉션의 내부 구조를 노출하지 않고 원소를 순회하는 패턴.

interface Iterator<T> {
    boolean hasNext();
    T next();
}
// 클라이언트는 배열인지 트리인지 몰라도 된다
for (Item item : collection) { ... }   // 내부적으로 이터레이터를 쓴다

무엇을 얻나

  • 순회 방법이 여러 가지여도 같은 인터페이스로 다룬다 (전위·중위·후위 순회)
  • 컬렉션이 순회 상태를 갖지 않는다 → 동시에 여러 번 순회할 수 있다
  • 컬렉션 구현을 바꿔도 클라이언트가 안 바뀐다

fail-fast

for (String s : list) { if (cond) list.remove(s); }   // ConcurrentModificationException

컬렉션은 구조 변경 횟수를 세고, 이터레이터는 만들어질 때의 값을 기억한다

  • 다르면 즉시 예외를 던진다
  • "조용히 잘못 도는 것보다 즉시 터지는 게 낫다" 는 설계

순회 중 삭제는 이터레이터의 remove 나 removeIf 로 한다

Visitor — 연산을 구조 밖으로

interface Visitor {
    void visit(Circle c);
    void visit(Rectangle r);
}

interface Shape { void accept(Visitor v); }
class Circle implements Shape {
    public void accept(Visitor v) { v.visit(this); }   // 자기 타입을 알려 준다
}
// 새 연산을 추가할 때 Shape 들을 안 고쳐도 된다
class AreaVisitor implements Visitor { ... }
class DrawVisitor implements Visitor { ... }

Visitor의 트레이드오프

  • 연산 추가는 쉽다 — → 새 Visitor 클래스 하나

  • 타입 추가는 어렵다 — → 모든 Visitor 를 고쳐야 한다

  • 그래서 "타입은 안정적이고 연산이 자주 느는" 경우에만 값을 한다

    • 예: AST · 컴파일러 · 문서 트리

언어에 패턴 매칭이 있으면 Visitor가 대부분 불필요해진다 — 닫힌 타입 계층에 switch 하나로 같은 일을 하고, 타입 추가 시 컴파일러가 누락을 잡아 준다.

면접 함정

  • "Iterator는 for문의 다른 형태" → 내부 구조를 감추는 것이 목적이다. 인덱스가 없는 구조(트리·해시)에서 진가가 드러난다.
  • "Visitor가 더 좋은 설계" → 타입이 늘어나는 도메인에서는 최악이다.

직접 만들 일이 있나

// 커스텀 자료구조를 for-each 로 순회 가능하게 만들 때
class Tree<T> implements Iterable<T> {
    public Iterator<T> iterator() {
        return new Iterator<>() {
            public boolean hasNext() { ... }
            public T next() { ... }
        };
    }
}

for (T item : tree) { ... }     // 이제 이렇게 쓸 수 있다

지연 순회

// 전체를 만들지 않고 필요한 만큼만 생성한다
Iterator<Integer> infinite = new Iterator<>() {
    int n = 0;
    public boolean hasNext() { return true; }
    public Integer next() { return n++; }
};

무한 수열을 다룰 수 있다는 것이 이터레이터의 숨은 강점이다 — 컬렉션을 반환했다면 불가능하다.

더블 디스패치 — Visitor의 원리

visit 이 두 번의 다형 호출로 이뤄진다

  • shape.accept(visitor) — ① shape 의 실제 타입이 결정된다
    • visitor.visit(this) ② visitor 의 실제 타입 + this 의 정적 타입으로 오버로드 결정

자바는 단일 디스패치만 지원하므로 이 우회가 필요하다 패턴 매칭이 있는 언어에서는 이 복잡함이 필요 없다

함께 보면 좋은 용어

노트에서 맥락과 함께 보기 — 행위 패턴 ② — 돌고, 알리고, 되돌린다