Why we invested in Comet

Future of work is a big trend as younger generations have started to question the employee status as we know it today. Being your own boss and having the capacity to organize your work with…

Smartphone

独家优惠奖金 100% 高达 1 BTC + 180 免费旋转




Iterator design pattern

Think about the first ever programming language you learnt- it could be C, C++, Java, Python or anything else. Regardless of what the first language was, one programming construct that was omnipresent, was the loop. Be it the project we are working on, or a solution to a DSA problem, looping almost always comes in.

We explore the Iteration design pattern here. We will understand why a design pattern for iteration exists in the first place, what is it, and how it takes shape.

Looping cannot exist without a data structure to iterate upon- be it an array, a hashmap or any other data structure. As much as looping forms the common programming construct for them, the way looping is performed is quite dependent on the specific data structure.

For a code that gets an array, and then loops through it with some business logic, the code is going to be like:

While for an arraylist, it is going to be:

Note that the difference is not about using a length function for an array vs a size() function for an arraylist- that can be standardized anyway through a ‘for loop’. But the fact, that the caller code above needs to know whether the data structure is an array or an arraylist, and then needs to store and use it accordingly(myArray of type array vs myArraylist of type arraylist), is what the issue is. What happens when the data structure is changed- from an array to a queue maybe? The caller function’s handling changes again. This introduces maintenance issues.

It is this situation that we will use to build the iteration design pattern upon.

Let’s say there is a travel app, which aggregates flights from various airlines- Indigo and GoFirst for instance. That means it has to call the APIs of the respective airlines. As a developer of the travel app, would the dev, or rather, should the dev be concerned about how the airline API implements its collection of flights? What happens if the airline decides to change that implementation?

Let’s see the code below:

See the Indigo and GoFirst classes above. They contain the indigoFlights and the goFirstFlights collections. Of our interest is the getIndigoFlights() and the getGoFirstFlights() methods, which return the respective flights collection. We see how they are used below:

The TravelSiteWithoutIterator class above contains Indigo and GoFirst . It calls the getIndigoFlights() and the getGoFirstFlights() of these classes defined above. Notice the type of the variables used to hold them. The indigoFlights is an Arraylist, while goFirstFlights is an array. That means the calling function has to be cognizant of the collection it is calling. Comes a third flight tomorrow with a different implementation, or a change in an existing implementation, the types of these variables will undergo a change.

This is the issue that the Iterator design pattern addresses, and is also the essence of the pattern.

Ideally, the calling class TravelSiteWithoutIterator should be agnostic to the way the Indigo flight collection or the GoFirst flight collection is implemented. There should be a layer of abstraction that contains these details and changes, and frees the calling class from worrying about them.

We look at the problem through this lens, and revisit and make changes to the Indigo and GoFirst classes.

See the code above. We remove the getIndigoFlights() and getGoFirstFlights() functions, and introduce a function called getIterator() in both, that returns a type called FlightIterator. We explore these types below.

FlightIterator is a generic interface, a type which the getIterator() function returns above. FlightIterator is implemented below:

Both the IndigoFlightsIterator and GoFirstFlightsIterator contain a collection of indigoFlights and goFirstFlights. They implement next() and the hasNext() methods, which are pretty self-explanatory. Note that these specific iterators know and handle the specific implementations of the array/arraylist appropriately.

What did we do so far? We made the Indigo and GoFirst return a IndigoFlightsIterator and GoFirstFlightsIterator respectively, both of which implement the FlightIterator, that contains the iterating utility methods hasNext() and next(). The implementations IndigoFlightsIterator and GoFirstFlightsIterator take the respective flight collections. So far, so good. But how does it solve the original issue of the caller function having to worry about the implementation? We see that next.

We implement TravelSiteWithIterator, parallel to TravelSiteWithoutIterator:

We examine the getAllFlights() method again, and understand the changes made to it.

For reference, TravelSiteWithoutIterator had the following lines in getAllFlights():

In contrast, the TravelSiteWithIterator in its getAllFlights() method just gets the indigoFlightsIterator and goFirstFlightsIterator, which are not handled as IndigoFlightsIterator or GoFirstFlightsIterator, but rather as the type(interface) FlightIterator, which both these specific iterators implement. That frees the calling function getAllFlights() of TravelSiteWithIterator from worrying about the specific type of iterator.

What about the specific type of collection- array or arraylist? With the iterator by our side, we just loop in through the collections in a generic way, be it arrays or arraylists, using the hasNext() and next() functions(implemented in their respective iterators IndigoFlightsIterator and GoFirstFlightsIterator). This means the iterators hide the internal implementation details of the collections, specifically in their next() and hasNext() functions. (Look at the IndigoFlightsIterator and GoFirstFlightsIterator again). In addition, the calling function getAllFlights() of TravelSiteWithIterator does not have to worry about the specific iterator types returned in getIterator() method. It saves the type as FlightIterator.

The calling class, TravelSiteWithIterator, need not worry about the internal implementations of the collections and their iteration. The specific iterators IndigoFlightsIterator and GoFirstFlightsIterator take care of the collection specific implementations, and expose generic functions hasNext() and next(), which can be used in a generic way.

Note that the Indigo and GoFirst classes are pretty much the same. The major change was around creating the iterators which they return through their getIterator() functions, which then take care of the internals, and expose a generic iterator to the calling class. That way the calling class TravelSiteWithIterator is now bound only to an iterator of generic interface FlightIterator, that takes care of the collection underneath and its iteration.

The Iterator pattern provides a way to access(or iterate) the elements of an aggregate object(collection) sequentially without exposing its underlying representation.

Add a comment

Related posts:

Broken Silence

For years we stood side by side, going through the motions. We knew something was wrong, but we failed to utter a word. To tell one another of our pains, because we were sick of complaining. We were…

The 4 Scenarios That Would Make Me Buy Another Car

But in terms of a status symbol or something to provide personal enjoyment — truth be told, I don’t give a shit about cars. Although it wouldn’t be absolutely necessary, it would certainly be nice to…

From Cooking Up Delight To The Gospel of Incident Resolution

The compulsory blog post about job change for Matt Stratton going from Chef to PagerDuty.