이터레이터 패턴

이승주·2024년 7월 10일
1

행위 패턴 (Behavioral Pattern)

행위 패턴은 클래스나 객체들이 서로 상호작용하는 방법이나 책임 분배 방법을 정의하는 패턴으로 총 11개의 패턴이 있다.

행위 패턴은 하나의 객체로 수행할 수 없는 작업을 여러 객체로 분배하면서 결합도를 최소화 할 수 있도록 도와준다.

결론적으로 행위 패턴은 코드의 유지보수성과 가독성을 향상시키며 재사용성을 증가시킨다.


이터레이터 패턴

11가지의 행위 패턴 중 오늘은 이터레이터 패턴에 대해서 정리해보려고한다.

이터레이터 패턴이란?

이터레이턴 패턴이란 이터레이터를 사용하여 컬렉션 요소들에 접근하는 디자인 패턴이다.
이를 통해 순회할 수 있는 여러가지 자료형의 구조와는 상관 없이 이터레이터라는 하나의 인터페이스로 순회가능하다.

쉽게 말해 이터레이터를 하나 만들어 두면 어떤 구조의 컬렉션이든 순회할 수 있다.

  • 컬렉션: 객체들을 묶어 자료의 구조를 취하는 데이터 집합(ex 리스트, 트리 , 그래프)

for문을 통해 단순 순회하면 되지 않는가?

리스트나 보통배열의 경우 연속적인 데이터 집합이기 때문에 간단한 for문으로 순회할 수 있다. 하지만 해시, 트리와 같은 컬렉션은 데이터 저장순서가 정해지지 않고 적재되기 때문에, 각 요소들을 어떤 기준으로 접근해야할지 애매해진다.

이처럼 복잡하게 얽혀있는 자료 컬렉션들을 순회하는 알고리즘 전략을 정의하는 것을 이터레이터 패턴이라고 한다.

컬렉션 객체 안에 들어있는 모든 원소들에 대한 접근 방식이 공통화 되어 있다면 어떤 종류의 컬렉션에서도 이터레이터만 뽑아내면 여러 전략으로 순회가 가능해 보다 다형(多形) 적인 코드를 설계할 수 있게 된다

이밖에도 이터레이터 패턴은 별도의 이터레이터 객체를 반환 받아 이를 이용해 순회하기 때문에, 집합체의 내부 구조를 노출하지 않고 순회 할 수 있다는 장점도 있다.


이터레이터 패턴 구조

이터레이터 패턴의 4가지 구성요소는 아래와 같다.

Aggregate (인터페이스) : ConcreateIterator 객체를 반환하는 인터페이스를 제공한다.

  • iterator() : ConcreateIterator 객체를 만드는 팩토리 메서드
  • 쉽게 컬렉션이라고 볼 수 있음

ConcreateAggregate (클래스) : 여러 요소들이 이루어져 있는 데이터 집합체
Iterator (인터페이스) : 집합체 내의 요소들을 순서대로 검색하기 위한 인터페이스를 제공한다.

  • hasNext() : 순회할 다음 요소가 있는지 확인 (true / false)
  • next() : 요소를 반환하고 다음 요소를 반환할 준비를 하기 위해 커서를 이동시킴

ConcreateIterator (클래스) : 반복자 객체

  • ConcreateAggregate가 구현한 메서드로부터 생성되며, ConcreateAggregate 의 컬렉션을 참조하여 순회한다.
  • 어떤 전략으로 순회할지에 대한 로직을 구체화 한다.

코드구현

// 집합체 객체 (컬렉션)
interface Aggregate {
    Iterator iterator();
}

class ConcreteAggregate implements Aggregate {
    Object[] arr; // 데이터 집합 (컬렉션)
    int index = 0;

    public ConcreteAggregate(int size) {
        this.arr = new Object[size];
    }

    public void add(Object o) {
        if(index < arr.length) {
            arr[index] = o;
            index++;
        }
    }

