Iterator Pattern
Iterator Pattern: Traversing Collections Uniformly
Section titled “Iterator Pattern: Traversing Collections Uniformly”Now let’s dive into the Iterator Pattern - one of the most fundamental behavioral design patterns that provides a way to access elements of a collection sequentially without exposing its underlying representation.
Why Iterator Pattern?
Section titled “Why Iterator Pattern?”Imagine you’re browsing a library. You don’t need to know how books are organized (by author, by topic, by shelf number) - you just walk through the aisles and browse. The library provides a consistent way to access books regardless of how they’re stored. The Iterator Pattern works the same way!
The Iterator Pattern provides a way to access elements of an aggregate object sequentially without exposing its underlying representation. It decouples the traversal logic from the collection structure.
What’s the Use of Iterator Pattern?
Section titled “What’s the Use of Iterator Pattern?”The Iterator Pattern is useful when:
- You want to traverse collections uniformly - Same interface for different collection types
- You want to hide collection structure - Clients don’t need to know internal representation
- You want multiple traversals - Support multiple simultaneous traversals
- You want to decouple traversal logic - Separate iteration logic from collection
- You want to support different iteration strategies - Forward, backward, filtered, etc.
What Happens If We Don’t Use Iterator Pattern?
Section titled “What Happens If We Don’t Use Iterator Pattern?”Without the Iterator Pattern, you might:
- Expose internal structure - Clients need to know how collection is implemented
- Tight coupling - Clients depend on specific collection implementation
- Code duplication - Traversal logic repeated for each collection type
- Hard to change - Changing collection structure breaks client code
- No uniform interface - Different collections have different traversal methods
Simple Example: The Book Collection
Section titled “Simple Example: The Book Collection”Let’s start with a super simple example that anyone can understand!
Visual Representation
Section titled “Visual Representation”Interaction Flow
Section titled “Interaction Flow”Here’s how the Iterator Pattern works in practice - showing how iterator traverses collections:
sequenceDiagram
participant Client
participant Collection as BookCollection
participant Iterator as BookIterator
Client->>Collection: create_iterator()
activate Collection
Collection->>Iterator: new BookIterator(collection)
activate Iterator
Iterator-->>Collection: Iterator created
deactivate Iterator
Collection-->>Client: Iterator
deactivate Collection
Client->>Iterator: has_next()
activate Iterator
Iterator->>Iterator: Check index < size
Iterator-->>Client: true
deactivate Iterator
Client->>Iterator: next()
activate Iterator
Iterator->>Iterator: Get element at index
Iterator->>Iterator: Increment index
Iterator-->>Client: Book("Design Patterns")
deactivate Iterator
Note over Client,Iterator: Iterator provides uniform\ninterface for traversal!
Client->>Iterator: has_next()
Iterator-->>Client: true
Client->>Iterator: next()
Iterator-->>Client: Book("Clean Code")
The Problem
Section titled “The Problem”You’re building a library system with different collection types (array, linked list). Without Iterator Pattern:
Problems:
- Exposed internal structure - Clients need to know implementation details
- Different traversal code - Each collection type requires different code
- Tight coupling - Clients depend on specific implementations
- Hard to extend - Adding new collection types breaks client code
The Solution: Iterator Pattern
Section titled “The Solution: Iterator Pattern”Class Structure
Section titled “Class Structure”classDiagram
class Iterable {
<<interface>>
+create_iterator() Iterator
}
class Iterator {
<<interface>>
+has_next() bool
+next() Object
+current() Object
}
class BookCollection {
-books: List
+create_iterator() Iterator
+add(book) void
}
class BookIterator {
-collection: BookCollection
-index: int
+has_next() bool
+next() Book
+current() Book
}
Iterable <|.. BookCollection : implements
Iterator <|.. BookIterator : implements
BookCollection --> Iterator : creates
BookIterator --> BookCollection : traverses
note for Iterator "Uniform traversal interface"
note for BookCollection "Hides internal structure"
Real-World Software Example: Distributed Data Processing
Section titled “Real-World Software Example: Distributed Data Processing”Now let’s see a realistic software example - a distributed system that needs to process data from multiple sources (database, file system, API) uniformly.
The Problem
Section titled “The Problem”You’re building a data processing pipeline that needs to process records from different sources. Without Iterator Pattern:
Problems:
- Different access patterns - Each source has different methods
- Client needs source type - Must know which source it’s using
- Hard to extend - Adding new sources requires client changes
- Code duplication - Similar processing logic repeated
The Solution: Iterator Pattern
Section titled “The Solution: Iterator Pattern”Iterator Pattern Variants
Section titled “Iterator Pattern Variants”There are different ways to implement the Iterator Pattern:
1. External Iterator (Standard)
Section titled “1. External Iterator (Standard)”Client controls iteration:
Pros: Client has full control, flexible
Cons: More code for client
2. Internal Iterator
Section titled “2. Internal Iterator”Collection controls iteration, client provides callback:
Pros: Simpler client code, less error-prone
Cons: Less control for client
When to Use Iterator Pattern?
Section titled “When to Use Iterator Pattern?”Use Iterator Pattern when:
✅ You want to traverse collections uniformly - Same interface for different types
✅ You want to hide collection structure - Clients don’t need implementation details
✅ You want multiple traversals - Support multiple simultaneous iterators
✅ You want to decouple traversal logic - Separate iteration from collection
✅ You want to support different iteration strategies - Forward, backward, filtered
When NOT to Use Iterator Pattern?
Section titled “When NOT to Use Iterator Pattern?”Don’t use Iterator Pattern when:
❌ Simple collections - If you only have one collection type, direct iteration is simpler
❌ Performance critical - Iterator adds indirection (usually negligible)
❌ Collections are too simple - For arrays or simple lists, direct access might be better
❌ Over-engineering - Don’t add complexity for simple cases
Common Mistakes to Avoid
Section titled “Common Mistakes to Avoid”Mistake 1: Exposing Internal Structure
Section titled “Mistake 1: Exposing Internal Structure”Mistake 2: Modifying Collection During Iteration
Section titled “Mistake 2: Modifying Collection During Iteration”Mistake 3: Not Handling Concurrent Modifications
Section titled “Mistake 3: Not Handling Concurrent Modifications”Benefits of Iterator Pattern
Section titled “Benefits of Iterator Pattern”- Uniform Interface - Same traversal interface for all collections
- Hidden Structure - Clients don’t know internal implementation
- Decoupled - Traversal logic separated from collection
- Multiple Traversals - Support multiple simultaneous iterators
- Easy to Extend - Add new collection types without changing clients
- Distributed Systems Friendly - Works with remote data sources
Revision: Quick Catch-Up
Section titled “Revision: Quick Catch-Up”What is Iterator Pattern?
Section titled “What is Iterator Pattern?”Iterator Pattern is a behavioral design pattern that provides a way to access elements of an aggregate object sequentially without exposing its underlying representation.
Why Use It?
Section titled “Why Use It?”- ✅ Uniform traversal - Same interface for all collections
- ✅ Hide structure - Clients don’t know implementation
- ✅ Decouple - Traversal logic separated from collection
- ✅ Multiple traversals - Support multiple simultaneous iterators
- ✅ Easy to extend - Add new collections without changing clients
How It Works?
Section titled “How It Works?”- Define Iterator interface - Uniform traversal methods
- Define Iterable interface - Collections that can be iterated
- Implement Concrete Iterator - Traverses specific collection
- Implement Concrete Aggregate - Collection that creates iterator
- Client uses iterator - Uniform interface for all collections
Key Components
Section titled “Key Components”Client → Iterable → Iterator → Aggregate- Iterator - Interface for traversal
- Concrete Iterator - Implements traversal for specific collection
- Iterable - Interface for collections that can be iterated
- Concrete Aggregate - Collection that creates iterator
- Client - Uses iterator uniformly
Simple Example
Section titled “Simple Example”class Iterator: def has_next(self): pass def next(self): pass
class Collection: def create_iterator(self): return Iterator()
iterator = collection.create_iterator()while iterator.has_next(): item = iterator.next()When to Use?
Section titled “When to Use?”✅ Traverse collections uniformly
✅ Hide collection structure
✅ Support multiple traversals
✅ Decouple traversal logic
✅ Support different iteration strategies
When NOT to Use?
Section titled “When NOT to Use?”❌ Simple collections
❌ Performance critical
❌ Collections are too simple
❌ Over-engineering
Key Takeaways
Section titled “Key Takeaways”- Iterator Pattern = Uniform traversal interface
- Iterator = Traversal interface
- Iterable = Collection interface
- Benefit = Uniform interface, hidden structure
- Use Case = Multiple collection types, distributed data sources
Common Pattern Structure
Section titled “Common Pattern Structure”class Iterator: def has_next(self): pass def next(self): pass
class Collection: def create_iterator(self): return Iterator()Remember
Section titled “Remember”- Iterator Pattern provides uniform traversal interface
- It hides collection structure
- It decouples traversal from collection
- It’s about uniformity, not just iteration!
- Essential for distributed systems with multiple data sources!
Interview Focus: Iterator Pattern
Section titled “Interview Focus: Iterator Pattern”Key Points to Remember
Section titled “Key Points to Remember”1. Core Concept
Section titled “1. Core Concept”What to say:
“Iterator Pattern is a behavioral design pattern that provides a way to access elements of an aggregate object sequentially without exposing its underlying representation. It decouples traversal logic from the collection structure, allowing uniform traversal of different collection types.”
Why it matters:
- Shows you understand the fundamental purpose
- Demonstrates knowledge of decoupling and encapsulation
- Indicates you can explain concepts clearly
2. When to Use Iterator Pattern
Section titled “2. When to Use Iterator Pattern”Must mention:
- ✅ Multiple collection types - Need uniform traversal interface
- ✅ Hide implementation - Clients shouldn’t know internal structure
- ✅ Multiple traversals - Support simultaneous iterators
- ✅ Distributed systems - Uniform interface for remote data sources
- ✅ Decouple traversal - Separate iteration logic from collection
Example scenario to give:
“I’d use Iterator Pattern when building a data processing pipeline that needs to process records from multiple sources - database, file system, and API. Each source has different internal structure, but Iterator provides uniform traversal interface. Client code doesn’t need to know if data comes from database or file - it just iterates uniformly.”
3. Iterator vs Direct Access
Section titled “3. Iterator vs Direct Access”Must discuss:
- Iterator: Uniform interface, hides structure, decoupled
- Direct Access: Simple, but exposes structure, tight coupling
- Key difference: Iterator abstracts traversal, direct access exposes implementation
Example to give:
“Iterator Pattern abstracts traversal logic, allowing clients to traverse collections without knowing their internal structure. Direct access requires clients to know if collection is array, linked list, or tree, leading to tight coupling. Iterator provides uniform interface regardless of implementation.”
4. Distributed Systems Relevance
Section titled “4. Distributed Systems Relevance”Must discuss:
- Multiple data sources - Database, file system, API, message queues
- Uniform interface - Same traversal code for all sources
- Lazy loading - Can load data on-demand during iteration
- Network efficiency - Can implement pagination in iterator
Example to give:
“In distributed systems, Iterator Pattern is essential for processing data from multiple sources uniformly. For example, a data pipeline might need to process records from database, S3 files, and Kafka streams. Iterator provides uniform interface, and can implement lazy loading and pagination to handle large datasets efficiently.”
5. Benefits and Trade-offs
Section titled “5. Benefits and Trade-offs”Benefits to mention:
- Uniform interface - Same traversal code for all collections
- Hidden structure - Clients don’t know implementation
- Decoupled - Traversal logic separated from collection
- Multiple traversals - Support simultaneous iterators
- Easy to extend - Add new collections without changing clients
Trade-offs to acknowledge:
- Complexity - Adds abstraction layer
- Performance - Small overhead (usually negligible)
- Over-engineering risk - Can be overkill for simple collections
6. Common Interview Questions
Section titled “6. Common Interview Questions”Q: “What’s the difference between Iterator Pattern and for-each loop?”
A:
“Iterator Pattern provides an abstraction layer that hides collection implementation and allows uniform traversal of different collection types. For-each loops work with specific collection types and expose their structure. Iterator Pattern is useful when you have multiple collection types or need to hide implementation, while for-each is simpler for single collection types.”
Q: “How would you implement Iterator for a distributed data source?”
A:
“For distributed data sources, I’d implement Iterator with lazy loading and pagination. The iterator would fetch data in batches from remote source, caching current batch. When has_next() is called, it checks if current batch has more items or fetches next batch. This allows processing large datasets without loading everything into memory.”
Q: “How does Iterator Pattern relate to SOLID principles?”
A:
“Iterator Pattern supports Single Responsibility Principle by separating traversal logic into iterator. It supports Open/Closed Principle - you can add new collection types without modifying client code. It supports Dependency Inversion Principle by having clients depend on Iterator interface rather than concrete collections. It also supports Interface Segregation Principle by providing focused iterator interface.”
Interview Checklist
Section titled “Interview Checklist”Before your interview, make sure you can:
- Define Iterator Pattern clearly in one sentence
- Explain when to use it (with examples showing uniform traversal)
- Describe Iterator vs Direct Access
- Implement Iterator Pattern from scratch
- Compare with other patterns (Visitor, Strategy)
- List benefits and trade-offs
- Identify common mistakes (exposing structure, concurrent modification)
- Give 2-3 real-world examples (especially distributed systems)
- Connect to SOLID principles
- Discuss when NOT to use it
- Explain distributed systems relevance
Remember: Iterator Pattern is about uniform traversal - providing a consistent way to access elements without exposing collection structure, essential for distributed systems! 🔄