- Java 언어로 배우는 디자인 패턴 입문 -

이 책으로 디자인 패턴에 대해 공부해 나가는 걸 포스팅 해나갈 예정이다. 사실 그동안 디자인 패턴 책을 펼쳤다 덮었다를 여러번 하였지만 매번 정독에 실패하였는데 이번에는 블로깅을 해나가면서 한번 정리해보고 내가 포스팅 하는 것을 되돌아 보며 스스로 자극을 받으면서 책에 나온 패턴들을 모두 한번씩 공부해 나가볼 예정이다.  

1. Iterator 패턴

Java 에서는 배열의 모든 요소를 표현하기 위해 아래와 같은 for문을 쓰게 된다.

for(int i = 0; i < arr.length; i++){
	System.out.println(arr[i]);
}

위와 같이 배열의 모든 요소를 차례대로 처리하는 로직에서 변수 i 를 추상화 하여 일반화 시킨것이 Iterator 패턴이다.  

예제프로그램

서가(BookShelf) 에 책(Book)을 넣고 책의 이름을 표시하는 프로그램 클래스 다이어그램 및 이미지 (책 41,42p)  

Aggregate 인터페이스

Aggregate 인터페이스는 요소들이 나열되어있는 “집합체”

//집합체를 나타내는 인터페이스
public interface Aggregate {
	public abstract Iterator iterator();
}

나열, 검색 또는 조사를 하고 싶을때 iterator 메소드를 사용하여 Iterator  인터페이스를 구현한 클래스의 인스턴스 1개를 만듬

Itreator 인터페이스

요소를 하나씩 나열하면서 루프 변수와 같은 역할을 수행

//하나씩 나열하면서 검색을 실행하는 인터페이스 
public interface Iterator {
	// "다음요소가 존재" 하는지를 조사하기 위한 메소드	
	public abstract boolean hasNext(); 

	// 집합체의 요소를 1개 반환후 내부 상태를 다음으로 진행
	public abstract Object next(); 
}

위의 메소드들에 대해서는 BookShelfIterator 에서 역할이 확실해진다

 Book 클래스

생성자로 책의 제목을 지정하는 책 클래스

//책을 나타내는 클래스
public class Book {
	private String name;

	public Book(String name){
		this.name = name;
	}

	public String getName(){
		return name;
	}
}
BookShelf 클래스

Aggregate 인터페이스를 구현하여 iterator 메소드를 기술함

//서가를 나타내는 클래스
public class BookShelf implements Aggregate{
	private Book[] books; // Book의 배열 
	private int last = 0;

	// 생성할때 배열의 크기를 지정 
	public BookShelf(int maxsize){
		this.books = new Book[maxsize];
	}

	public Book getBookAt(int index){
		return books[index];
	}
	public void appendBook(Book book ){
		this.books[last] = book;
		last++;
	}

	public int getLength(){
		return last;
	}

	public Iterator iterator() {
		return new BookShelfIterator(this);
	}
}

Iterator 로서 BookShelfIterator 라는 클래스의 인스턴스를 생성해서 반환  책을 하나씩 반환하고 싶을때는 iterator 메소드를 호출

BookShelfIterator 클래스

BookShelf 클래스의 검색을 실행하는 BookShelfIterator 클래스

//서가를 검색하는 클래스
public class BookShelfIterator implements Iterator {
	private BookShelf bookShelf;
	private int index;

	public BookShelfIterator(BookShelf bookShelf){
		this.bookShelf = bookShelf;
		this.index = 0;
	}

	public boolean hasNext(){
		//다음 요소가 있는지 없는지 확인하는 부분 
		if (index < bookShelf.getLength()){
			return true;
		}else {
			return false;
		}
	}

	// 현재 index의 요소를 반환해주고 index를 다음으로 넘겨줌   
	public Object next(){
		Book book = bookShelf.getBookAt(index);
		index++; // 위에 언급한 for 문의 i++ 에 해당 
		return book;
	}
}

hasNext 메소드의 if문과 next()의 반환 로직을 유의해서 살펴보자

Main 클래스

패턴을 테스트 해볼 메인 클래스

//동작 테스트용 클래스 
public class Main {
	public static void main(String[] args) {
		BookShelf bookShelf = new BookShelf(4);
		bookShelf.appendBook(new Book("Around the World in 80 Says"));
		bookShelf.appendBook(new Book("Bible"));
		bookShelf.appendBook(new Book("Cinderella"));
		bookShelf.appendBook(new Book("Daddy-Long-Legs"));
		Iterator it = bookShelf.iterator();
		while(it.hasNext()){
			Book book = (Book)it.next();
			System.out.println(book.getName());
		}
	}
}

Iterator 패턴의 등장인물

Iterator(반복자)의 역할 요소를 순서대로 검색해가는 인터페이스(API)를 결정함 (hasNext, next 메소드 등)

ConcreteIterator(구체적인 반복자)의 역할 Iterator 가 결정한 인터페이스를 실제로 구현,예제에서는 BookShelfIterator가 이 역할을 함 검색하기위해 필요한 정보를 가지고 있고, 처리되고 있는 책은 index 필드에 기억 됨

Aggregate (집합체)의 역할 Iterator 역할을 만들어내는 인터페이스를 결정 ‘내가 가지고 있는 요소를 순서대로 검색해 주는 사람’을 만들어 내는 메소드(iterator)

ConcreteAggregate(구체적인 집합체)의 역할 Aggregate 역할이 결정한 인터페이스를 실제로 구현하는 일을 함 ConcreteIterator 역할의 인스턴스를 생성, 예제의 BookShelf

사고 넓히기

Iterator 패턴을 사용하는 이유는 - 패턴을 사용함으로써 구현과 분리해서 하나씩 셀수 있기 때문이다.

while(it.hasNext()){
	Book book = (Book)it.next();
	System.out.println(book.getName());
}

위에서 보다시피 hasNext와 next는 Iterator 의 메소드일 뿐이고 while문은 BookShelf의 구현에는 의존하고 있지 않다는것을 확인할수 있다. 이로써 BookShelf를 수정하더라도 올바른 Iterator만 반환해준다면 while 루프는 변경이 필요치 않다. …… 패턴 사용에 대한 주의점 등은 책을 참고 하고 포스팅에는 생략한다. ……

연습문제

1-1 예제 푸로그램의 BookShelf 클래스에서는 최초의 지정한 서가의 크기를 초과해서 책을 넣을 수 없습니다. 그래서 배열이 아니라 java.util.ArrayList를 사용해서 서가의 크기를 초과해도 책을 추가할 수 있도록 하십시오.

//서가를 나타내는 클래스
public class BookShelf implements Aggregate{
	private ArrayList books; // Book의 배열 
	private int last = 0;

	// 생성할때 배열의 크기를 지정 
	public BookShelf(int maxsize){
		this.books = new ArrayList(maxsize);
	}

	public Book getBookAt(int index){
		return books.get(index);
	}
	public void appendBook(Book book){
		this.books.add(last, book);
		last++;
	}

	public int getLength(){
		return last;
	}

	public Iterator iterator() {
		return new BookShelfIterator(this);
	}
}