Pemrograman · Pola desain
Penjelasan pola Iterator beserta contohnya
Jawaban singkat
Iterator adalah pola desain behavioral yang menyediakan cara mengakses elemen koleksi satu per satu tanpa membuka cara koleksi itu disimpan. Logika traversal berada di objek iterator terpisah dengan method seperti next(), sehingga satu koleksi dapat menawarkan beberapa urutan traversal dan kode klien dapat mengulang array, pohon, dan stream dengan cara yang sama. Di TypeScript dan JavaScript pola ini sudah bawaan bahasa lewat Symbol.iterator dan for...of.
Tujuan dan masalah yang diselesaikan
Tujuan: menyediakan cara mengakses elemen objek agregat secara berurutan tanpa membuka representasi dasarnya. Iterator adalah salah satu pola behavioral dalam buku Gang of Four (GoF).
Bayangkan sebuah class yang menyimpan bagan organisasi perusahaan sebagai pohon. Berbagai bagian program perlu mendaftar semua karyawan: satu laporan berjalan level demi level, yang lain butuh urutan depth-first. Jika setiap laporan menelusuri pohon sendiri, setiap laporan bergantung pada field internal pohon, dan mengubah pohon agar memakai penyimpanan lain akan merusak semuanya.
Dengan Iterator, pohon menawarkan objek iterator. Sebuah laporan meminta iterator dan memanggil next() sampai selesai. Laporan itu tidak tahu apakah datanya pohon, array, atau baris yang diambil dari database halaman demi halaman.
Struktur dan peserta
| Peran | Tanggung jawab |
|---|---|
| Iterator | Interface untuk traversal, biasanya next() yang mengembalikan elemen berikutnya dan flag yang menandakan apakah traversal sudah selesai |
| Concrete iterator | Mengimplementasikan traversal untuk satu koleksi dan mengingat posisi saat ini |
| Aggregate (iterable) | Interface dengan method yang membuat iterator |
| Concrete aggregate | Koleksinya; mengembalikan concrete iterator baru setiap kali diminta |
| Client | Memakai iterator untuk mengunjungi elemen tanpa tahu struktur koleksi |
Di JavaScript peran-peran ini punya nama standar, yang didefinisikan oleh protokol iterasi di ES2015. Sebuah iterable punya method [Symbol.iterator](). Sebuah iterator punya method next() yang mengembalikan { value, done }. Setiap loop for...of, spread [...x], dan destructuring assignment memakai protokol ini.
Implementasi TypeScript: iterator yang ditulis manual
Class range ini hanya menyimpan start, end, dan step, bukan daftar angka. Iterator-nya menghitung setiap nilai saat diminta.
class Range implements Iterable<number> {
constructor(private start: number, private end: number, private step = 1) {}
[Symbol.iterator](): Iterator<number> {
let current = this.start;
const { end, step } = this;
return {
next(): IteratorResult<number> {
if (current >= end) return { value: undefined, done: true };
const value = current;
current += step;
return { value, done: false };
},
};
}
}
const evens = new Range(0, 10, 2);
for (const n of evens) console.log(n); // 0 2 4 6 8 (one per line)
console.log([...evens]); // [ 0, 2, 4, 6, 8 ]
console.log(Math.max(...new Range(1, 4))); // 3Setiap pemanggilan [Symbol.iterator]() mengembalikan iterator baru dengan posisi current sendiri. Itulah sebabnya evens bisa diulang dua kali. Jika koleksi mengembalikan iterator yang sama setiap kali, loop kedua tidak akan melihat elemen apa pun.
Generator: iterator dengan kode lebih sedikit
Fungsi generator, yang ditulis dengan function*, membuat iterator secara otomatis. Setiap yield menghasilkan nilai berikutnya, dan fungsi berhenti sementara sampai next() dipanggil lagi. Di sini sebuah binary tree menawarkan traversal in-order tanpa membuka node-nodenya:
class TreeNode<T> {
constructor(public value: T, public left?: TreeNode<T>, public right?: TreeNode<T>) {}
}
class BinaryTree<T> implements Iterable<T> {
constructor(private root?: TreeNode<T>) {}
*[Symbol.iterator](): Generator<T> {
yield* inOrder(this.root);
}
}
function* inOrder<T>(node?: TreeNode<T>): Generator<T> {
if (!node) return;
yield* inOrder(node.left);
yield node.value;
yield* inOrder(node.right);
}
const tree = new BinaryTree(
new TreeNode(5, new TreeNode(3, new TreeNode(1)), new TreeNode(8)),
);
console.log([...tree]); // [ 1, 3, 5, 8 ]
for (const value of tree) {
if (value > 3) break; // stops early; the rest is never computed
console.log(value); // 1, then 3
}Kapan dipakai dan kapan tidak
- Pakai bila koleksi punya struktur internal yang kompleks (pohon, graf, API berhalaman) dan Anda ingin klien mengulanginya dengan sederhana.
- Pakai untuk menawarkan beberapa urutan traversal pada data yang sama, seperti depth-first dan breadth-first.
- Pakai untuk urutan yang lazy atau tak terbatas, di mana nilai hanya dihitung saat diminta.
- Pakai untuk memberi berbagai koleksi satu interface loop yang seragam.
- Jangan menulis iterator kustom untuk data yang sudah berupa array. Kembalikan array (atau salinan read-only) dan biarkan pemanggil memakai iterasi bawaan.
- Jangan memakai iterator lazy bila pemanggil butuh akses acak lewat indeks atau panjang di awal; array lebih cocok.
Penggunaan di dunia nyata
- JavaScript:
Array,Map,Set, danStringadalah iterable, danmap.entries()atauset.values()mengembalikan iterator. - Java: interface
java.util.IteratordenganhasNext()dannext(), dipakai oleh loopforyang disempurnakan padaIterablemana pun. - Python: protokol iterator dengan
__iter__dan__next__, serta fungsi generator yang memakaiyield. - Database dan API: cursor atas hasil query dan API HTTP berhalaman menyajikan hasil satu halaman demi satu halaman, sehingga klien dapat mengulang hasil yang sangat besar tanpa memuat semuanya ke memori.
Pola terkait
| Pola | Hubungannya dengan Iterator |
|---|---|
| Composite | Iterator sering dipakai untuk menelusuri pohon Composite tanpa membuka node-nodenya. |
| Visitor | Iterator menelusuri elemen; Visitor mendefinisikan apa yang dilakukan pada setiap elemen. Keduanya sering dipakai bersama. |
| Factory Method | Method yang membuat iterator, seperti [Symbol.iterator](), adalah factory method: setiap koleksi mengembalikan class iterator-nya sendiri. |
| Memento | Iterator dapat menyimpan posisinya sebagai memento sehingga traversal bisa dilanjutkan nanti. |
FAQ
Apa perbedaan antara iterable dan iterator?
Iterable adalah objek yang dapat menghasilkan iterator, lewat [Symbol.iterator]() di JavaScript. Iterator adalah objek yang menyimpan posisi saat ini dan mengembalikan elemen satu per satu lewat next().
Apakah generator itu iterator?
Ya. Memanggil fungsi generator mengembalikan objek generator, yang sekaligus iterator (punya next()) dan iterable (mengembalikan dirinya sendiri dari [Symbol.iterator]()).
Mengapa tidak langsung membuka array internalnya saja?
Membuka array mengikat setiap pemanggil pada pilihan penyimpanan itu dan membiarkan mereka memodifikasinya. Iterator menyembunyikan strukturnya, sehingga Anda dapat mengubahnya nanti atau menghitung elemen secara lazy tanpa mengubah pemanggil.
Apa itu internal iterator?
Internal iterator adalah method yang menerima callback dan menjalankannya untuk setiap elemen, seperti array.forEach(fn). Koleksi yang mengendalikan loop. External iterator, seperti next(), membiarkan klien mengendalikan kapan maju dan kapan berhenti.