    // 내부 컬렉션을 인자로 넣어 이터레이터 구현체를 클라이언트에 반환
    @Override
    public Iterator iterator() {
        return new ConcreteIterator(arr);
    }
}
// 반복체 객체
interface Iterator {
    boolean hasNext();
    Object next();
}

class ConcreteIterator implements Iterator {
    Object[] arr;
    private int nextIndex = 0; // 커서 (for문의 i 변수 역할)

    // 생성자로 순회할 컬렉션을 받아 필드에 참조 시킴
    public ConcreteIterator(Object[] arr) {
        this.arr = arr;
    }

    // 순회할 다음 요소가 있는지 true / false
    @Override
    public boolean hasNext() {
        return nextIndex < arr.length;
    }

    // 다음 요소를 반환하고 커서를 증가시켜 다음 요소를 바라보도록 한다.
    @Override
    public Object next() {
        return arr[nextIndex++];
    }
}

사용 흐름

public static void main(String[] args) {
    // 1. 집합체 생성
    ConcreteAggregate aggregate = new ConcreteAggregate(5);
    aggregate.add(1);
    aggregate.add(2);
    aggregate.add(3);
    aggregate.add(4);
    aggregate.add(5);

    // 2. 집합체에서 이터레이터 객체 반환
    Iterator iter = aggregate.iterator();

    // 3. 이터레이터 내부 커서를 통해 순회
    while(iter.hasNext()) {
        System.out.printf("%s → ", iter.next());
    }
}


이터레이터 패턴 특징

패턴 사용 시기

  • 컬렉션에 상관없이 객체 접근 순회 방식을 통일하고자 할 때
  • 컬렉션을 순회하는 다양한 방법을 지원하고 싶을 때
  • 컬렉션의 복잡한 내부 구조를 클라이언트로 부터 숨기고 싶은 경우 (편의 + 보안)
  • 데이터 저장 컬렉션 종류가 변경 가능성이 있을 때
  • 클라이언트가 집합 객체 내부 표현 방식을 알고 있다면, 표현 방식이 달라지면 클라이언트 코드도 변경되어야 하는 문제가 생긴다.

패턴 장점

  • 일관된 이터레이터 인터페이스를 사용해 여러 형태의 컬렉션에 대해 동일한 순회 방법을 제공한다.
  • 컬렉션의 내부 구조 및 순회 방식을 알지 않아도 된다.
  • 집합체의 구현과 접근하는 처리 부분을 반복자 객체로 분리해 결합도Visit Website를 줄 일 수 있다.
  • Client에서 iterator로 접근하기 때문에 ConcreteAggregate 내에 수정 사항이 생겨도 iterator에 문제가 없다면 문제가 발생하지 않는다.
  • 순회 알고리즘을 별도의 반복자 객체에 추출하여 각 클래스의 책임을 분리하여 단일 - 책임 원칙(SRP)Visit Website를 준수한다.
  • 데이터 저장 컬렉션 종류가 변경되어도 클라이언트 구현 코드는 손상되지 않아 수정에는 닫혀 있어 개방 폐쇄 원칙(OCP)Visit Website를 준수한다.

패턴 단점

  • 클래스가 늘어나고 복잡도가 증가한다.
  • 만일 앱이 간단한 컬렉션에서만 작동하는 경우 패턴을 적용하는 것은 복잡도만 증가할 수 있다.
  • 이터레이터 객체를 만드는 것이 유용한 상황인지 판단할 필요가 있다.
  • 구현 방법에 따라 캡슐화를 위배할 수 있다.

참고자료

InpaDev-반복자Iterator-패턴-완벽-마스터하기

GIS DEVELOPER-GoF의 Design Pattern - 2. Iterator(유튜브)

호락호락한순무-[Dev] 디자인 패턴 중 행위 패턴이란 무엇이지 알아보자

profile
반복되는 실수를 기록을 통해 줄여가보자!

0개의 댓글

관련 채용 정보