|
| 1 | +--- |
| 2 | +title: Iterator Pattern |
| 3 | +tags: |
| 4 | + - behavioral |
| 5 | +created: 2025-10-07 |
| 6 | +--- |
| 7 | +## Definition |
| 8 | + |
| 9 | +The Iterator Pattern provides a way to access the elements of an aggregate object sequentially without exposing its underlying representation. |
| 10 | + |
| 11 | +--- |
| 12 | +## Real World Analogy |
| 13 | + |
| 14 | +The **Iterator Pattern** is used to access elements from a collection of objects (like `ArrayList`, `LinkedList`, `Stack`, `Queue`, or a raw `Array`) without exposing the collection's internal structure. Without this pattern, iterating over different collection types would be tedious, often requiring you to write custom access logic (new methods or classes) for _each_ underlying collection type. The Iterator Pattern provides a unified way to access the elements of all these different collections. |
| 15 | + |
| 16 | +Suppose we are at a food court, and we have various food options from different sellers. The twist is that each seller has a different way of implementing their menu internally. For instance, |
| 17 | +- Shop 1's menu is based on an `ArrayList` implementation |
| 18 | +- Shop 2 uses a `LinkedList` |
| 19 | +- Shop 3 uses a raw `Array`, and so on. |
| 20 | + |
| 21 | +To display the full menu to customers, we need a way to combine and iterate over the menus from all the shopkeepers. We'll refer to the component responsible for showing the complete menu as the **Waitress**. The Waitress needs to get the menus from every shop. |
| 22 | + |
| 23 | +Let's focus on two shops: a Dinner shop and a Dessert shop. The **DinnerMenu** uses an `ArrayList` implementation, while the **DessertMenu** uses a fixed-size Java array. Each shop has its own strategy for managing its menu items. |
| 24 | + |
| 25 | +If the Waitress were to iterate over these menus directly, she would need to check the underlying implementation of each menu and use specific iteration logic (e.g., a `for` loop for the array, a different loop for the `ArrayList`). If any new shop arrived with a different underlying collection, we would have to entirely change the implementation of the Waitress. |
| 26 | + |
| 27 | +This is where the **Iterator Pattern** provides a clean solution. Although Java already provides the `Iterator` interface within its Collections Framework (which nearly all built-in collections use), this pattern shows us how to apply this abstraction to custom collections. |
| 28 | + |
| 29 | +For any new shop that comes up with its own unique menu implementation, the shopkeeper just needs to create an **Iterator** for their specific collection type to integrate with the Food Court. The Waitress then only deals with the standard `Iterator` interface. She can easily add the new menu to her list, and the Iterator handles the logic of accessing the objects, regardless of the menu's internal structure. |
| 30 | + |
| 31 | +![[iterator_waitress.png]] _Diagram explaining iteration over different menus._ |
| 32 | + |
| 33 | +---- |
| 34 | +## Design |
| 35 | + |
| 36 | +```mermaid |
| 37 | +classDiagram |
| 38 | + class MenuItem { |
| 39 | + -String Name |
| 40 | + -String Description |
| 41 | + -int Price |
| 42 | + } |
| 43 | +
|
| 44 | + class Menu { |
| 45 | + <<interface>> |
| 46 | + +createIterator() Iterator |
| 47 | + } |
| 48 | +
|
| 49 | + class DinnerMenu { |
| 50 | + -ArrayList~MenuItem~ menuItems |
| 51 | + +addItem(String name, String description, int price) |
| 52 | + +createIterator() Iterator |
| 53 | + } |
| 54 | +
|
| 55 | + class DessertMenu { |
| 56 | + -final int SIZE |
| 57 | + -int numberofItem |
| 58 | + -MenuItem[] items |
| 59 | + +addItem(String name, String description, int price) |
| 60 | + +createIterator() Iterator |
| 61 | + } |
| 62 | +
|
| 63 | + class DessertMenuIterator { |
| 64 | + -MenuItem[] items |
| 65 | + -int position |
| 66 | + +hasNext() boolean |
| 67 | + +next() MenuItem |
| 68 | + +remove() void |
| 69 | + } |
| 70 | +
|
| 71 | + class Waitress { |
| 72 | + -List~Menu~ menus |
| 73 | + +printMenu() void |
| 74 | + -printMenu(Iterator~MenuItem~ iterator) void |
| 75 | + } |
| 76 | +
|
| 77 | + %% Relationships |
| 78 | + Menu <|.. DinnerMenu |
| 79 | + Menu <|.. DessertMenu |
| 80 | + Iterator <|.. DessertMenuIterator |
| 81 | + DessertMenu --> DessertMenuIterator : creates |
| 82 | + DinnerMenu --> MenuItem : contains |
| 83 | + DessertMenu --> MenuItem : contains |
| 84 | + Waitress --> Menu : uses |
| 85 | + |
| 86 | +``` |
| 87 | + |
| 88 | +_Class Diagram for Iterator Pattern._ |
| 89 | + |
| 90 | +## Implementation in Java |
| 91 | + |
| 92 | +To implement the Iterator Pattern, the **Waitress** will now depend on the standard Java `Iterator` interface for accessing collections of **Menus** and **MenuItems** from the various food court shops. |
| 93 | + |
| 94 | +We will use standard Java `List` and `Iterator` interfaces in our implementation. |
| 95 | + |
| 96 | +**MenuItem** is the base class representing a single item on the menu, holding its name, description, and price. |
| 97 | + |
| 98 | +```java title="MenuItem.java" |
| 99 | +import java.util.ArrayList; |
| 100 | +import java.util.Iterator; |
| 101 | +import java.util.List; |
| 102 | + |
| 103 | +class MenuItem { |
| 104 | + private String Name; |
| 105 | + private String Description; |
| 106 | + private int Price; |
| 107 | + |
| 108 | + public MenuItem(String Name, String Description, int Price) { |
| 109 | + this.Description = Description; |
| 110 | + this.Price = Price; |
| 111 | + this.Name = Name; |
| 112 | + } |
| 113 | + |
| 114 | + public String getDescription() { |
| 115 | + return Description; |
| 116 | + } |
| 117 | + |
| 118 | + public String getName() { |
| 119 | + return Name; |
| 120 | + } |
| 121 | + |
| 122 | + public int getPrice() { |
| 123 | + return Price; |
| 124 | + } |
| 125 | +} |
| 126 | +``` |
| 127 | + |
| 128 | +**Menu** interface defines the contract for all menu types. Any concrete menu class must implement this, ensuring it provides a `createIterator()` method to return a standard `Iterator`. |
| 129 | + |
| 130 | +```java title="Menu.java" |
| 131 | +// Creating the Interface for Menu which will return the iterator |
| 132 | +interface Menu { |
| 133 | + public Iterator<MenuItem> createIterator(); |
| 134 | +} |
| 135 | +``` |
| 136 | + |
| 137 | +This Dinner menu uses an `ArrayList` internally to store its items. Since `ArrayList` is a standard Java collection, we can simply return its built-in iterator from the `createIterator()` method. |
| 138 | + |
| 139 | +```java title="DinnerMenu.java" |
| 140 | +// Creating the Menus where it depends on the ArrayList |
| 141 | +// It has the ArrayList as Implementation. |
| 142 | +class DinnerMenu implements Menu { |
| 143 | + private ArrayList<MenuItem> menuItems = new ArrayList<MenuItem>(); |
| 144 | + |
| 145 | + public DinnerMenu() { |
| 146 | + addItem("Paneer Masala", "Curry Paneer Masala", 123); |
| 147 | + addItem("Butter Paneer Masala", "Tadka butter Paneer Masala", 134); |
| 148 | + addItem("Tawa Paneer Masala", "Delicious Paneer in the Tawa", 178); |
| 149 | + } |
| 150 | + |
| 151 | + // Returning the Java Inbuilt iterator. |
| 152 | + @Override |
| 153 | + public Iterator<MenuItem> createIterator() { |
| 154 | + return menuItems.iterator(); |
| 155 | + } |
| 156 | + |
| 157 | + public void addItem(String name, String description, int price) { |
| 158 | + MenuItem menuItem = new MenuItem(name, description, price); |
| 159 | + this.menuItems.add(menuItem); |
| 160 | + } |
| 161 | +} |
| 162 | +``` |
| 163 | + |
| 164 | +Dessert menu uses a simple fixed-size array (`MenuItem[]`) for its internal implementation. Because a raw array does not automatically provide a standard `Iterator`, we must create a custom iterator specifically for this collection type. |
| 165 | + |
| 166 | +```java title="DessertMenu.java" |
| 167 | +//Creating the another menu which implements the array so for it we need to create our custom iterator |
| 168 | +class DessertMenu implements Menu { |
| 169 | + private final int SIZE = 3; |
| 170 | + private int numberofItem = 0; |
| 171 | + private MenuItem[] items = new MenuItem[SIZE]; |
| 172 | + |
| 173 | + public DessertMenu() { |
| 174 | + addItem("Gulab Jamun", "Sweet and Juicy ", 89); |
| 175 | + addItem("Rasmalai", "Pure Milk Rasmalai", 350); |
| 176 | + addItem("Rabdi", "Pure and Fresh Milk Rabdi", 230); |
| 177 | + } |
| 178 | + |
| 179 | + public void addItem(String name, String description, int price) { |
| 180 | + MenuItem menuItem = new MenuItem(name, description, price); |
| 181 | + if (numberofItem < SIZE) { |
| 182 | + items[numberofItem++] = menuItem; |
| 183 | + } else { |
| 184 | + System.err.println("Number of Items Reached"); |
| 185 | + } |
| 186 | + } |
| 187 | + |
| 188 | + // For this array-based menu, we return an instance of our custom iterator. |
| 189 | + @Override |
| 190 | + public Iterator<MenuItem> createIterator() { |
| 191 | + return new DessertMenuIterator(this.items); |
| 192 | + } |
| 193 | +} |
| 194 | +``` |
| 195 | + |
| 196 | +**DessertMenuIterator** is the **custom iterator** created specifically to traverse the raw `MenuItem[]` array used by the `DessertMenu`. It implements the standard `Iterator<MenuItem>` interface, providing uniform access methods like `hasNext()` and `next()`. |
| 197 | + |
| 198 | +```java title="DessertMenuIterator.java" |
| 199 | +// Creating our custom iterator using the java Iterator |
| 200 | +class DessertMenuIterator implements Iterator<MenuItem> { |
| 201 | + private MenuItem[] items; |
| 202 | + private int position = 0; |
| 203 | + |
| 204 | + public DessertMenuIterator(MenuItem[] menuItems) { |
| 205 | + this.items = menuItems; |
| 206 | + } |
| 207 | + |
| 208 | + @Override |
| 209 | + public boolean hasNext() { |
| 210 | + // Check if the current position is within the bounds of the array and the item is not null |
| 211 | + return position < items.length && items[position] != null; |
| 212 | + } |
| 213 | + |
| 214 | + @Override |
| 215 | + public MenuItem next() { |
| 216 | + // Return the current item and advance the position |
| 217 | + return this.items[position++]; |
| 218 | + } |
| 219 | + |
| 220 | + @Override |
| 221 | + public void remove() { |
| 222 | + throw new UnsupportedOperationException("This method is not implemented for the DessertMenuIterator"); |
| 223 | + } |
| 224 | +} |
| 225 | +``` |
| 226 | + |
| 227 | +The `Waitress` class is the **client** that depends only on the high-level `Menu` interface and the standard `Iterator` interface. This allows her to work with any menu implementation without knowing or caring if the menu uses an `ArrayList`, a raw `Array`, or any other collection internally. |
| 228 | + |
| 229 | +```java title="Waitress.java" |
| 230 | +// Implementing the Waitress Code |
| 231 | +class Waitress { |
| 232 | + private List<Menu> menus; |
| 233 | + |
| 234 | + public Waitress(List<Menu> menu) { |
| 235 | + this.menus = menu; |
| 236 | + } |
| 237 | + |
| 238 | + public void printMenu() { |
| 239 | + // Loop through all the menus provided |
| 240 | + for (Menu currentmenu : menus) { |
| 241 | + System.out.println("\n--- " + currentmenu.getClass().getSimpleName() + " Menu ---"); |
| 242 | + // Ask the menu to create its appropriate iterator |
| 243 | + Iterator<MenuItem> iterator = currentmenu.createIterator(); |
| 244 | + // Pass the standard iterator to the printing method |
| 245 | + printMenu(iterator); |
| 246 | + } |
| 247 | + } |
| 248 | + |
| 249 | + // Printing all the Menu items present in the Menus using the Iterator |
| 250 | + private void printMenu(Iterator<MenuItem> iterator) { |
| 251 | + // The iteration logic is now uniform regardless of the underlying collection type |
| 252 | + while (iterator.hasNext()) { |
| 253 | + MenuItem menuItem = iterator.next(); |
| 254 | + System.out.print(menuItem.getName() + ", "); |
| 255 | + System.out.print("$" + menuItem.getPrice() + " --- "); |
| 256 | + System.out.println(menuItem.getDescription()); |
| 257 | + } |
| 258 | + } |
| 259 | +} |
| 260 | +``` |
| 261 | + |
| 262 | +This is the main class where we demonstrate the pattern. We create instances of different menu types and pass them to the `Waitress`. The Waitress successfully prints both menus using a single, unified iteration logic defined by the `Iterator` interface. |
| 263 | + |
| 264 | +```java title="IteratorPatternDemo.java" |
| 265 | +public class IteratorPatternDemo { |
| 266 | + public static void main(String[] args) { |
| 267 | +// Initiating the menu classes |
| 268 | + Menu desertMenu = new DessertMenu(); |
| 269 | + Menu dinnerMenu = new DinnerMenu(); |
| 270 | + |
| 271 | +// The Waitress accepts a list of Menu objects, regardless of their internal implementation. |
| 272 | + Waitress waitress = new Waitress(new ArrayList<Menu>() { |
| 273 | + { |
| 274 | + add(desertMenu); |
| 275 | + add(dinnerMenu); |
| 276 | + } |
| 277 | + }); |
| 278 | + |
| 279 | + waitress.printMenu(); |
| 280 | + } |
| 281 | +} |
| 282 | +``` |
| 283 | + |
| 284 | +Now, whenever any new Menu Arrives, the developer only needs to create the Menu using the `Menu` interface and implement its specific `createIterator()` method. The new Menu is then simply added to the `ArrayList` of the Waitress, and the existing iteration logic remains unchanged. This is the power of the **Iterator Pattern**: decoupling the client (Waitress) from the collection structure (Menu implementation). |
| 285 | + |
| 286 | +**Output:** |
| 287 | +```txt |
| 288 | +DessertMenu == |
| 289 | +Gulab Jamun, 89 --- Sweet and Juicy |
| 290 | +Rasmalai, 350 --- Pure Milk Rasmalai |
| 291 | +Rabdi, 230 --- Pure and Fresh Milk Rabdi |
| 292 | +
|
| 293 | +DinnerMenu == |
| 294 | +Paneer Masala, 123 --- Curry Paneer Masala |
| 295 | +Butter Paneer Masala, 134 --- Tadka butter Paneer Masala |
| 296 | +Tawa Paneer Masala, 178 --- Delicious Paneer in the Tawa |
| 297 | +``` |
| 298 | +These is the output for the `IteratorpatternDemo`class. |
| 299 | + |
| 300 | +--- |
| 301 | +## Real World Example |
| 302 | + |
| 303 | +The Iterator Pattern is used in almost every application where element iteration is required. Key examples include: |
| 304 | +- **Java Collections Framework:** All core collection classes (like `ArrayList`, `HashSet`, `HashMap` keys/values) implement the `Iterable` interface, which provides the `iterator()` method. |
| 305 | +- **JDBC:** Iterators are implicitly used when reading result sets from a `SELECT` query. |
| 306 | +- **I/O buffers:** Iteration is used when sequentially reading data from files or streams. |
| 307 | + |
| 308 | +---- |
| 309 | +## Design Principles: |
| 310 | + |
| 311 | +- **Encapsulate What Varies** - Identify the parts of the code that are going to change and encapsulate them into separate class just like the Strategy Pattern. |
| 312 | +- **Favor Composition Over Inheritance** - Instead of using inheritance on extending functionality, rather use composition by delegating behavior to other objects. |
| 313 | +- **Program to Interface not Implementations** - Write code that depends on Abstractions or Interfaces rather than Concrete Classes. |
| 314 | +- **Strive for Loosely coupled design between objects that interact** - When implementing a class, avoid tightly coupled classes. Instead, use loosely coupled objects by leveraging abstractions and interfaces. This approach ensures that the class does not heavily depend on other classes. |
| 315 | +- **Classes Should be Open for Extension But closed for Modification** - Design your classes so you can extend their behavior without altering their existing, stable code. |
| 316 | +- **Depend on Abstractions, Do not depend on concrete class** - Rely on interfaces or abstract types instead of concrete classes so you can swap implementations without altering client code. |
| 317 | +- **Talk Only To Your Friends** - An object may only call methods on itself, its direct components, parameters passed in, or objects it creates. |
| 318 | +- **Don't call us, we'll call you** - This means the framework controls the flow of execution, not the user’s code (Inversion of Control). |
| 319 | +- **A class should have only one reason to change** - This emphasizes the Single Responsibility Principle, ensuring each class focuses on just one functionality. |
0 commit comments