🙋🏻♀️ What is the Iterator Pattern?
The Iterator Pattern is a behavioral design pattern that provides a way to access the elements of an aggregate object (such as an array or a list) sequentially without exposing its underlying representation. This pattern abstracts the details related to accessing and traversing the elements of the aggregate object, making it easy to work with complex data structures.
Iterator
: an interface that defines the methods required to traverse a collection (likenext()
,hasNext()
).Aggregate
: an interface representing a collection of objects, which returns an instance of the Concrete Iterator.
✨ Simple TypeScript Example
Let’s start with the Iterator interface. The Iterator
interface mandates the next()
and hasNext()
methods.
interface Iterator<T> {
next(): T
hasNext(): boolean
}
The Aggregate
interface provides a method to get an Iterator
object.
interface Aggregate<T> {
createIterator(): Iterator<T>
}
Here’s a Concrete Iterator for an array.
class ArrayIterator<T> implements Iterator<T> {
private collection: T[]
private position: number = 0
constructor(collection: T[]) {
this.collection = collection
}
next(): T {
return this.collection[this.position++]
}
hasNext(): boolean {
return this.position < this.collection.length
}
}
And here’s a Concrete Aggregate, an array that creates ArrayIterators
.
class ArrayAggregate<T> implements Aggregate<T> {
private collection: T[]
constructor(collection: T[]) {
this.collection = collection
}
createIterator(): Iterator<T> {
return new ArrayIterator(this.collection)
}
}
In the client code, you create an ArrayAggregate
and use an Iterator to traverse it.
const arrayAggregate = new ArrayAggregate([1, 2, 3, 4, 5])
const iterator = arrayAggregate.createIterator()
while (iterator.hasNext()) {
console.log(iterator.next())
}
🧑💻 Use it or Avoid it
When to use it:
- If you want to access elements in complex data structures and don’t want to know the details of how to traverse the structure.
- If you want to support multiple traversal algorithms with different iterators and choose which to use at runtime.
When to avoid it:
- For simple structures (like JavaScript arrays), native loops or the forEach method are more straightforward.
- If you don’t need to support multiple traversal methods, or you’re not trying to encapsulate the traversal operation, an Iterator may be overkill.