Skip to content

Iterator #11

@simoniful

Description

@simoniful

Iterator Pattern은 list, stack, tree 등과 같은 기본적인 컬렉션의 내부 구현을 노출하지 않으면서 컬렉션의 모든 요소에 순차적으로 접근할 수 있는 방법을 제공하는 패턴입니다. Swift에서는 IteratorProtocol을 채택하여 for문을 사용하여 반복할 수 있는 타입을 정의하게 됩니다.

Swift에서 Iterator Protocol은 for in loop을 사용하여 반복할 수 있는 유형을 정의합니다. 직접적으로 Iterator Protocol을 따르는 거 보다 iterator object에 Sequence protocol을 채용하여 구현하는 편 입니다. Sequence protocol 역시 내부적으로는 Iterator Protocol을 준수합니다. 해당 프로토콜을 따르면서 map(), filter(), sort() 등의 고차 함수 기능을 사용할 수 있게 됩니다. 프로토콜을 따르는 모든 객체에서 이러한 유용한 내장 함수를 사용할 수 있기 때문에 별도의 정렬, 분할 및 비교 알고리즘을 작성할 필요가 없습니다.

  • Iterator: 요소에 접근하고 탐색하기 위한 인터페이스를 정의
  • Concrete Iterator: Iterator 인터페이스를 기반으로 구현
    • Iterator 객체는 자체적으로 순회의 진행 상황을 추적 필요 - next(), hasNext(), first(), currentItem() etc
    • 여러 개의 반복들이 서로 독립적으로 동일한 컬렉션을 반복 가능
  • iterable: Iterator 객체를 생성하기 위한 인터페이스를 정의
  • Concrete iterable: 적절한 Concrete Iterator의 인스턴스를 반환하는 Iterator 생성 인터페이스를 기반으로 구현
    • 클라이언트가 요청 할 때마다 Concrete Iterator 인스턴스를 반환
  • Client: 인터페이스를 통해 Collection, Iterator 모두 사용

사용

객체들의 그룹을 유지하는 Collection 타입이 있고 이를 반복문에서 사용하고 싶을 때 iterator pattern을 사용합니다. Collection은 프로그래밍을 할 때 가장 많이 사용하는 데이터 타입 중 하나입니다. 이러한 Collection은 사실 그냥 하나의 컨테이너일 뿐인데요, 다른 코드에서 Collection의 요소들을 사용할 수 있도록 접근하는 방법을 제공하지 않으면 그냥 데이터 덩어리에 불과합니다.
 
Collection의 모든 요소에 접근하는 방법을 제공하기 위해 다양한 방법을 구현할 수 있을 텐데요, 만약 트리라는 Collection이 존재한다면, DFS, BFS과 같은 탐색 알고리즘을 Collection에 추가하는 것은 데이터를 저장한다는 책임을 갖는 Collection의 입장에서는 이상할 수 있습니다.
 
따라서 이러한 알고리즘은 Iterator라는 별도의 객체로 컬렉션 객체로부터 반복적으로 컬렉션 요소에 접근하는 역할을 분리 구현하는 방법이 Iterator 패턴입니다. 알고리즘 자체를 구현하는 것 외에도 Iterator 객체는 현재 위치나 끝까지 남은 요소 수와 같은 세부 정보를 캡슐화하여 여러 개의 반복문이 동시에 동작하더라도 서로 독립적으로 사용할 수 있습니다.
 
이러한 Iterator는 Collection의 요소를 가지고 오는 하나의 방법을 제공하는데요, 클라이언트는 아무것도 반환하지 않을 때까지 반복문을 통해 요소를 가지고 오게 됩니다. 또한 특정 Collection을 탐색하는 특별한 방법이 필요한 경우 Collection, Client를 수정하지 않고 새로운 Iterator 클래스만 만들어주면 됩니다.

컬렉션의 내부 구현을 외부로 노출하지 않는다고 합니다, 이 의미는 실제로 사용되는 컬렉션이 배열인지, 리스트인지와 같은 실제 구조와는 전혀 상관없이 각 원소들에 접근을 가능하게 한다는 것을 의미합니다. 그냥 Iterator를 받아서 끝에 도달할 때까지 다음 원소를 Iterator에게 다음 원소를 요청하기(.next())만 하면 되니까요.

iterator 패턴을 사용하면 컬렉션은 자신의 요소들을 관리하는 역할에만 집중할 수 있고, 어떤 요청에 의해 특정한 요소를 외부로 알려주는 책임은 Iterator에게 모두 맡길 수 있게 됩니다

주의 사항

IteratorProtocol이라는 프로토콜이 있으며 개체가 순회되는 방식을 사용자 지정할 수 있습니다. 순회에서 다음 객체를 반환하는 next() 메서드를 구현하기만 하면 됩니다. 그러나 직접 IteratorProtocol을 따를 필요는 없습니다. 커스텀 Iterator가 필요하더라도 IteratorProtocol을 직접 준수하는 것보다 Sequence를 준수하고 커스텀 next() 로직을 제공하는 것이 더 좋습니다. 링크에서 IteratorProtocol 및 Sequence와 함께 작동하는 방법에 대한 자세한 정보를 찾을 수 있습니다.

또한, 앱이 단순한 Collection만 필요로 하는 경우 Iterator Pattern은 굳이 필요 없을 수도 있습니다. Iterator를 사용하는 것은 몇 몇 Collection의 요소를 직접 처리하는 것 보다 비효율적일 수 있습니다.

정리

  • Iterator 패턴은 for 구문을 사용하여 컬렉션을 순환하는 표준 방법을 제공합니다.
  • 객체를 직접 Iterator 프로토콜 대신 Sequence를 준수하는 것이 좋습니다.
  • Sequence를 준수하면 map(), filter()와 같은 고차 함수를 손쉽게 사용이 가능합니다.
  • Single Responsibility Principle : 순회 알고리즘을 별도의 클래스로 추출하여 단일 책임 원칙을 지킬 수 있습니다.
  • Open / Closed Principle : 새로운 타입의 컬렉션 및 Iterator를 구현하더라도 기존 코드에서 사용할 수 있습니다.
  • 각각의 Iterator 객체에는 자체적인 반복 상태가 존재하기 때문에 동일한 컬렉션을 병렬로 처리할 수도 있습니다.
  • 각각의 Iterator 객체에는 자체적인 반복 상태가 존재하기 때문에 반복을 지연하거나 다시 실행할 수도 있습니다.
  • 내부 구현을 감추면서도 사용자가 컬렉션 객체가 가지고 있는 모든 원소들에 접근할 수 있는 방법을 제공합니다. 그리고 동시에 원소를 탐색하는 역할을 컬렉션 객체에서 분리 할 수 있습니다.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions