|
| 1 | +<div id="top"></div> |
| 2 | + |
| 3 | +# Iterator Design Pattern |
| 4 | + |
| 5 | +- A behavioral design pattern: |
| 6 | + - lets you (traverse elements || get a way to access the elements) of a collection without know its underlying representation (list, stack, tree, etc.). |
| 7 | + |
| 8 | +- Identification: |
| 9 | + - Iterator is easy to recognize by the navigation methods (such as next, previous and others). |
| 10 | + - Client code that uses iterators might not have direct access to the collection being traversed. |
| 11 | + |
| 12 | +<details> |
| 13 | + <summary> <h2 style="display: inline;"> Sections </h2> </summary> |
| 14 | + |
| 15 | +- [Iterator Design Pattern](#iterator-design-pattern) |
| 16 | + - [Definitions](#definitions) |
| 17 | + - [Components \&\& Diagrams (UML class || Sequence diagrams).](#components--diagrams-uml-class--sequence-diagrams) |
| 18 | + - [Components By Guru](#components-by-guru) |
| 19 | + - [1. The Iterator interface:](#1-the-iterator-interface) |
| 20 | + - [2. Concrete Iterators implement:](#2-concrete-iterators-implement) |
| 21 | + - [3. The Collection (Container || Aggregation) interface:](#3-the-collection-container--aggregation-interface) |
| 22 | + - [4. Concrete Collections:](#4-concrete-collections) |
| 23 | + - [5. The Client:](#5-the-client) |
| 24 | + - [Components By geeksforgeeks](#components-by-geeksforgeeks) |
| 25 | + - [1. Iterator Interface/Abstract Class](#1-iterator-interfaceabstract-class) |
| 26 | + - [2. Concrete Iterator](#2-concrete-iterator) |
| 27 | + - [3. Aggregate Interface/Abstract Class](#3-aggregate-interfaceabstract-class) |
| 28 | + - [4. Concrete Aggregate](#4-concrete-aggregate) |
| 29 | + - [What problems can it solve || When to Use || Use Cases](#what-problems-can-it-solve--when-to-use--use-cases) |
| 30 | + - [Geeks: The Design Pattern can be useful in various scenarios, such as:](#geeks-the-design-pattern-can-be-useful-in-various-scenarios-such-as) |
| 31 | + - [GURU](#guru) |
| 32 | + - [Examples](#examples) |
| 33 | + - [Operations with context Example](#operations-with-context-example) |
| 34 | + - [Social Example \[Hard\]](#social-example-hard) |
| 35 | + - [When to **not** use Iterator Design Pattern](#when-to-not-use-iterator-design-pattern) |
| 36 | + - [Summery](#summery) |
| 37 | + - [Sources](#sources) |
| 38 | +</details> |
| 39 | + |
| 40 | + |
| 41 | +## Definitions |
| 42 | + |
| 43 | +- <details> |
| 44 | + <summary> <h3 style="display: inline;"> Tutorial Point </h3> </summary> |
| 45 | + |
| 46 | + - Used to get a way to access the elements of a collection object in sequential manner without any need to know its underlying representation. |
| 47 | + |
| 48 | + </details> |
| 49 | + |
| 50 | +- <details> |
| 51 | + <summary> <h3 style="display: inline;"> geeksforgeeks.org </h3> </summary> |
| 52 | + |
| 53 | + - The Iterator pattern: |
| 54 | + - is a widely used design pattern in software development |
| 55 | + - that provides a way to access the elements of an aggregate object (such as a list or collection) sequentially without exposing its underlying representation |
| 56 | + |
| 57 | + ---- |
| 58 | + |
| 59 | + - It defines a separate object, called an iterator, |
| 60 | + - which encapsulates the details of traversing the elements of the aggregate, |
| 61 | + - allowing the aggregate to change its internal structure without affecting the way its elements are accessed. |
| 62 | + |
| 63 | + </details> |
| 64 | + |
| 65 | + |
| 66 | +- <details> |
| 67 | + <summary> <h3 style="display: inline;"> refactoring.guru </h3> </summary> |
| 68 | + |
| 69 | + `Iterator is a behavioral design pattern` |
| 70 | + - that allows sequential traversal through a complex data structure without exposing its internal details. |
| 71 | + |
| 72 | + </details> |
| 73 | + |
| 74 | + |
| 75 | +## Components && Diagrams (UML class || Sequence diagrams). |
| 76 | + |
| 77 | +### Components By Guru |
| 78 | + |
| 79 | +<img style="background-color:#554777" src = "assets/iterator_guru_structure.png"> |
| 80 | + |
| 81 | +#### 1. The Iterator interface: |
| 82 | + - declares the operations required for traversing a collection: |
| 83 | + - fetching the next element, |
| 84 | + - retrieving the current position, |
| 85 | + - restarting iteration, etc. |
| 86 | + |
| 87 | +#### 2. Concrete Iterators implement: |
| 88 | + - specific algorithms for traversing a collection. |
| 89 | + - The iterator object should track the traversal progress on its own. |
| 90 | + - This allows several iterators to traverse the same collection independently of each other. |
| 91 | + |
| 92 | +#### 3. The Collection (Container || Aggregation) interface: |
| 93 | + - declares one or multiple methods for getting iterators compatible with the collection. |
| 94 | + - Note that: |
| 95 | + - the return type of the methods must be declared as the iterator interface so that the concrete collections can return various kinds of iterators. |
| 96 | + |
| 97 | +#### 4. Concrete Collections: |
| 98 | + - return new instances of a particular concrete iterator class each time the client requests one. |
| 99 | + - You might be wondering, where’s the rest of the collection’s code? Don’t worry, it should be in the same class. |
| 100 | + - It’s just that these details aren’t crucial to the actual pattern, so we’re omitting them. |
| 101 | + |
| 102 | +#### 5. The Client: |
| 103 | + - Works with both collections and iterators via their interfaces. |
| 104 | + - This way the client isn’t coupled to concrete classes, |
| 105 | + - allowing you to use various collections and iterators with the same client code. |
| 106 | + |
| 107 | + - clients don’t create iterators on their own, |
| 108 | + - but instead get them from collections. |
| 109 | + - Yet, in certain cases, the client can create one directly; for example, when the client defines its own special iterator. |
| 110 | + |
| 111 | + |
| 112 | +### Components By geeksforgeeks |
| 113 | + |
| 114 | +#### 1. Iterator Interface/Abstract Class |
| 115 | +- Defines the interface for accessing and traversing elements in the collection. |
| 116 | +- It typically includes methods like `hasNext()`, `next()`, and optionally `remove()`. |
| 117 | + |
| 118 | +### 2. Concrete Iterator |
| 119 | +- Implements the Iterator interface and maintains the current position in the traversal of the aggregate. |
| 120 | +- It provides the actual implementation for the traversal operations defined in the Iterator interface. |
| 121 | + |
| 122 | + |
| 123 | +### 3. Aggregate Interface/Abstract Class |
| 124 | +- Defines the interface for creating an Iterator object. |
| 125 | +- It typically includes a method like `createIterator()` that returns an Iterator object for the collection. |
| 126 | + |
| 127 | +### 4. Concrete Aggregate |
| 128 | +- Implements the Aggregate interface and represents the collection of objects. |
| 129 | +- It provides the implementation for creating an Iterator object that can traverse its elements. |
| 130 | + |
| 131 | +## What problems can it solve || When to Use || Use Cases |
| 132 | + |
| 133 | +- The Iterator pattern: |
| 134 | + - Allows us to access the elements of a collection sequentially without exposing its underlying representation. |
| 135 | + - It provides a way to iterate over a collection regardless of its internal structure. |
| 136 | + |
| 137 | + |
| 138 | +### Geeks: The Design Pattern can be useful in various scenarios, such as: |
| 139 | + |
| 140 | +- **Need for sequential access**: |
| 141 | + - Use the Iterator pattern: |
| 142 | + - when you need to access elements of a collection sequentially without exposing its underlying representation. |
| 143 | + |
| 144 | +- **Decoupling iteration logic**: |
| 145 | + - Use the Iterator pattern: |
| 146 | + - when you want to decouple the iteration logic from the collection. |
| 147 | + - This allows the collection to change its internal structure without affecting the way its elements are accessed. |
| 148 | + |
| 149 | +- **Support for multiple iterators**: |
| 150 | + - Use the Iterator pattern: |
| 151 | + - when you need to support multiple iterators over the same collection. |
| 152 | + - Each iterator maintains its own iteration state, allowing multiple iterations to occur concurrently. |
| 153 | + |
| 154 | +- **Simplifying client code**: |
| 155 | + - Use the Iterator pattern: to simplify client code that iterates over a collection. |
| 156 | + - Clients only need to interact with the iterator interface, abstracting away the complexity of the collection’s internal structure. |
| 157 | + |
| 158 | +### GURU |
| 159 | + - Use the Iterator pattern when your collection has a complex data structure under the hood, but you want to hide its complexity from clients (either for convenience or security reasons). |
| 160 | + |
| 161 | + - Use the pattern to reduce duplication of the traversal code across your app. |
| 162 | + |
| 163 | + - Use the Iterator when you want your code to be able to traverse different data structures or when types of these structures are unknown beforehand. |
| 164 | + |
| 165 | +## Examples |
| 166 | + |
| 167 | +### Operations with context Example |
| 168 | +Source: [link](https://www.geeksforgeeks.org/iterator-pattern/) |
| 169 | +Dart Code: [link](salary_example.dart) |
| 170 | + |
| 171 | +<img style="background-color:#554777" src = "assets/iterator_geeks.jpg"> |
| 172 | + |
| 173 | + |
| 174 | +```dart |
| 175 | +
|
| 176 | +// Employee class |
| 177 | +class Employee { |
| 178 | + final String _name; |
| 179 | + final double _salary; |
| 180 | +
|
| 181 | + Employee(this._name, this._salary); |
| 182 | +
|
| 183 | + double getSalary() => _salary; |
| 184 | + String getName() => _name; |
| 185 | +} |
| 186 | +
|
| 187 | +/// Iterator interface |
| 188 | +abstract interface class Iterator<T> { |
| 189 | + bool hasNext(); |
| 190 | + T next(); |
| 191 | +} |
| 192 | +
|
| 193 | +/// Aggregate interface |
| 194 | +abstract interface class Aggregate<T> { |
| 195 | + Iterator<T> createIterator(); |
| 196 | +} |
| 197 | +
|
| 198 | +/// Concrete Iterator |
| 199 | +class EmployeeIterator implements Iterator<Employee> { |
| 200 | + int _currentIndex = 0; |
| 201 | + final List<Employee> _employees; |
| 202 | +
|
| 203 | + EmployeeIterator(List<Employee> employees) : _employees = employees; |
| 204 | +
|
| 205 | + @override |
| 206 | + bool hasNext() { |
| 207 | + return _currentIndex < _employees.length; |
| 208 | + } |
| 209 | +
|
| 210 | + @override |
| 211 | + Employee next() { |
| 212 | + if (!hasNext()) { |
| 213 | + throw Exception("No such element"); |
| 214 | + } |
| 215 | + return _employees[_currentIndex++]; |
| 216 | + } |
| 217 | +} |
| 218 | +
|
| 219 | +// Concrete Aggregate |
| 220 | +class Company implements Aggregate<Employee> { |
| 221 | + final List<Employee> _employees; |
| 222 | +
|
| 223 | + Company(List<Employee> employees) : _employees = employees; |
| 224 | +
|
| 225 | + @override |
| 226 | + Iterator<Employee> createIterator() { |
| 227 | + return EmployeeIterator(_employees); |
| 228 | + } |
| 229 | +} |
| 230 | +
|
| 231 | +void main() { |
| 232 | + List<Employee> employees = []; |
| 233 | + employees.add(Employee("Alice", 50000)); |
| 234 | + employees.add(Employee("Bob", 60000)); |
| 235 | + employees.add(Employee("Charlie", 70000)); |
| 236 | +
|
| 237 | + Company company = Company(employees); |
| 238 | + Iterator<Employee> iterator = company.createIterator(); |
| 239 | +
|
| 240 | + double totalSalary = 0; |
| 241 | + while (iterator.hasNext()) { |
| 242 | + totalSalary += iterator.next().getSalary(); |
| 243 | + } |
| 244 | +
|
| 245 | + print("Total salary: $totalSalary"); |
| 246 | +} |
| 247 | +
|
| 248 | +/// Total salary: 180000.0 |
| 249 | +
|
| 250 | +``` |
| 251 | + |
| 252 | +### Social Example [Hard] |
| 253 | + |
| 254 | +- Source: [refactoring.guru java example](https://refactoring.guru/design-patterns/iterator/java/example) |
| 255 | +- Dart Code: [link](social_example.dart) |
| 256 | + |
| 257 | + |
| 258 | +## When to **not** use Iterator Design Pattern |
| 259 | + |
| 260 | +1. **When the collection is not accessed sequentially**: If the collection is not accessed sequentially, using the Iterator pattern may add unnecessary complexity. |
| 261 | +2. **When the collection structure is fixed**: If the structure of the collection is fixed and unlikely to change, using the Iterator pattern may be overkill. Direct access methods may be more appropriate and simpler to implement. |
| 262 | +3. **When performance is critical**: In performance-critical applications, the overhead of using iterators may be significant, especially if the collection is large. In such cases, consider direct access methods for better performance. |
| 263 | +4. **When the language provides better alternatives**: Some languages provide built-in constructs or libraries that offer more efficient ways to iterate over collections. |
| 264 | + |
| 265 | + |
| 266 | + |
| 267 | + |
| 268 | + |
| 269 | +## Summery |
| 270 | + |
| 271 | +<img style="background-color:#554777" src = "assets/iterator_structure.png"> |
| 272 | + |
| 273 | +- The main idea of the Iterator pattern is to extract the traversal behavior of a collection into a separate object called an `iterator`. |
| 274 | + |
| 275 | + |
| 276 | +## Sources |
| 277 | + |
| 278 | +- https://www.geeksforgeeks.org/iterator-pattern/ |
| 279 | +- https://refactoring.guru/design-patterns/iterator |
| 280 | +- https://www.tutorialspoint.com/design_pattern/iterator_pattern.htm |
| 281 | + |
| 282 | + |
| 283 | +<p align="right">(<a href="#top">back to top</a>)</p> |
0 commit comments