[Design Pattern] Iterator

이은수, Lee EunSoo·2024년 9월 14일
0

DesignPattern

목록 보기
2/12
post-thumbnail

개요

이터레이터 패턴이란

정말 단순하게 말하면 접근자를 만드는 패턴 정도로 생각하면 된다.

배열의 경우 그냥 인덱스로 접근하면 그만이지만 복잡한 데이터 구조의 경우(트리, 그래프 등) 순회를 하기에 어려운점이 있는데 이때 사용한다.

설명

iterator를 사용하는 이유가 뭘까?

교과서적인 목적을 말해보자면 데이터 집합체의 내부 구조를 외부에 노출하지 않고 원소들을 순차적으로 검색하기 위해서 사용한다고 한다.

이터레이터의 장단점

장점

  • 데이터 접근시 직접적으로 데이터에 접근하지 않으니 보안측면에서 유리하고
  • 접근자를 따로 생성하니 구현과 접근을 따로 개발 할 수 있다.
  • 접근의 형태를 원하는 형태로 여러개 만들 수 있다.

단점

  • Aggregate와 Iterator는 항상 짝을 이루기에 어느 한쪽이 변경되면 나머지도 코드 수정이 필요해진다.
  • 패턴 생성을 위해서 추가로 클래스를 생성해 주어야 한다.
    • = 성능저하가 생길 수 있다.

형태

위에서도 봤지만 다시 한번 보자

Iterator패턴을 구현하려면 순환접근 하고자 하는 클래스가 iterable이라는 인터페이스를 구현해야 한다. 여기에서는 Aggregate로 되어있다.

또한 아까 말했던 접근 하고자 하는 클래스는 ConcreteAggregate를 말한다.
여기서 iterator를 생성한다.

iterator는 이 iterable이 구현된 코드에서만 접근이 가능하다.

  • Aggregate
    • 데이터의 묶음인 ConcreteAggregate를 만들기 위한 interface
    • 구체적인 속성의 이름을 나타내는 것이 아니라 역할을 나타냄
      보통 속성과 구분하여 role 속성이라 부름
  • ConcreteAggregate
    • Aggregate를 구현하며 데이터를 저장하는 클래스
    • iterator를 생성함
  • Iterator
    • Iterator를 만들기 위한 interface
    • iterator에 필요한 메소드 들을 미리 정의한다.
    • role method
  • ConcreteIterator
    • Iterator를 구현하는 구체적인 반복자 클래스
    • 요소들을 순차적으로 반환하는 메소드를 구현함.
    • role클래스

예시코드

Java

아래 코드는 java를 이용해 만든 코드이다.



interface Iterator<E> {
    public boolean hasNext();
    public E next();
}

//Aggregate
interface Iterable<E> {
    public Iterator<E> iterator();
}

class Book {
    private String name = "";

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

    public String getName() {
        return name;
    }
}

//ConcreteAggregate클래스 
class BookShelf implements Iterable<Book> {
    private Book[] books;//Data
    private int last = 0;//book의 개수를 파악하기 위한 변수

    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;
    }

	

    @Override
    public Iterator<Book> iterator() {
	    //iterator의 생성
		return new BookShelfIterator(this);
    }
}

//ConcreteIterator
class BookShelfIterator implements Iterator<Book> {
    private BookShelf bookShelf; //책장 생성
    private int index;

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

    @Override
    public boolean hasNext() {
        if (index < bookShelf.getLength()) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public Book next() {
        if(!hasNext()){
            throw new NoSuchElementException();
        }
        Book book = bookShelf.getBookAt(index);
        index++;
        return book;
    }
}

public class Main {
    public static void main(String[] args) {
    	//책장 생성
        BookShelf bookShelf = new BookShelf(4);

		//책추가 
        bookShelf.appendBook(new Book("Around the World in 80 Days"));
        bookShelf.appendBook(new Book("Bible"));
        bookShelf.appendBook(new Book("Cinderella"));
        bookShelf.appendBook(new Book("Daddy-Long-Legs"));

		//iterator생성
        Iterator<Book> it = bookShelf.iterator();
        
		//내부 data출력
        while (it.hasNext()) {
            Book book = it.next();//next()메소드는 현재 book을 반환한다.
            System.out.println(book.getName());
        }
    }
}

이 코드는 책과 책장을 만들고 책장의 요소를 반환하는 iterator를 만들었다.

↳ 예제를 클래스 다이어그램으로 표현

예제 추가 설명

Aggregate가 Iterable 이고
ConcreteAggregate가 BookShelf,
BookShelfIterator가 ConcreteIterator 이다.

Swift

스위프트로 구현해보자 하는데 스위프트는 인터페이스가 없다

그래서 비슷한 기능을 하는 프로토콜을 사용해서 만들었다

그리고 스위프트의 경우 프로토콜에서 제네릭을 사용하려면 associatedtype키워드를 사용해야 한다 위의 java코드와 거의 동일한 역할을 하니 참고 바란다.

// Iterator 프로토콜 정의
protocol Iterator {
    associatedtype Element
    
    func hasNext() -> Bool
    func next() -> Element
}

// Aggregate 프로토콜 정의
protocol Aggregate {
    associatedtype IteratorType: Iterator
    func iterator() -> IteratorType
}

// ConcreteAggregate 클래스 구현
class ConcreteAggregate<Element>: Aggregate {
    private var arr: [Element] = []
    
    init() {}
    
    // 요소 추가 메서드
    func add(_ element: Element) {
        arr.append(element)
    }
    
    // Iterator 반환 메서드
    func iterator() -> ConcreteIterator<Element> {
        return ConcreteIterator(aggregate: self)
    }
    
    // 내부 요소 접근 메서드
    fileprivate func getElement(at index: Int) -> Element {
        return arr[index]
    }
    
    // 요소 수 반환 메서드
    fileprivate var count: Int {
        return arr.count
    }
}

// ConcreteIterator 클래스 구현
class ConcreteIterator<Element>: Iterator {
    private let aggregate: ConcreteAggregate<Element>
    private var currentIndex: Int = 0
    
    init(aggregate: ConcreteAggregate<Element>) {
        self.aggregate = aggregate
    }
    
    func hasNext() -> Bool {
        return currentIndex < aggregate.count
    }
    
    func next() -> Element {
        let element = aggregate.getElement(at: currentIndex)
        currentIndex += 1
        return element
    }
}

// 사용 예시
// 문자열을 다루는 ConcreteAggregate와 ConcreteIterator
let stringAggregate = ConcreteAggregate<String>()
stringAggregate.add("Swift")
stringAggregate.add("Iterator")
stringAggregate.add("Pattern")

let stringIterator = stringAggregate.iterator()

print("문자열 컬렉션 순회:")
while stringIterator.hasNext() {
    let element = stringIterator.next()
    print(element)
}

정리

이번시간에는 iterator패턴에 대해서 알아보았다.

iterator패턴은 사용자 정의 데이터에 접근하기 위해서 많이 사용하는 패턴으로 접근하는 데이터를 담는 클래스는 iterable인터페이스를 구현하는 형태여야 한다.

profile
iOS 개발자 취준생, 천 리 길도 한 걸음부터

0개의 댓글