스위프트로 작성된 반복자
반복자는 복잡한 데이터 구조의 내부 세부 정보를 노출하지 않고 해당 구조를 차례대로 순회할 수 있도록 하는 행동 디자인 패턴입니다.
반복자 덕분에 클라이언트들은 단일 반복기 인터페이스를 사용하여 유사한 방식으로 다른 컬렉션들의 요소들을 탐색할 수 있습니다.
복잡도:
인기도:
사용 예시들: 이 패턴은 스위프트 코드에 자주 사용됩니다. 많은 프레임워크들과 라이브러리들은 이 패턴을 컬렉션을 순회하는 표준 방법을 제공하기 위해 사용합니다.
식별법: 반복자는 그의 탐색 메서드들(예: next, previous 등)로 쉽게 인식할 수 있습니다. 또 반복자를 사용하는 클라이언트 코드는 반복자가 순회하는 컬렉션을 직접 접근하지 못할 수도 있습니다.
개념적인 예시
이 예시는 반복자 디자인 패턴의 구조를 보여주고 다음 질문에 중점을 둡니다:
- 패턴은 어떤 클래스들로 구성되어 있나요?
- 이 클래스들은 어떤 역할을 하나요?
- 패턴의 요소들은 어떻게 서로 연관되어 있나요?
이 패턴의 구조를 배우면 실제 스위프트 사용 사례를 기반으로 하는 다음 예시를 더욱 쉽게 이해할 수 있을 것입니다.
Example.swift: 개념적인 예시
import XCTest /// This is a collection that we're going to iterate through using an iterator /// that conforms to IteratorProtocol. class WordsCollection { fileprivate lazy var items = [String]() func append(_ item: String) { self.items.append(item) } } extension WordsCollection: Sequence { func makeIterator() -> WordsIterator { return WordsIterator(self) } } /// Concrete Iterators implement various traversal algorithms. These classes /// store the current traversal position at all times. class WordsIterator: IteratorProtocol { private let collection: WordsCollection private var index = 0 init(_ collection: WordsCollection) { self.collection = collection } func next() -> String? { defer { index += 1 } return index < collection.items.count ? collection.items[index] : nil } } /// This is another collection that we'll provide AnyIterator for traversing its /// items. class NumbersCollection { fileprivate lazy var items = [Int]() func append(_ item: Int) { self.items.append(item) } } extension NumbersCollection: Sequence { func makeIterator() -> AnyIterator<Int> { var index = self.items.count - 1 return AnyIterator { defer { index -= 1 } return index >= 0 ? self.items[index] : nil } } } /// Client does not know the internal representation of a given sequence. class Client { // ... static func clientCode<S: Sequence>(sequence: S) { for item in sequence { print(item) } } // ... } /// Let's see how it all works together. class IteratorConceptual: XCTestCase { func testIteratorProtocol() { let words = WordsCollection() words.append("First") words.append("Second") words.append("Third") print("Straight traversal using IteratorProtocol:") Client.clientCode(sequence: words) } func testAnyIterator() { let numbers = NumbersCollection() numbers.append(1) numbers.append(2) numbers.append(3) print("\nReverse traversal using AnyIterator:") Client.clientCode(sequence: numbers) } } Output.txt: 실행 결과
Straight traversal using IteratorProtocol: First Second Third Reverse traversal using AnyIterator: 3 2 1 실제 사례 예시
Example.swift: 실제 사례 예시
import XCTest class IteratorRealWorld: XCTestCase { func test() { let tree = Tree(1) tree.left = Tree(2) tree.right = Tree(3) print("Tree traversal: Inorder") clientCode(iterator: tree.iterator(.inOrder)) print("\nTree traversal: Preorder") clientCode(iterator: tree.iterator(.preOrder)) print("\nTree traversal: Postorder") clientCode(iterator: tree.iterator(.postOrder)) } func clientCode<T>(iterator: AnyIterator<T>) { while case let item? = iterator.next() { print(item) } } } class Tree<T> { var value: T var left: Tree<T>? var right: Tree<T>? init(_ value: T) { self.value = value } typealias Block = (T) -> () enum IterationType { case inOrder case preOrder case postOrder } func iterator(_ type: IterationType) -> AnyIterator<T> { var items = [T]() switch type { case .inOrder: inOrder { items.append($0) } case .preOrder: preOrder { items.append($0) } case .postOrder: postOrder { items.append($0) } } /// Note: /// AnyIterator is used to hide the type signature of an internal /// iterator. return AnyIterator(items.makeIterator()) } private func inOrder(_ body: Block) { left?.inOrder(body) body(value) right?.inOrder(body) } private func preOrder(_ body: Block) { body(value) left?.preOrder(body) right?.preOrder(body) } private func postOrder(_ body: Block) { left?.postOrder(body) right?.postOrder(body) body(value) } } Output.txt: 실행 결과
Tree traversal: Inorder 2 1 3 Tree traversal: Preorder 1 2 3 Tree traversal: Postorder 2 3 1