[GoF] Iterator 패턴

패턴명칭

Iterator

필요한 상황

배열과 같이 동일한 형태의 데이터를 여러 개 저장하고 있는 저장소(Aggregator)가 있다. 이 저장소에 저장된 데이터를 순서대로 접근하기 위한 패턴이다. 순서대로 접근하기 위한 방법은 저장소의 종류와는 상관없이 동일한 방식(API)을 제공한다.

예제 코드

Aggregator와 Iterator 인터페이스는 저장소의 형태와 종류에 상관없이 저장소를 구성하는 데이터를 순차적으로 모두 접근하기 위한 API를 제공한다. 이 인터페이스를 구성하는 Table과 RowIterator는 저장소에 대한 특화된 구현 클래스이다. Row 역시 저장소를 구성하는 특화된 데이터 구조에 대한 클래스이다. 이들 클래스를 사용하는 예제는 다음과 같다.

package pattern;

public class Main {
	public static void main(String[] args) {
		Table table = new Table();
		
		table.add(new Row("Jane", 27));
		table.add(new Row("Suji", 35));
		table.add(new Row("Tom", 19));
		table.add(new Row("Robin", 43));
		table.add(new Row("Robert", 58));
		
		Iterator it = table.iterator();
		while(it.next()) {
			Row row = (Row)it.current();
			System.out.println(row);
		}
	}
}

Aggregator 인터페이스는 다음과 같다.

package pattern;

public interface Aggregator {
	Iterator iterator();
}

Itertor 인터페이스는 다음과 같다.

package pattern;

public interface Iterator {
	boolean next();
	Object current();
}

next는 다음 구성 데이터로 이동하고 true를 반환한다. 만약 다음 데이터가 존재하지 않으면 false를 반환한다. current는 현재 Itertor가 가르키고 있는 현재의 데이터를 반환한다. Aggregator를 구현하는 Table 클래스는 다음과 같다.

package pattern;

import java.util.LinkedList;

public class Table implements Aggregator {
	private LinkedList<Row> table = new LinkedList<Row>();
	
	public Table() {
		//.
	}
	
	public Row get(int index) {
		return table.get(index);
	}
	
	public void add(Row row) {
		table.addLast(row);
	}
	
	public int getLength() {
		return table.size();
	}
	
	@Override
	public Iterator iterator() {
		return new RowIterator(this);
	}
}

Iterator를 구현하는 RowIterator 클래스는 다음과 같다.

package pattern;

public class RowIterator implements Iterator {
	private Table table;
	private int index;
	
	public RowIterator(Table table) {
		this.table = table;
		this.index = -1;
	}
	
	@Override
	public boolean next() {
		index++;
		return index < table.getLength();
	}

	@Override
	public Object current() {
		return table.get(index);
	}
}

저장소를 구성하는 실제 데이터에 대한 Row 클래스는 다음과 같다.

package pattern;

public class Row {
	private String name;
	private int age;
	
	public Row(String name, int age) {
		this.name = name;
		this.age = age;
	}
	
	public String getName() {
		return name;
	}
	
	public int getAge() {
		return age;
	}
	
	public String toString() {
		return "(" + name + ", " + age + ")";
	}
}

실행 결과는 다음과 같다.

(Jane, 27)
(Suji, 35)
(Tom, 19)
(Robin, 43)
(Robert, 58)
이 글은 소프트웨어 설계의 기반이 되는 GoF의 디자인패턴에 대한 강의자료입니다. 완전한 실습을 위해 이 글에서 소개하는 클래스 다이어그램과 예제 코드는 완전하게 실행되도록 제공되지만, 상대적으로 예제 코드와 관련된 설명이 함축적으로 제공되고 있습니다. 이 글에 대해 궁금한 점이 있으면 댓글을 통해 남겨주시기 바랍니다.

Safe Termination 패턴

패턴 명칭

Safe Termination

필요한 상황

스레드의 종료를 안전하게 하기 위한 패턴이다. 스레드는 기본적으로 stop 매서드를 제공하지만, 이 매서드는 안정성 문제로 deprecated 되었다. 스레드는 자신의 코드가 모두 실행되고 종료되는 것이 가장 이상적이지만, 실행 중간에 종료되어야 할 상황에서 안전하게 종료될 수도 있어야 한다.

예제 코드

먼저 수를 계속 카운팅하는 스레드 Worker를 기동시키는 아래의 코드가 있다.

package tstThread;

public class Main {
	public static void main(String[] args) {
		System.out.println("BEGIN");
		
		Worker worker = new Worker();
		worker.start();
		
		try {
			Thread.sleep(2000);
			
			System.out.println("[stopRequest BEGIN]");
			worker.stopRequest();
			System.out.println("[stopRequest END]");
			worker.join();
		} catch(InterruptedException e) {
			//.
		}
		
		System.out.println("END");
	}
}

Worker 스레드는 다음과 같다.

package tstThread;

public class Worker extends Thread {
	private volatile boolean bStop = false;
	
	public void stopRequest() {
		bStop = true;
		interrupt();
	}
	
	public void run() {
		try {
			for(int i=0; true; i++) {
				if(bStop) break;
				System.out.println("Counting: " + i);
				Thread.sleep(100);
			}
		} catch(InterruptedException e) {
			//.
		} finally {
			release();
		}
	}
	
	private void release() {
		System.out.println("SAFE TERMINATION");
	}
}

bStop 변수가 스레드를 안전하게 종료시키는 장치인데, volatile로 선언되었다. 이 변수는 stopRequest 매서드로 인해 true로 설정되며, interrupt 매서드의 호출을 동반한다. interrupt 매서드의 호출은 wait, sleep로 인해 스레드가 대기하는 상황에서도 안전하게 스레드를 종료시키기 위함이다. 실행 결과는 다음과 같다.

BEGIN
Counting: 0
Counting: 1
Counting: 2
Counting: 3
Counting: 4
Counting: 5
Counting: 6
Counting: 7
Counting: 8
Counting: 9
Counting: 10
Counting: 11
Counting: 12
Counting: 13
Counting: 14
Counting: 15
Counting: 16
Counting: 17
Counting: 18
Counting: 19
[stopRequest BEGIN]
[stopRequest END]
SAFE TERMINATION
END