Iterator Design Pattern in Ruby

1. Definition

The Iterator Design Pattern provides a way to access the elements of an aggregate object (like arrays or collections) sequentially without exposing the underlying representation. An iterator abstracts the traversal of a collection, allowing the same code to iterate over different types of collections.

2. Problem Statement

Imagine you have a collection of items, and you need to traverse through these items without knowing the underlying structure of the collection. Direct access might expose details of its representation or require multiple methods for different types of collections.

3. Solution

The Iterator pattern suggests separating the iteration logic from the collection. By doing this, you can have one consistent method to access items in different collections, making the code cleaner and more maintainable.

4. Real-World Use Cases

1. Traversing through different types of data structures like arrays, trees, or graphs.

2. Implementing pagination in databases.

3. Streaming data from different sources.

5. Implementation Steps

1. Define an Iterator interface with methods for traversal.

2. Implement the concrete iterator for the specific collection.

3. Use the iterator to traverse the collection.

6. Implementation in Ruby

# Step 1: Iterator Interface
class Iterator
  def has_next?
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end
  def next_item
    raise NotImplementedError, "#{self.class} has not implemented method '#{__method__}'"
  end
end
# Step 2: Concrete Iterator for Array
class ArrayIterator < Iterator
  def initialize(collection)
    @collection = collection
    @index = 0
  end
  def has_next?
    @index < @collection.size
  end
  def next_item
    value = @collection[@index]
    @index += 1
    value
  end
end
# Aggregate object (Array in this example)
class CustomArray
  def initialize
    @items = []
  end
  def add_item(item)
    @items << item
  end
  def create_iterator
    ArrayIterator.new(@items)
  end
end
# Client code
collection = CustomArray.new
%w[A B C D].each { |item| collection.add_item(item) }
iterator = collection.create_iterator
while iterator.has_next?
  puts iterator.next_item
end

Output:

A
B
C
D

Explanation:

1. Iterator is an abstract class defining the contract for concrete iterators.

2. ArrayIterator is a concrete iterator for arrays, keeping track of its position with the @index variable.

3. CustomArray represents a collection, and it provides a method create_iterator to get an iterator for its items.

4. In the client code, we add items to our collection, get its iterator, and then traverse the collection using the iterator.

7. When to use?

Use the Iterator Pattern when:

1. You need a consistent way to traverse different collections.

2. You want to provide several alternative ways to traverse a collection.

3. You want to abstract the inner workings of a collection from its consumers.


Comments