디자인 패턴 - Iterator

이강민·2024년 8월 25일
0

커널360

목록 보기
33/56
post-thumbnail

Iterator

처리를 반복 할 수 있도록 내부적으로 컬렉션이나 배열을 생성하여 반복자를 제공하는 행동패턴입니다.
List 와 배열을 통해 생성하지 않고 왜 Iterator를 사용할까?
컬렉션의 종류에 상관없이 클라이언트는 사용 클래스를 인스턴스화하고 내용을 추가하기만 한다면 반복문을 사용할 수 있습니다. 이는 클라이언트가 복잡한 컬렉션을 직접 생성하는 대신 인터페이스를 통해 일관성 있게 처리할 수 있도록 합니다. 가령 일반적인 패턴에서 보이듯 어떤 클래스에서는 ArrayList 또는 배열을 사용하여 코드의 복잡성이 증가하는데 이를 일관성 있게 바꾸어주어 유지보수에 좋은 코드를 작성할 수 있도록 유도합니다.

구현

구현은 자바 java.util 패키지의 Iterable 클래스와 Iterator 클래스를 사용하여 구현합니다.
어떤 객체의 모음의 클래스는 Iterable를 통해 구현하고 객체의 모음 클래스가 반복자를 실행할 수 있도록 실제 반복하는 클래스를 만들어 Iterator를 통해 구현시키고 해당 클래스를 객체 모음 클래스에서 리턴하도록 구현하여 Main에서는 Iterator에 접근하지 않고 바로 사용할 수 있도록 합니다.

예제

예제 프로그램은 Book객체를 BookShelf를 만들어 객체를 모으고
BookShelf를 통해 Book 객체를 반복 순회하면서 객체를 반환 할 수 있도록 만들 것 입니다.

이렇게 하면 사용자(클라이언트) BookShelf 클래스를 인스턴스화하고 Book을 담기만 하면 어떻게 구현했던가 반복자를 사용할 수 있습니다.

먼저 Book 클래스입니다.
Book클래스는 단지 Book에 대한 정보만 담고 있어야되지요.

public class Book {
	private String name;

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

	public String getName() {
		return name;
	}
}

그 다음 BookShelf입니다.
예제에서는 배열을 만들어 관리하지만 다양한 컬렉션을 이용해 담을 수도 있습니다.


public class BookShelf implements Iterable<Book> {
	private Book[] books; 
	private int size = 0; 
	// 책상의 크기를 결정한다.
	public BookShelf(int maxsize){
		this.books = new Book[maxsize];
	}
    // 책을 차례로 반환하기 위해 index에 따라 반환하도록 한다.
	public Book getBookAt(int index){
		return books[index];
	}
	// 책을 등록하는 코드, Book 클래스를 받고 배열에 저장한다음 크기를 증가시킨다. 
	public void appendBook(Book book){
		this.books[size] = book;
		this.size++;
	}
	// 해당 크기를 반환한다. 반복하려면 크기를 알아야한다.
	public int getLength(){
		return this.size;
	}
	// 반복부분, next와 hasNext 등 기본 제공 메소드를 제공하기 위해 
    반복자를 상속받은 클래스를 리턴한다. 
	@Override
	public Iterator<Book> iterator() {
		return new BookShelfIterator(this);
	}
}

그 다음 Iterator를 구현한 클래스를 생성합니다.
(여기서는 BookShelfIterator클래스가 그 역할을 한다.)


public class BookShelfIterator implements Iterator<Book> {
	private BookShelf bookShelf;
	private int index;
	// 책장을 받아 해당 책장을 인스턴스 초기화한다.
	public BookShelfIterator(BookShelf bookShelf) {
		this.bookShelf = bookShelf;
		this.index = 0; // 0부터 시작임
	}
    
	//hasNext는 책장의 길이와 index의 크기로 구분한다. 
	@Override
	public boolean hasNext() {
		if(index < bookShelf.getLength()){
			return true;
		}else{
			return false;
		}
	}
    
    //책의 현재 인덱스를 통해 book을 반환하고 index를 증가시킨다.
	@Override
	public Book next() {
		if(!hasNext()){
			throw new NoSuchElementException();
		}
		Book book = bookShelf.getBookAt(index);
		index++;
		return book;
	}
}

사용부분 서두에 말한 것 처럼 책장을 만들고 책을 넣고 그냥 반복문을 사용하면 끝이다.

public class BookMain {
	public static void main(String[] args) {
    // 책장을 인스턴스화 하고
		BookShelf bookShelf = new BookShelf(4);
        // 책을 넣은 다음
		bookShelf.appendBook(new Book("Around the World"));
		bookShelf.appendBook(new Book("Bible"));
		bookShelf.appendBook(new Book("Cinderella"));
		bookShelf.appendBook(new Book("Daddy-Long"));

		// 반복자를 만든 다음
		Iterator<Book> iterator = bookShelf.iterator();
        
        // 반복시키면 끝, 배열인지 List인지 고민 없이 사용함
		while (iterator.hasNext()) {
			Book book = iterator.next();
			System.out.println(book.getName());
		}
		System.out.println();

		for(Book book : bookShelf) {
			System.out.println(book.getName());
		}
		System.out.println();
	}
}

When Why

when

  • 컬렉션의 내부 구조를 숨기고 싶을 때

    • 컬렉션의 구현 방식이 외부에 노출되지 않도록 하여, 컬렉션이 어떻게 저장되고 관리되는지에 상관없이 요소에 접근할 수 있도록 합니다.
  • 컬렉션의 요소들을 순차적으로 처리할 필요가 있을 때

    • 리스트, 배열, 트리 등 여러 가지 컬렉션 구조에서 동일한 방식으로 순차적으로 요소에 접근할 필요가 있을 때 유용합니다.
  • 여러 가지 순회 방법을 제공해야 할 때

    • 예를 들어, 하나의 컬렉션에 대해 역순, 중위, 전위 등의 여러 가지 순회 방식을 제공할 수 있습니다.
  • 동일한 인터페이스로 다양한 컬렉션을 순회해야 할 때

    • 다양한 종류의 컬렉션을 하나의 일관된 인터페이스로 처리할 수 있습니다. 이는 클라이언트 코드의 복잡성을 줄여주고 유연성을 증가시킵니다.

why

  • 책임 분리

    • 컬렉션과 그 컬렉션을 순회하는 방법을 별도로 관리할 수 있습니다. 이는 코드의 유지보수성과 확장성을 향상시킵니다.
  • 일관된 인터페이스 제공

    • 컬렉션의 종류에 상관없이, 동일한 방식으로 요소들을 순회할 수 있는 일관된 인터페이스를 제공할 수 있습니다.
  • 내부 구현의 은닉

    • 컬렉션의 내부 구조를 클라이언트에게 노출하지 않고, 안전하게 요소에 접근할 수 있게 해줍니다. 이는 캡슐화의 원칙을 따르는 것입니다.
  • 확장성

    • 새로운 순회 방식이나 새로운 종류의 컬렉션을 추가할 때, 기존 코드를 변경하지 않고도 확장이 가능합니다.
profile
AllTimeDevelop

0개의 댓글

관련 채용 정보