11. 반복자 패턴

AlmondGood·2022년 7월 25일
0

디자인패턴

목록 보기
12/16

반복자 패턴(Iterator Pattern)

우리는 다양한 자료구조 형태를 반복문을 사용해서 읽고, 쓰고, 수정해왔습니다. 반복문을 사용하기 위해서 인덱스 변수를 만들고, 배열이나 리스트의 인덱스의 값을 가져왔죠.

하지만 객체 배열이나 리스트가 있다면 어떨까요?
이것도 인덱스를 활용할 수 있겠지만, 객체 내의 값에 직접 접근해야 하는 일이 생길 수 있습니다.
의존성이 높아지는 것이죠.

반복자 패턴 : 컬렉션의 구현 방법을 노출하지 않으면서 집합체 내의 모든 항목에 접근하는 방법을 제공하는 패턴

즉, 자료구조에 상관없이 반복자를 이용해 모든 항목에 접근할 수 있다는 말입니다.



반복자 패턴 구현

성적을 나열해서 출력하는 클래스를 만든다고 가정해봅시다.

// 반복자 인터페이스입니다.
interface Iterator {
    boolean hasNext();
    Score next();
}

반복자는 각 항목에 접근해서 값을 불러옵니다.

// 실제로 반복해줄 구체 반복자입니다.
class ScoreIterator implements Iterator {
	// 성적표로부터 성적 배열을 받습니다.
    private Score[] score;
    private int index = 0;

    ScoreIterator(Score[] score) {
        this.score = score;
    }

	// 다음 인덱스에 값이 있는지 확인합니다.
    @Override
    public boolean hasNext() {
        return this.index < score.length && score[this.index] != null;
    }

	// 현재 인덱스의 값을 반환하고 인덱스에 1을 더합니다.
    @Override
    public Score next() {
        return score[this.index++];
    }
}

정보를 가져올 클래스입니다.
이 클래스에서 정보를 받고, 성적표 클래스로 값을 넘겨줍니다.

class Score{
    private int no;
    private int math;
    private int java;

    Score(int no, int math, int java) {
        this.no = no;
        this.math = math;
        this.java = java;
    }

    public int getNo() {
        return this.no;
    }
    public int getMath() {
        return this.math;
    }
    public int getJava() {
        return this.java;
    }

}

성적표 클래스입니다.
addScore() 메소드를 통해 Score배열에 성적을 저장합니다.

class ScoreSheet {
    private Score[] scores;
    private int index = 0;

    ScoreSheet(int size) {
        this.scores = new Score[size];
    }

    void addScore(Score score) {
        this.scores[this.index] = score;
        this.index++;
    }

	// 반복자 객체를 만들고, 반복자에 성적 배열을 넘겨줍니다.
    public Iterator createIter() {
        return new ScoreIterator(scores);
    }
}

메인 함수입니다.

public static void main(String[] args) {
	Score score1 = new Score(1, 90, 100);
    Score score2 = new Score(2, 100, 50);
    Score score3 = new Score(3, 60, 80);

	ScoreSheet scoreSheet = new ScoreSheet(3);
    scoreSheet.addScore(score1);
    scoreSheet.addScore(score2);
    scoreSheet.addScore(score3);

	Iterator scoreIterator = scoreSheet.createIter();

      
 	// 따로 출력만을 전담하는 클래스를 만드는 게 좋습니다.
    while (scoreIterator.hasNext()) {
    	Score score = scoreIterator.next();
        System.out.printf("번호 : %d / 수학 : %d / 자바 : %d\n", score.getNo(), score.getMath(), score.getJava());
    }
    // 번호 : 1 / 수학 : 90 / 자바 : 100
	// 번호 : 2 / 수학 : 100 / 자바 : 50
	// 번호 : 3 / 수학 : 60 / 자바 : 80
}

반복자는 Score 클래스가 어떤 구조인지 몰라도 됩니다.
어차피 반복자의 역할은 Score 의 객체를 반환하는 것이 끝이거든요.

이렇게하면 각 클래스들은 서로의 구조를 알지 않고도 반복하고, 값에 접근할 수 있게 됩니다.




반복자 패턴의 장단점

장점

  • 크기가 큰 순회 알고리즘을 별도의 클래스로 추출하여 클라이언트 코드와 컬렉션을 정리함으로 인해 단일 책임 원칙을 만족할 수 있습니다.
  • 새로운 유형의 컬렉션 및 반복자를 구현하고 기존의 코드는 수정하지 않음으로써 개방/폐쇄 원칙을 만족할 수 있습니다.
  • 각각의 반복자 객체는 그들만의 고유한 반복 상태가 포함되어 있기 때문에 동일한 컬렉션을 병렬로 반복할 수 있습니다.
  • 위와 같은 이유로 반복을 지연시킬 수 있고 필요할 때 계속할 수 있습니다.

단점

  • 앱이 간단한 컬렉션 만으로도 동작하는 경우 패턴을 적용하는 것이 오히려 지나칠 수 있습니다.
  • 반복자를 사용하는 것은 일부 특수 컬렉션의 요소를 직접 탐색하는 것 보다 덜 효율적일 수 있습니다.

https://keencho.github.io/posts/iterator-pattern/




자바의 Iterable / Iterator 인터페이스

Iterable 인터페이스는 말 그대로 반복할 수 있게 만들어 주는 인터페이스입니다.
Iterable 인터페이스 안에 Iterator를 반환하는 Iterator() 메소드가 있어 하위 클래스들이
강제로 반복자를 만들도록 했죠.

따라서 이걸 상속받는 컬렉션들, 즉 List, Queue, Stack 류 들의 자료구조에서 반복자를 사용할 수 있게 됩니다.

그리고 Iterator 인터페이스는 각 동작이 추상 메소드로 선언되어 있습니다.

hasNext() , next() 메소드가 선언되어 위의 자료구조들에서 구현되었기 때문에
어떤 자료구조든 상관없이 자유롭게 사용할 수 있게 되었습니다.

profile
Zero-Base to Solid-Base

0개의 댓글