O Iterador é um padrão de projeto comportamental que permite a passagem sequencial através de uma estrutura de dados complexa sem expor seus detalhes internos.
Graças ao Iterator, os clientes podem examinar elementos de diferentes coleções de maneira semelhante usando uma única interface iterador.
Exemplo conceitual
Este exemplo ilustra a estrutura do padrão de projeto Iterator . Ele se concentra em responder a estas perguntas:
De quais classes ele consiste?
Quais papéis essas classes desempenham?
De que maneira os elementos do padrão estão relacionados?
Depois de aprender sobre a estrutura do padrão, será mais fácil entender o exemplo a seguir, com base em um caso de uso Swift do mundo real.
Example.swift: Exemplo conceitual
import XCTest
/// This is a collection that we're going to iterate through using an iterator
/// derived from 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: Resultados da execução
Straight traversal using IteratorProtocol:
First
Second
Third
Reverse traversal using AnyIterator:
3
2
1
Exemplo do mundo real
Example.swift: Exemplo do mundo real
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?.inOrder(body)
right?.inOrder(body)
}
private func postOrder(_ body: Block) {
left?.inOrder(body)
right?.inOrder(body)
body(value)
}
}
Output.txt: Resultados da execução
Tree traversal: Inorder
2
1
3
Tree traversal: Preorder
1
2
3
Tree traversal: Postorder
2
3
1