skipnothing/Design Patterns

Iterator

A for (i = 0; i < shelf.books.length; i++) loop works until the day books stops being an array. Reach into the backing array and every loop you wrote breaks once that collection becomes a tree or a stream.

  • ▸A loop that indexes a collection's backing array breaks the day that storage stops being an array.
  • ▸Move position-tracking into a separate cursor and one loop walks an array, a tree, or a stream unchanged.
  • ▸Four languages spell *no more elements* four ways over one two-method shape; yield writes the cursor for you.
01

Loops that reach into storage

A Bookshelf stores its titles in a plain array and exposes two methods, size() and at(i), so a caller can walk every title:

for (let i = 0; i < shelf.size(); i++) {
  print(shelf.at(i));
}

This reads fine, but the loop quietly makes two assumptions: that positions run 0..size-1, and that fetching position i is cheap. Both hold only because a plain array sits behind at(i).

Then the shelf grows and titles must stay sorted, so the team swaps the array for a balanced tree. A tree has no slot numbered 5. at(i) either vanishes or walks from the root on every call, turning one cheap pass into a crawl. Every loop written against size() and at(i), scattered across the codebase, breaks or silently slows.

The loop knew too much. It was never walking books; it was walking array indices and trusting the shelf to translate them. The storage shape leaked through at(i) into every caller, so changing the storage meant editing all of them.

The fix is to stop letting callers ask what is at index i and give them something that answers is there a next, and what is it, a question an array, a tree, and a live stream can all answer the same way.

WHEN THE STORAGE SHAPE CHANGES

Keep going, sign up to unlock the rest

4 more parts in this topic, plus 18+ more topics in Design Patterns.

Sign up, it's freeSee the full Design Patterns