본문 바로가기

Dev.../패턴자료

The Iterator Pattern

The Iterator Pattern

    Iterator 는 디자인 패턴에서 가장 간단하고 가장 빈번하게 사용되는 패턴들 중의 하나이다. Iterator 패턴은 데이터의 내부적인 표현을 자세하게 아는 것 없이 표준적인 인터페이스를 이용한 데이터의 리스트나 컬렉션을 통하여 이동하는 것을 허용한다. 게다가 어떤 특별한 프로세싱과 데이터 컬렉션의 특정한 원소를 반환을 하는 특별한 Iterator를 정의할 수 있다.

Motivation

    Iterator는  어떻게 Iterator 가 데이터를 이동시키는지를 드러내지 않고 데이터 원소들의 집합을 통하여 이동하는 정의된 Iterator는 인터페이스(interface)이기 때문에 반환되는 데이터를 위한 편리한 어떤 방법이든 구현할 수 있다.
public interface Iterator {	public Object First();	public Object Next();	public boolean isDone();	public Object CurrentItem();}
여기서 리스트의 최상위로 이동할 수 있고 리스트를 통하여 이동하고, 원소들이 더 있는지를 알아내고, 현재 리스트의 항목을 찾을 수 있다. 이 인터페이스는 구현하기가 쉽고 확실한 장점들을 가지고 있지만, 자바에서 Iterator의 선택은 Enumeration 타입으로 만들어져 있다 (자바 초창기에는 그랬지만, 현재는 Iterator 가 인터페이스로 존재한다).
public interface Enumeration {	public boolean hasMoreElements();	public Object nextElement();}
    리스트의 상위로 이동하는 메소드를 가지고 있지 않은 것이 첫째로 제한적인 것처럼 보여지지만 자바에서는 그것은 심각한 문제가 아니다. 왜냐면 리스트를 통하여 이동하고자 할 때마다 Enumeration의 새로운 인스턴스를 얻는 것이 통상적이기 때문이다. 자바 Enumeration의 한가지 단점은 자바 언어의 강한 형 변환이다. 이것은 hasMoreELements() 메소드가 반환된 Object 타입을 실제적인 타입으로 캐스팅하는 귀찮은 요구 없이 컬렉션에서 데이터의 실제적인 타입의 객체를 반환하는 것을 방해한다. 그러므로 Iterator 나 Enumeration 인터페이스는 다형성을 의도하는 것이다.

Enumerations in Java

Enumeration 타입은 Vector 와 Hashtable 클래스들로 만드어져 있다. 두 클래스는 클래스의 데이터의 Enumeration을 반환하는 elements 메소드를 포함하고 있다.

public Enumeration elements();

    이 elements() 메소드는 실제로 Enumeration 클래스의 인스턴스를 생성하는 Factory 메소드의 일종이다.

    그리고 나서 다음 코드를 가지고 리스트를 통해 이동시킨다.
Enumeration e = vector.elements();while (e.hasMoreElements()) {	String name = (String)e.nextElement();	System.out.println(name);}
    추가적으로 HashTable은 테이블에서 각 원소에 키들의 enumeration을 반환하는 keys 메소드를 가지고 있다.

public Enumeration keys ();

    이것이 자바에서 Enumeration을 구현하기 위한 발탁된 스타일이고 같은 데이터의 enumeration들을 동시에 동적으로 얼마든지 가질 수 있는 이점이 있다.

Filtered Iterators

하나의 컬렉션을 통하여 정확히 정의된 이동 메소드를 갖는 것은 도움이 되고, 반환되기 전의 데이터에 대한 몇 가지 계산을 수행하여 걸러진 Enumerations 을 정의할 수 있다. 예를 들어 특별한 방법으로 정렬된 데이터를 반환할 수 있거나  특정 영역에 매치되는 단지 몇 개의 객체들을 반환할 수 있다. 그래서, 걸려진 enumerations 들을 위한 매우 유사한 인터페이스들을 많이 같이 가지고 있는것 보다 같은 메소드를 갖는 이러한 enumerations 의 각각의 것을 가지고서  enumeration 의 각 타입을 반환하는 메소들 간단히 제공한다.

Sample Code

Interpreter 단원에서 설명했던 수영선수들, 클럽들, 시간들의 목록을 재사용하고 KidData 클래스에  몇 가지 기능을 추가해 보자. 
public class KidData {	Vector kids;	//------------------------------------------   	public KidData(String filename) {		kids = new Vector();		InputFile f = new InputFile(filename);		String s = f.readLine();				while(s != null) {			if(s.trim().length() > 0) {				Kid k = new Kid(s);				kids.addElement(k);			}					s = f.readLine();		}	}		//--------------------------------	public Enumeration elements() {		//return an enumeration of the kids		return kids.elements();	}	
    컬렉션에서 모든 Kids의 enumeration을 얻기 위해, 우리는 간단히 Vector 그 자체의 enumeration을 반환한다.

The Filtered Enumeration

