디자인 패턴 톺아보기 - Iterator Pattern

Updated:

1. 반복자 패턴(Iterator Pattern) 이란?

GOF 에서 말하는 반복자 패턴의 목적은 아래와 같습니다.

Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.

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

1.1. 구조

Sample / Sequence Diagram

image

  • Iterator
    • 원소를 접근하고 순회하는 데 필요한 인터페이스를 제공합니다.
  • ConcreteIterator
    • Iterator에 정의된 인터페이스를 구현하는 클래스로, 순회 과정 중 집합 객체 내에서 현재 위치를 기억합니다.
  • Aggregate
    • Iterator 객체를 생성하는 인터페이스를 제공합니다.
  • ConcreteAggregate
    • 해당하는 ConcreteIterator의 인스턴스를 반환하는 Iterator 생성 인터페이스를 구현합니다.

1.2. 사용 방법

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

1.3. 장/단점

  • Advantages (+)
    • Enables simultaneous traversals.
    • Simplifies the aggregate interface.
    • Allows changing the traversal dynamically at run-time.

1.4. 고려사항

  • Consider the left design (problem):
    • Aggregate responsible for access and traversal.
    • One traversal.
  • Consider the right design (solution):
    • Iterator responsible for access and traversal.
    • Multiple traversals.

2. 반복자 패턴(Iterator Pattern) 사용예시

반복자 패턴은 다음 경우에 사용합니다.

  • 객체 내부 표현방식을 모르고도 집합 객체의 각 원소들에 접근하고 싶을 때
  • 집합 객체를 순회하는 다양한 방법을 지원하고 싶을 때
  • 서로 다른 집합 객체ㅔ 구조에 대해서도 동일한 방법으로 순회하고 싶을 때

2.1. GOF 패턴

2.1.1. Iterator

interface Iterator<E> { 
	E next();
	boolean hasNext();
} 

2.1.2. Aggregate

interface Aggregate<E> { 
	// ...
	Iterator<E> createIterator();
	boolean add(E element);
} 

2.1.3. ConcreteAggregate / ConcreteIterator

class Aggregate1<E> implements Aggregate<E> {  // E = Type parameter
	// Hiding the representation.
	private Object[] elementData; // represented as object array
	private int idx = 0;
	private int size;

	//
	public Aggregate1(int size) { 
		if (size < 0) {
			throw new IllegalArgumentException("size: " + size);
		}
		this.size = size;
		elementData = new Object[size];
	} 
	
	public boolean add(E element) { 
		if (idx < size) { 
			elementData[idx++] = element;
			return true;
		}  else {
			return false;
		}
	} 
	
	public int getSize() { 
		return size;
	} 
	
	// Factory method for instantiating Iterator1.
	public Iterator<E> createIterator() { 
		return new Iterator1<E>();
	} 
	
	//
	// Implementing Iterator1 as inner class.
	//
	private class Iterator1<E> implements Iterator<E> { 
		// Holds the current position in the traversal.
		private int cursor = 0; // index of next element to return
		//
		public boolean hasNext() { 
			return cursor < size;
		} 
	
		public E next() {  // E = Type of element returned by this method
			if (cursor >= size) {
				throw new NoSuchElementException();
			}
	
			return (E) elementData[cursor++]; // cast from Object to E
		} 
	} 
} 

2.1.4. Main

public class Main{

	// Running the Client class as application.
	public static void main(String[] args) {
		// Setting up an aggregate.
		Aggregate<String> aggregate = new Aggregate1<String>(3);
		aggregate.add(" ElementA ");
		aggregate.add(" ElementB ");
		aggregate.add(" ElementC ");
		//
		// Creating an iterator.
		Iterator<String> iterator = aggregate.createIterator();
		//
		System.out.println("Traversing the aggregate front-to-back:");
		while (iterator.hasNext()) { 
			System.out.println(iterator.next());
		} 
	}
}

결과는 아래와 같습니다.

Traversing the aggregate front-to-back:
 ElementA 
 ElementB 
 ElementC 

참고 자료

Leave a comment