Low-level design2 min

Iterator Pattern

Type: Behavioral Pattern
Relevance: Extremely High. It is literally built into almost every modern programming language (e.g., IEnumerable in C#, Iterable in Java, Symbol.iterator in JavaScript).

The Iterator pattern lets you traverse elements of a collection without exposing its underlying representation (list, stack, tree, etc.).

Real-Life Analogy

Think of a TV Remote Control. A TV has a complex underlying data structure mapping channels to frequencies. But as a user, you don't care how it's stored. You just press the Next Channel button. The remote is the Iterator. It knows how to traverse the channels without you having to understand the TV's internal circuitry.

The Problem

Collections can be stored in many ways: Arrays, Linked Lists, Trees, Hash Maps. If your client code wants to loop through a collection, it shouldn't have to know how that collection is structured. If you change your data structure from an Array to a Tree, you don't want to rewrite all your for loops.

How to Implement Iterator

  1. Define an Iterator interface with methods like getNext() and hasMore().
  2. Define an IterableCollection interface with a method createIterator().
  3. The concrete collection implements createIterator() to return an instance of a specific Iterator class that knows how to traverse that specific collection.

Example in Code

typescript
// 1. The Iterator Interface
interface Iterator<T> {
  current(): T;
  next(): T;
  key(): number;
  valid(): boolean;
  rewind(): void;
}

// 2. The Collection Interface
interface Aggregator<T> {
  getIterator(): Iterator<T>;
}

// 3. Concrete Collection and Iterator
class WordsCollection implements Aggregator<string> {
  private items: string[] = [];

  public getItems(): string[] { return this.items; }
  public getCount(): number { return this.items.length; }
  public addItem(item: string): void { this.items.push(item); }

  public getIterator(): Iterator<string> {
    return new AlphabeticalOrderIterator(this);
  }
}

class AlphabeticalOrderIterator implements Iterator<string> {
  private collection: WordsCollection;
  private position: number = 0;

  constructor(collection: WordsCollection) {
    this.collection = collection;
  }

  public rewind(): void { this.position = 0; }
  public current(): string { return this.collection.getItems()[this.position]; }
  public key(): number { return this.position; }
  
  public next(): string {
    const item = this.collection.getItems()[this.position];
    this.position += 1;
    return item;
  }

  public valid(): boolean {
    return this.position < this.collection.getCount();
  }
}

// Client Code
const collection = new WordsCollection();
collection.addItem("First");
collection.addItem("Second");
collection.addItem("Third");

const iterator = collection.getIterator();

// The client loops through without knowing it's an array under the hood!
while (iterator.valid()) {
  console.log(iterator.next());
}

Class Diagram

Note on Modern Languages

In JavaScript/TypeScript, you rarely implement this from scratch anymore. You use generators (function*) or the built-in [Symbol.iterator] protocol to make an object compatible with standard for...of loops!