    그러나 우리가 어떤 클럽에 속해 있는 그러한 아이들에 대해서만 enumerate 하기를 원했었다고 가정해 보자. 이것은 KidData 클래스에서 데이터에 접근하는 특별한 Enumeration 클래스를 필요로 한다. 그런 접근을 하도록 정의된 elements() 메소드 때문에 이것은 간단하다. 그리고 나서 우리는 특정의 클럽에 속한 아이들을 반환하는 Enumeration 을 작성이 필요하다.
public class kidClub implements Enumeration {	String clubMask;     //name of club	Kid kid;             //next kid to return	Enumeration ke;      //gets all kids	KidData kdata;       //class containing kids		//----------------------------------------		public kidClub(KidData kd, String club) {		clubMask = club;     //save the club		kdata = kd;          //copy the class		kid = null;          //default		ke = kdata.elements();  //get Enumerator	}		//----------------------------------------		public boolean hasMoreElements() {		//return true if there are any more kids 		//belonging to the specified club		boolean found = false;		while(ke.hasMoreElements() && ! found) {			kid = (Kid)ke.nextElement();			found = kid.getClub().equals(clubMask);		}		if(! found) 		kid = null;    //set to null if none left		return found;	}	//----------------------------------------	public Object nextElement() {		if(kid != null)			return kid;		else			//throw exception if access past end			throw new NoSuchElementException();	}}
    모든 작업은 생성자에서 정해진 클럽에 속한 또 다른 아이에 대한 컬렉션을 통하여 읽은 hasMoreElements() 메소드에서 마쳐지고, kid 변수에 kid를 저장하거나 더 이상 아이들이 없으면 예외를 발생시킨다. 정상적인 상황에서 이 예외는 절대로 발생되지 않는다.

    마지막으로 우리는 KidData에 새로게 걸러진 Enumeration을 반환하는 메소드 추가가 필요하다.
	public Enumeration kidsInClub(String club) {		return new kidClub(this, club);                                       	}
이 간단한 메소드는 클럽 이니셜과 관련된 Enumeration 클래스 kidClub에 KidClub의 인스턴스를 전달한다. 간단한 프로그램은 아래와 같고 좌측에 모든 아이들에 대하여 나타내고 우측에 속해있는 클럽들을 나타낸다.

       

Consequences of the Iterator Pattern

  1. 데이터 수정. iterators 에 대한 가장 의미 있는 질문은 데이터가 변경되는 동안 데이터를 통하여 iterating의 질문이다. 코드가 광범위해지고 오직 가끔 다음 원소로 이동한다면, 그것을 통하여 이동하는 동안에 기본적인 컬렉션에서 어떤 원소가 추가되거나 삭제될 수 있다. 또, 그것은 그 컬렉션을 변경할 수 있는 또 다른 쓰레드가 가능하다.
     
  2. 권한 있는 접근. Enumeration 클래스들은  원래의 컨테이너 클래스의 기본적인 자료 구조에 권한 있는 접근의 몇몇 정렬을 필요로 한다. 그래서 그것들은 데이터를 통해서 이동할 수 있다. 만약 데이터가 벡터나 해쉬테이블에서 정렬되어 있다면 이동하기가 더 쉽지만, 클래스에 포함된 다른 컬렉션 구조가 있다면 get 연산자를 통하여 이용할 수 있는 구조를 만들어 줘야 한다. 대신 견제하는 클래스로부터 상속 받은  Iterator 클래스를 만들 수 있고 데이터를 직접 접근할 수 있다.
     
  3. 외부 대 내부 Iterators. 디자인 패턴 교재에서는 Iterators 의 두 가지 타입을 설명하고 있다 : 외부 와 내부. 우리는 외부 Iterator 만 설명했다. 내부의 Iterator 는 전체적인 컬렉션을 통하여 이동되는 방법이고 사용자의 특별한 요청 없이 각 원소에 대해 직접적인 연산을 수행한다. 이러한 것들은 자바에서 공통적인 것은 아니지만 데이터의 값을 0 과 1사이의 값으로 컬렉션을 정규화하는 메소드를 상상할 수 있다.

'Dev... > 패턴자료' 카테고리의 다른 글

The State Pattern  (0) 2005.03.01
The Command Pattern  (0) 2005.03.01
The Interpreter Pattern  (0) 2005.03.01
The Strategy Pattern  (0) 2005.03.01
The Template Pattern  (0) 2005.03.01