DEV Community

Binoy Vijayan
Binoy Vijayan

Posted on • Updated on

GoF-Iterator Pattern

The Iterator Pattern is a behavioural design pattern that provides a way to access elements of a collection sequentially without exposing its underlying representation. It allows clients to traverse a collection of elements without needing to know the specifics of how the collection is implemented.

Structure:

Iterator Interface/Protocol: Defines methods for accessing the elements of a collection without exposing its underlying structure. Common methods include next(), hasNext(), reset(), etc.

Concrete Iterator: Implements the Iterator interface, keeps track of the current position in the traversal of the collection, and provides methods to access elements.

Aggregate Interface/Protocol: Defines methods for creating an Iterator object, typically a createIterator() method.

Concrete Aggregate: Implements the Aggregate interface, provides a way to create an iterator object for traversing the elements.

Explanation:

The Iterator Pattern promotes the separation of concerns by decoupling the traversal mechanism from the collection being traversed. This separation enhances the flexibility and extensibility of the code by allowing different traversal algorithms to be applied to the same collection without modifying the collection itself.

Example in Swift:

// Iterator Protocol
protocol Iterator {
    func hasNext() -> Bool
    func next() -> Any?
}

// Concrete Iterator
class ArrayIterator: Iterator {
    private var array: [Any]
    private var index: Int = 0

    init(array: [Any]) {
        self.array = array
    }

    func hasNext() -> Bool {
        return index < array.count
    }

    func next() -> Any? {
        guard hasNext() else { return nil }
        defer { index += 1 }
        return array[index]
    }
}

// Aggregate Protocol
protocol Aggregate {
    func createIterator() -> Iterator
}

// Concrete Aggregate
class ConcreteAggregate: Aggregate {
    private var items: [Any]

    init(items: [Any]) {
        self.items = items
    }

    func createIterator() -> Iterator {
        return ArrayIterator(array: items)
    }
}

// Usage
let items = [1, 2, 3, 4, 5]
let aggregate = ConcreteAggregate(items: items)
let iterator = aggregate.createIterator()

while iterator.hasNext() {
    if let item = iterator.next() as? Int {
        print(item)
    }
}

Enter fullscreen mode Exit fullscreen mode

In this example

Iterator Protocol: Defines methods for accessing elements (next()) and checking if there are more elements (hasNext()).

Concrete Iterator: ArrayIterator implements the Iterator protocol for traversing an array.

Aggregate Protocol: Defines a method createIterator() to create iterators.

Concrete Aggregate: ConcreteAggregate holds collection data and creates iterators for the collection.

Usage: An array is created, and an iterator is obtained from it. The loop iterates over elements using the iterator, printing each element.

Image description

Usage

The Iterator Pattern finds application in various scenarios where you need to traverse collections of objects without exposing their underlying structure. Here are some common usage scenarios:

Collections: Iterator Pattern is extensively used in programming languages' standard libraries for iterating over collections such as arrays, lists, maps, etc. Users don't need to know the internal structure of these collections; they can simply use iterators to access elements sequentially.

Database Query Results: When fetching data from databases, iterator patterns can be employed to iterate over query results without directly dealing with the database connection or query execution logic.

File System Traversal: When working with files and directories in a file system, an iterator can be used to traverse the directory structure and access files and subdirectories without exposing the implementation details of file handling.

Menu Navigation: In graphical user interfaces, iterators can be utilized to navigate through menus or lists of items without tightly coupling the navigation logic with the UI components.

Tree Traversal: For hierarchical data structures like trees, iterators can help in traversing the nodes of the tree in a sequential manner, enabling operations like depth-first or breadth-first traversal.

Custom Data Structures: When designing custom data structures, implementing the Iterator Pattern allows users to iterate over the elements of the data structure in a standardised way, enhancing the reusability and maintainability of the code.

Network Communication: In network communication protocols, iterators can be used to iterate over packets or messages received from the network, abstracting away the low-level details of network communication.

Game Development: In game development, iterators can be employed to iterate over game objects, entities, or components, facilitating various game mechanics such as collision detection, AI behaviour, or rendering.

Summary

Overall, the Iterator Pattern is beneficial in scenarios where you want to separate the traversal logic from the underlying collection or data structure, promoting code reusability, maintainability, and flexibility. It simplifies client code by providing a uniform interface for iterating over diverse types of collections or data sources.

Overview of GoF Design Patterns

Top comments (0)