반응형

의도

  • 집합 객체 내부 구조를 노출시키지 않고 순회 하는 방법을 제공하는 패턴
  • 내부 표현부를 노출하지 않고 어떤 집합 객체에 속한 원소들을 순차적으로 접근할 수 있는 방법을 제공한다.

 

장점

  • 집합 객체를 순회하는 클라이언트 코드를 변경하지 않고 다양한 순회 방법을 제공할 수 있다.
  • 집합 객체가 가지고 있는 객체들에 손쉽게 접근할 수 있다
  • 일관된 인터페이스를 사용해 여러 형태의 집합 구조를 순회할 수 있다.

 

단점

  • 클래스가 늘어나고 복잡도가 증가한다.

 

알려진 사용 예

  • 자바
    • java.util.Enumeration
      • 과거의 클래스. Iterator로 기능이 대체 되었다.
    • java.util.Iterator
      • remove
        • 동시성 문제가 없는 Collection에서 지원되고 문제가 생길 가능성이 있는 곳에서는 지원하지 않는 기능
      • forEachRemaining
        • Consumer 함수형 인터페이스 사용
        • 순회 결과를 하나씩 받아서 처리 가능
    • Java StAX(Streaming API for XML)의 Iterator 기반 API
      • XML을 읽고 쓰기가 가능하다.
        • 콘솔 기반의 API, 이터레이터 기반의 API를 제공한다.
        • 콘솔 기반의 API
          • 하나의 인스턴스가 지나가면서 내용이 바뀐다.(상태변경)
          • 메모리는 이터레이터 기반보다 효율적이다. 그러나 재사용, 변경 측면에서는 좋지 않다.
          • 유연성 있게 되려면 상태가 바뀌기 보다는 하나 하나 제각각의 XML을 표현하는 불변객체를 사용하는 것이 좋다.
        • 이터레이터 기반의 API
          • XmlEventReader, XmlEventWriter
          • 이벤트가 XML 엘리먼트당 하나씩 지나간다. 인스턴스가 엘리먼트당 만들어진다. 이를 이용한다.
          • 하나 하나 제각각의 XML을 표현하는 불변객체이기 떄문에 유연한 처리가 가능하다.
          • 일반적으로 유연한 이터레이터 기반의 API를 사용하는 것을 권장한다.
  • 스프링
    • CompositeIterator
      • Iterator 여러 개를 조합해서 사용한다.

 

활용성

  • 집합 객체 내부의 표현 방식이 변경 가능성이 있을 때
    • 클라이언트가 집합 객체 내부 표현 방식을 알고 있다면, 표현 방식(List에서 Set으로 등)이 달라지면 클라이언트 코드도 변경되어야 하는 문제가 생긴다.
  • 객체 내부 표현 방식을 모르고도 집합 객체의 각 원소들에 접근하고 싶을 때
  • 집합 객체를 순회하는 다양한 방법을 지원하고 싶을 때
  • 서로 다른 집합 객체 구조에 대해서도 동일한 방법으로 순회하고 싶을 때

 

결과

  • 집합 객체의 다양한 순회 방법을 제공한다.
    • 구조가 복잡한 집합 객체는 다양한 방법으로 순회할 수 있다.
    • 예) 컴파일러에서 코드 생성 및 의미 점검(semantic checking)을 진행하려면 파스 트리를 순회해야 한다.
      • 코드를 생성하기 위해 트리를 순회할 때 중위 순회 방식이나 전위 순회 방식을 사용할 수 있다.
      • 이 때, Iterator는 순회 알고리즘을 바꿀 수 있도록 한다.
    • 단, 순회 방법이 추가되는 경우 집합 객체의 코드는 변경될 수 있다. 해당 순회 방법을 지원하기 위해 코드가 추가되어야 한다.
  • Iterator는 Aggregate 클래스의 인터페이스를 단순화한다.
    • Iterator의 순회 인터페이스는 Aggregate 클래스에 정의한 자신과 비슷한 인터페이스들을 없애서 Aggregate 인터페이스를 단순화할 수 있다.
  • 집합 객체에 따라 하나 이상의 순회 방법이 제공될 수 있다.
    • 각 Iterator마다 자신의 순회 상태가 있으므로 하나의 집합 객체를 한번에 여러 번 순회시킬 수 있다.

 

협력 방법

  • ConcreteIterator는 집합 객체 내 현재 객체를 계속 추적하고 다음번 방문할 객체를 결정한다.

 

구조

 

실제 구현 구조

 

소스코드

//ConcreteAggregate
public class Board {

    List<Post> posts = new ArrayList<>();

    public List<Post> getPosts() {
        return posts;
    }

    public void setPosts(List<Post> posts) {
        this.posts = posts;
    }

    public void addPost(String content) {
        this.posts.add(new Post(content));
    }

    public Iterator<Post> iterator() {
        return new RecentPostIterator(this.getPosts());
    }
}
//ConcreteIterator
public class RecentPostIterator implements Iterator<Post> {

    private Iterator<Post> internalIterator;

    public RecentPostIterator(List<Post> posts) {
        Collections.sort(posts, (p1, p2) -> p2.getCreatedDateTime().compareTo(p1.getCreatedDateTime()));
        this.internalIterator = posts.iterator();
    }

    @Override
    public boolean hasNext() {
        return this.internalIterator.hasNext();
    }

    @Override
    public Post next() {
        return this.internalIterator.next();
    }
}
public class Post {

    private String title;

    private LocalDateTime createdDateTime;

    public Post(String title) {
        this.title = title;
        this.createdDateTime = LocalDateTime.now();
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public LocalDateTime getCreatedDateTime() {
        return createdDateTime;
    }

    public void setCreatedDateTime(LocalDateTime createdDateTime) {
        this.createdDateTime = createdDateTime;
    }
}
public class Client {

    public static void main(String[] args) {
        Board board = new Board();
        board.addPost("포스트1");
        board.addPost("포스트2");
        board.addPost("포스트3");

        //가장 최신 글 먼저 순회하기
        Iterator<Post> postIterator = board.iterator();
        while (postIterator.hasNext()) {
            Post post = postIterator.next();
            System.out.println(post.getTitle());
        }
    }
}

 

관련 패턴

  • 반복자 패턴은 복합체 패턴과 같이 재귀적 구조가 있을 떄 자주 사용한다.
  • 다양한 반복자를 사용해서 적당한 Iterator 서브클래스를 얻으려면 팩토리 메서드 패턴을 사용할 수 있다.
  • 반복자 자신이 반복한 결과를 저장하기 위해서 메멘토 패턴도 반복자 패턴과 함께 자주 사용된다.

 


[참고자료]

리처드 헬름, 랄프 존슨, 존 블리시디스, 『GoF의 디자인 패턴 : 재사용성을 지닌 객체지향 소프트웨어의 핵심요소』, 김정아 번역, 프로텍미디어(2015)

http://www.cs.unc.edu/~stotts/GOF/hires/pat5dfso.htm

코딩으로 학습하는 GoF의 디자인 패턴, 백기선

반응형

+ Recent posts