"Iterator" adalah salah satu pola desain yang paling sering tidak diperhatikan oleh programer, karena implementasinya, sebagai suatu peraturan, dibangun langsung ke dalam alat standar bahasa pemrograman. Namun, ini juga merupakan salah satu pola perilaku yang dijelaskan dalam 
buku "Geng Empat", "GoF", "Pola Desain: Elemen Perangkat Lunak Berorientasi Objek Reusable" , dan memahami perangkatnya tidak pernah sakit, dan kadang-kadang bahkan dapat membantu dalam sesuatu.
"Iterator" adalah metode akses sekuensial ke semua elemen objek komposit (khususnya, tipe wadah, seperti array atau set).Alat bahasa standar
Buat beberapa jenis 
array :
let numbersArray = [0, 1, 2] 
... dan kemudian "berjalan" melalui 
siklus :
 for number in numbersArray { print(number) } 
... sepertinya tindakan yang sangat alami, terutama untuk bahasa pemrograman modern seperti 
Swift . Namun, di balik layar tindakan sederhana ini adalah kode yang menerapkan prinsip-prinsip pola Iterator.
Dalam "Swift", untuk dapat "beralih" variabel menggunakan 
for sepeda, tipe variabel harus menerapkan 
protokol Sequence . Antara lain, protokol ini mensyaratkan tipe untuk memiliki 
Iterator tipe 
associatedtype , yang pada gilirannya harus menerapkan persyaratan protokol 
IteratorProtocol Protokol, serta 
metode pabrik makeIterator() , yang mengembalikan "iterator" spesifik untuk tipe ini:
 protocol Sequence { associatedtype Iterator : IteratorProtocol func makeIterator() -> Self.Iterator  
Protokol 
IteratorProtocol , pada gilirannya, hanya berisi satu metode - 
next() , yang mengembalikan elemen berikutnya dalam urutan:
 protocol IteratorProtocol { associatedtype Element mutating func next() -> Self.Element? } 
Kedengarannya seperti "banyak kode rumit," tetapi sebenarnya tidak. Di bawah ini kita akan melihat ini.
Tipe 
Array mengimplementasikan protokol 
Sequence (meskipun tidak secara langsung, tetapi melalui rantai 
inheritance protokol : 
MutableCollection mewarisi persyaratan 
Collection , dan yang terakhir mewarisi persyaratan 
Sequence ), jadi instance-nya, khususnya, dapat "diiterasi" menggunakan -sepeda.
Jenis khusus
Apa yang perlu dilakukan untuk bisa mengulang tipe Anda sendiri? Seperti yang sering terjadi, paling mudah untuk menunjukkan contoh.
Misalkan ada tipe yang mewakili rak buku yang menyimpan sekumpulan instance kelas tertentu, yang pada gilirannya mewakili sebuah buku:
 struct Book { let author: String let title: String } struct Shelf { var books: [Book] } 
Untuk dapat "mengulang" instance kelas 
Shelf , kelas ini harus memenuhi persyaratan protokol 
Sequence . Untuk contoh ini, cukup dengan mengimplementasikan metode 
makeIterator() , terutama karena persyaratan protokol lainnya memiliki 
implementasi standar . Metode ini harus mengembalikan instance dari jenis yang mengimplementasikan protokol 
IteratorProtocol . Untungnya, dalam kasus Swift, ini adalah kode yang sangat sederhana:
 struct ShelfIterator: IteratorProtocol { private var books: [Book] init(books: [Book]) { self.books = books } mutating func next() -> Book? {  
Metode 
next() dari tipe 
ShelfIterator dinyatakan 
mutating , karena instance type harus menyimpan keadaan yang terkait dengan iterasi saat ini:
 mutating func next() -> Book? { defer { if !books.isEmpty { books.removeFirst() } } return books.first } 
Opsi implementasi ini selalu mengembalikan elemen pertama dalam urutan, atau 
nil jika urutan kosong. Blok 
defer "dibungkus" dengan kode untuk mengubah koleksi iterated, yang menghilangkan elemen dari langkah iterasi terakhir segera setelah metode kembali.
Contoh penggunaan:
 let book1 = Book(author: ". ", title: "") let book2 = Book(author: ". ", title: " ") let book3 = Book(author: ". ", title: " ") let shelf = Shelf(books: [book1, book2, book3]) for book in shelf { print("\(book.author) β \(book.title)") }  
Karena semua tipe yang digunakan (termasuk 
Array mendasari 
Shelf ) didasarkan pada 
semantik nilai (sebagai lawan dari referensi) , Anda tidak perlu khawatir tentang nilai variabel asli yang diubah selama iterasi. Saat menangani jenis berdasarkan semantik tautan, titik ini harus diingat dan diperhitungkan saat membuat iterator Anda sendiri.
Fungsionalitas klasik
"Iterator" klasik yang dijelaskan dalam buku "Gangs of Four", selain mengembalikan elemen berikutnya dari urutan iterable, juga dapat setiap saat mengembalikan elemen saat ini dalam proses iterasi, elemen pertama dari urutan iterable dan nilai "flag" yang menunjukkan apakah masih ada elemen dalam urutan berulang relatif terhadap langkah iterasi saat ini.
Tentu saja, akan mudah untuk mendeklarasikan protokol sehingga memperluas kemampuan standar 
IteratorProtocol :
 protocol ClassicIteratorProtocol: IteratorProtocol { var currentItem: Element? { get } var first: Element? { get } var isDone: Bool { get } } 
Elemen pertama dan saat ini dikembalikan opsional sejak urutan sumber mungkin kosong.
Opsi implementasi sederhana:
 struct ShelfIterator: ClassicIteratorProtocol { var currentItem: Book? = nil var first: Book? var isDone: Bool = false private var books: [Book] init(books: [Book]) { self.books = books first = books.first currentItem = books.first } mutating func next() -> Book? { currentItem = books.first books.removeFirst() isDone = books.isEmpty return books.first } } 
Dalam uraian asli pola, metode 
next() mengubah keadaan internal iterator untuk pergi ke elemen berikutnya dan bertipe 
Void , dan elemen saat ini dikembalikan oleh metode 
currentElement() . Dalam protokol 
IteratorProtocol , kedua fungsi ini seolah-olah digabungkan menjadi satu.
Kebutuhan akan metode 
first() juga diragukan, karena iterator tidak mengubah urutan asli, dan kami selalu memiliki kesempatan untuk mengakses elemen pertamanya (jika ada, tentu saja).
Dan, karena metode 
next() mengembalikan 
nil ketika iterasi selesai, metode 
isDone() juga menjadi tidak berguna.
Namun, untuk tujuan akademik, sangat mungkin untuk membuat fungsi yang dapat menggunakan fungsionalitas penuh:
 func printShelf(with iterator: inout ShelfIterator) { var bookIndex = 0 while !iterator.isDone { bookIndex += 1 print("\(bookIndex). \(iterator.currentItem!.author) β \(iterator.currentItem!.title)") _ = iterator.next() } } var iterator = ShelfIterator(books: shelf.books) printShelf(with: &iterator)  
Parameter 
iterator dinyatakan 
inout karena keadaan internal berubah selama eksekusi fungsi. Dan ketika fungsi dipanggil, instance iterator ditransmisikan tidak secara langsung oleh nilainya sendiri, tetapi dengan referensi.
Hasil pemanggilan metode 
next() tidak digunakan, mensimulasikan tidak adanya nilai kembali implementasi buku teks.
Alih-alih sebuah kesimpulan
Sepertinya ini yang ingin saya katakan kali ini. Semua kode indah dan sengaja menulisnya!
Artikel saya yang lain tentang pola desain: