본문 바로가기
Computer Sience/Desgin Pattern

[Design Pattern] 이터레이터(Iterator) 패턴

by 제우제우 2024. 10. 31.

이터레이터(Iterator) 패턴

집합 객체 내부 구조를 노출시키지 않고 순회 하는 방법을 제공하는 패턴

집합 객체를 순회하는 클라이언트 코드를 변경하지 않고 다양한 순회 방법을 제공할 수 있다. 

이터레이터(Iterator) 패턴 before

Post

@Getter
public class Post {
    private String title;
    private final LocalDateTime createdDateTime;
    public Post(String title) {
        this.title = title;
        this.createdDateTime = LocalDateTime.now();
    }
}

 

Board

public class Board {
    List<Post> posts = new ArrayList<>();
    public List<Post> getPosts(){
        return posts;
    }
    public void addPost(String content){
        this.posts.add(new Post(content));
    }
}

 

Client

public class Client {
    public static void main(String[] args) throws InterruptedException{
        Board board = new Board();
        board.addPost("내용1");
        Thread.sleep(100L);
        board.addPost("내용2");
        Thread.sleep(100L);
        board.addPost("내용3");
        Thread.sleep(100L);

        // 들어간 순서대로 순회하기
        List<Post> posts = board.getPosts();
        for(int i = 0; i < posts.size(); i++){
            Post post = posts.get(i);
            System.out.println(post.getTitle());
        }
        // 가장 최신 글 먼저 순회하기
        Collections.sort(posts, (o1, o2) -> o2.getCreatedDateTime().compareTo(o1.getCreatedDateTime()));
        for(int i = 0; i < posts.size(); i++){
            Post post = posts.get(i);
            System.out.println(post.getTitle());
        }
    }
}
// 출력 
내용1
내용2
내용3
내용3
내용2
내용1

 

현재 클라이언트 코드는 Board 클래스의 posts 구현체에 따라서 변경된다. 

ex) ArrayList → Set

 

이터레이터 패턴을 적용해서 컬렉션의 내부 구조가 바뀌어도 클라이언트의 코드 변경 없이 순회 가능하도록 만들어 보자.

이터레이터(Iterator) 패턴 after

이미 ArrayList는 Iterator를 제공한다. 

public class Board {
    List<Post> posts = new ArrayList<>();
    public void addPost(String content){
        this.posts.add(new Post(content));
    }
    public Iterator<Post> getDefaultIterator(){
        return this.posts.iterator();
    }
}

public class Client {
    public static void main(String[] args) throws InterruptedException{
        // 생략 ...
        // 들어간 순서대로 조회하기
        Iterator<Post> iterator = board.getDefaultIterator();
        while (iterator.hasNext()){
            Post post = iterator.next();
            System.out.println(post.getTitle());
        }
    }
}

이렇게 하면 나중에 Set 같은 다른 컬렉션 구조로 바뀌어도 클라이언트 코드 변경 없이 순회가 가능하다. 

이터레이터 패턴 적용 

RecentPostIterator - ConcreteIterator 

public class RecentPostIterator implements Iterator<Post> {
    private Iterator<Post> internalIterator;
    public RecentPostIterator(List <Post> posts) {
        posts.sort((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();
    }
}

최신글 순서대로 정렬해서 만들어지는 이터레이터 

 

Board

public class Board {
    List<Post> posts = new ArrayList<>();
    public void addPost(String content){
        this.posts.add(new Post(content));
    }
    public Iterator<Post> getDefaultIterator(){
        return this.posts.iterator();
    }
    public Iterator<Post> getRecentPostIterator(){
        return new RecentPostIterator(this.posts);
    }
}

getRecentPostIterator() API가 추가 

 

클라이언트 

public class Client {
    public static void main(String[] args) throws InterruptedException{
        Board board = new Board();
        board.addPost("내용1");
        Thread.sleep(100L);
        board.addPost("내용2");
        Thread.sleep(100L);
        board.addPost("내용3");
        Thread.sleep(100L);

        // 들어간 순서대로 조회하기
        Iterator<Post> iterator = board.getDefaultIterator();
        while (iterator.hasNext()){
            Post post = iterator.next();
            System.out.println(post.getTitle());
        }

        // 최신 글 순서대로 조회
        Iterator<Post> recentIterator = board.getRecentPostIterator();
        while (recentIterator.hasNext()){
            Post post = recentIterator.next();
            System.out.println(post.getTitle());
        }
    }
}

이제 클라이언트는 내부의 구체적인 구조를 몰라도 목적에 맞게 순회를 하는 이터레이터를 얻을 수 있다. 

즉 내부의 구조가 바뀌어도 클라이언트 코드의 변경이 없다. 

물론 ConcreteIterator인 RecentPostIterator는 변경된다. 

이터레이터(Iterator) 패턴 정리

집합 객체 내부 구조를 노출시키지 않고 순회 하는 방법을 제공하는 패턴

 

장점

집합 객체가 가지고 있는 객체들에 손쉽게 접근할 수 있다

일관된 인터페이스를 사용해 여러 형태의 집합 구조를 순회할 수 있다

 

단점

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

이터레이터(Iterator) 패턴 적용 사례

자바

java.util.Enumeration과 java.util.Iterator

Java StAX (Streaming API for XML)의 Iterator 기반 API

XmlEventReader, XmlEventWriter

 

스프링

CompositeIterator

참고자료

백기선님 디자인 패턴 강의

 

코딩으로 학습하는 GoF의 디자인 패턴 강의 | 백기선 - 인프런

백기선 | 디자인 패턴을 알고 있다면 스프링 뿐 아니라 여러 다양한 기술 및 프로그래밍 언어도 보다 쉽게 학습할 수 있습니다. 또한, 보다 유연하고 재사용성이 뛰어난 객체 지향 소프트웨어를

www.inflearn.com