디자인 패턴: 반복자 패턴

DanChu 🌟·2022년 7월 25일
0

iterator pattern

객체 지향 프로그래밍에서 반복자를 사용하여 컨테이너를 가로지르며 컨테이너의 요소들에 접근하는 디자인 패턴.
반복자 패턴은 컨테이너로부터 알고리즘을 분리시키고, 일부의 경우 알고리즘들은 필수적으로 컨테이너에 특화되어있어서 분리가 불가능.

유연하고 재사용 가능한 객체 지향 소프트웨어를 설계하기 위해 반복되는 디자인 문제를 해결하는 방법

출처: 위키

컬렉션의 구현 방법을 노출시키지 않으면서 그 집합체 안의 모든 항목에 접근하며 각각에 대한 반복작업을 할 수 있게 해줌

  • Iterator : 집합체의 요소들을 순서대로 검색하기 위한 인터페이스 정의
  • ConcreateIterator : Iterator 인터페이스를 구현함
  • Aggregate : 여러 요소들로 이루어져 있는 집합체
  • ConcreateAggregate : Aggreagate 인터페이스를 구현하는 클래스

반복자 패턴의 예시

책꽃이 안에 책 꽂은 후, 하나씩 확인하기

  1. Book.class: 한권의 책에 대한 정보를 가지고 있는 클래스
public class Book {
    private String name;

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

    public String getName() {
        return name;
    }
}
  1. Aggregate.class: 집합체를 의미하는 인터페이스
    Aggregate는 iterator 역할을 만들어내는 인터페이스를 결정
public interface Aggregate {

    public abstract Iterator createIterator();
}
  1. BookShelf.class: 책을 보관하는 책꽂이 역할을 하는 클래스
    BookShelf는 이러한 Aggregate의 구현체.
    실제로 책꽂이 안을 돌아다닐 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);
    }
}
  1. BookShelfIterator.class: BookShelf 클래스에서 검색을 수행하는 클래스
    BookShelfIterator를 Iterator로 다루기위해 Iterator 인터페이스를 상속받음.
    Iterator 인터페이스는 Java에 이미 구현되어 있음.
public class BookShelfIterator implements Iterator<Book> {
    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;
    }
}

next()는 다음 요소를 반환
hashNext()는 검색을 계속 수행할지 여부를 판단

실행

Main.class

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()에 의해 한권씩 책을 꺼내 이름을 출력.

반복자 패턴의 장단점

장점

  • 집합체 클래스 응집도 높여줌
  • 집합체 내에서 일이 처리되는 방식을 알지 않고도 집합체 안의 모든 항목에 접근 가능
  • 모든 항목에 하나씩 접근하는 작업을 컬렉션 객체가 아닌 이터레이터 객체에서 맡게 됨
    - 집합체의 인터페이스 및 구현이 간단해질 뿐 아니라, 집합체에서는 반복 작업에 신경쓰지않고 자신이 원래 해야할 일에 집중할 수 있음

단점

  • 단순한 순회를 구현하는 경우에는 클래스만 많아져서 복잡도가 증가할 수 있음

자바의 iterable 인터페이스

iterable: Collection의 상위 인터페이스

public interface Collection<E> extends Iterable<E> {
    // ...
}
public interface Iterable<T> {
    Iterator<T> iterator();

    default void forEach(Consumer<? super T> action) {
        Obejcts.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
    // ...
}

Iterable 인터페이스 안에 디폴트로 forEach()가 추가 (Java 8 이상)

// forEach()추가 전, stream으로 forEach

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
list.stream().forEach(System.out::println);
// 추가 후, stream생성 필요없이 List에서 바로 forEach 사용 가능

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
list.forEach(System.out::println);

자바의 iterator 인터페이스

public interface Iterator<E> {
    boolean hasNext();
    E next();
    default void remove() {
        throw ...
    }
    default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }
}
  • 디폴트 메소드로 remove와 forEachRemaining가 정의되어 있음
  • Iterator 인터페이스를 구현하고자 하는 클래스는 hasNext() 메소드와 next() 메소드를 오버라이딩하면 됨
  • 컬렉션 구현 방법을 외부로 노출시키지 않고, 해당 메소드를 사용하기 위해서 이 Iterator 인터페이스를 사용





references

0개의 댓글