GoF design patterns in Scala: Iterator (and why it is still cool in 2019)
Continuing with this series of “Gang of Four Design patterns”, today we’ll take a look to Iterator. At first I thought that it didn’t really make sense to create this article, as every data structure in Scala implements ways of iterating over its elements one way or another. However, a careful read on the pattern taught me otherwise.
But what is Iterator exactly about? Well, the intent of the Iterator design pattern is to:
Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.
From my point of view (and the books, I am not so smart) this has two important usages today:
- define some way of traversing your own data structure
- create different ways of traversing the existing ones.
It is important to remark that, according to Iterator pattern, the idea is not to create and endless set of access functions in the data structure to be accessed but to encapsulate them on an external singleton. Scala is really suitable for that: data is modeled with a case class, and then we have a companion object with the access methods completely separated from the data structures.
Let’s see some examples of this.
This will be our data structure. It is pretty straightforward, just a basic tree implementation. But how can we perform typical operations like a map? Well, according the iterator pattern, we isolate the map function on a separated singleton.
And that’s it! Now we can do things like
Now, let’s do some funny stuff!
From my point of view, the key about Iterator is to be able to create different ways of accessing and going through the data structure. This can be useful in many ways. For example, if we wanted to create different representations of the same tree, we could have this
As you can see, with Scala is straightforward to implement different ways of accessing a data structure, encapsulate them into a singleton and use them al along your application.
A this point, you could say “ok, I’m glad you are having a good time with your new ADTs, but I will probably just use the existing data structures so I don’t really care”. But hey! You can probably be using Iterator pattern even without knowing.
For example, you have a HashMap storing some stuff in your code, but at some point you realize that you need to print the elements in a particular order. How can you implement this?
Note that toOrderedString is receiving an implicit ordering function, as it is required by the sortBy function. This means that this implementation will crash on compilation time if the method is used on a type not implementing Ordering, like our previous SimpleTree. The rest of the implementation is quite simple.
And to finish this article, on a slightly more complex example we will create a foreach function that goes through the data structure in a deterministic way decided on execution time
As you can see, Iterator pattern is present on many of the usual ways of programming in Scala.