●Iterator : 집합체의 요소들을 순서대로 검색하기 위한 인터페이스 정의
● ConcreateIterator : Iterator 인터페이스를 구현함
● Aggregate : 여러 요소들로 이루어져 있는 집합체
● ConcreateAggregate : Aggreagate 인터페이스를 구현하는 클래스
Book 클래스
package DesignPattern.Iterator;
public class Book {
private String name;
public Book(String name){
this.name = name;
}
public String getName(){
return name;
}
}
한권의 책에 대한 정보를 가지고 있는 클래스
Aggregate 인터페이스
package DesignPattern.Iterator;
import java.util.Iterator;
public interface Aggregate {
Iterator createIterator();
}
집합체를 의미하는 인터페이스
Aggregate는 Iterator 역할을 만들어내는 인터페이스를 결정한다.
BookShelf 클래스
package DesignPattern.Iterator;
import java.util.Iterator;
public class BookShelf implements Aggregate{
private Book[] books;
private int last =0;
public BookShelf(int size){
books = new Book[size];
}
public Book getBook(int index){
return books[index];
}
public int getLength() {
return last;
}
// 책꽂이에 책을 꽂는다
public void appendBook(Book book) {
if (last < books.length) {
this.books[last] = book;
last++;
} else {
System.out.println("책꽂이가 꽉 찼습니다!");
}
}
@Override
public Iterator createIterator() {
return new BookShelfIterator(this);
}
}
책을 보관하는 책꽂이 역할을 하는 클래스
BookShelf는 이러한 Aggregate의 구현체이다.
실제로 책꽂이 안을 돌아다닐 Iterator를 생성하고 책꽂이를 관리하는 역할을 한다.
BookShelfIterator
package DesignPattern.Iterator;
import java.util.Iterator;
public class BookShelfIterator implements Iterator {
private BookShelf bookShelf;
private int index = 0;
public BookShelfIterator(BookShelf bookShelf) {
this.bookShelf = bookShelf;
}
@Override
public boolean hasNext(){
return index < bookShelf.getLength();
}
@Override
public Book next() {
Book book = bookShelf.getBook(index);
index++;
return book;
}
}
BookShelf 클래스에서 검색을 수행하는 클래스
BookShelfIterator를 Iterator로 다루기 위해 Iterator 인터페이스를 상속받는다.
BookMain
public class BookMain {
public static void main(String[] args) {
BookShelf bookShelf = new BookShelf(10);
Book book1 = new Book("문학동네");
Book book2 = new Book("너무 한낮의 연애");
Book book3 = new Book("늑대의 문장");
bookShelf.appendBook(book1);
bookShelf.appendBook(book2);
bookShelf.appendBook(book3);
System.out.println("현재 꽂혀있는 책 : "+bookShelf.getLength()+"권");
Iterator it = bookShelf.createIterator();
while(it.hasNext()){
Book book = (Book)it.next();
System.out.println(book.getName());
}
}
}
콘솔 출력 결과
현재 꽂혀있는 책 : 3권
문학동네
너무 한낮의 연애
늑대의 문장
BookShelfIterator는 책꽂이에서 책을 한권씩 뽑아오는 역할을 한다.
검색할 책이 존재하는 동안 while문이 수행되며, it.next()에 의해 한권씩 책을 꺼내 이름을 출력한다.
⭕<장점>
다양한 데이터 순회 알고리즘을 코드를 분리할 수 있다.(단일책임원칙,SRP)
기존 코드를 수정하지 않고도 새로운 타입의 콜랙션과 이터레이터를 추가할 수 있다. (개방/폐쇄원칙,OCP)
객체마다 이터레이터를 가지고 있기 때문에 같은 콜랙션을 병렬처리할 수 있다.
필요할 때, 순회(iteration)을 지연시키거나, 이어서 진행할 수 있다.
❌<단점>
간단한 컬랙션인 경우, 이터레이터 패턴을 적용하는 것은 과도할 수 있다.
콜랙션에서 특정 위치의 요소에 바로 접근해야 할 때, 이터레이터를 사용하는 것은 비효율적일 수 있다.
Collection 인터페이스와 List, Set, Queue 인터페이스의 계층구조는 알고 있었지만, Iterable이 Collection의 상위 인터페이스 인지는 잘 몰랐다.
public interface Collection<E> extends Iterable<E> {
Collection이 iterable을 상속받고 있음을 알 수 있다.
public interface Iterable<T> {
Iterator<T> iterator();
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
iterable 내부에 forEach문과 Spliterator라는 default 메소드가 있음을 알 수 있다.
또한 인터페이스 답게 추상메소드로 iterator가 선언되어있었다.
덕분에
iterator() 메소드를 통해 for-each loop을 사용할 수 있게 되는 것이다.
따라서, Iterable 인터페이스의 역할은 iterator() 메소드를 하위 클래스에서 무조건 구현을 하게 만들기 위함인 것이라고 볼 수 있다.
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
Iterator 인터페이스를 구현하고자 하는 클래스는
hasNext 메소드와 next 메소드를 override 하면 된다.
Collection과는 별개로 존재하는 인터페이스이다.
컬렉션 구현 방법을 노출시키지 않으면서도
그 집합체 안에 들어있는 모든 항목에 접근할 수 있는 방법을 제공해야 할 때 사용할 것