I just learnt a new design from the Head First Design Pattern book. Today, I learnt about the Iterator pattern.
According to the head-first design pattern, the Iterator pattern is a pattern that allows you to access the elements of a collection sequentially without exposing its underlying representation. It allows you to extract the transversal logic of collections. It does so in a way that it makes transversing elements of any collection easier.
Implementing the Iterator is pretty straightforward
- Define an iterator interface
interface Iterator<out T> {
fun hasNext() : Boolean
fun next() : T
}
The Iterator interface below has two methods. The hasNext
and the next
. The hasNext
is used to check if the collection has items remaining while the next
is used to get the next item from the collection. Instead of creating your implementation of the Iterator interface, you can also use the Iterator interface in Java.
2.Create a class that implements the Iterator interface. As you can see they all contain different kinds of collections. Without the Iterator pattern, you'll have to transverse each collection separately.
class MangaIterator(val mangas: List<Manga>) : Iterator<Manga> {
var position: Int = 0
private set
override fun hasNext(): Boolean {
return if (position >= mangas.size || mangas.getOrNull(position) == null) false else true
}
override fun next(): Manga {
val manga = mangas[position]
position += 1
return manga
}
}
class AnimeIterator(val animes: Array<Anime>) : Iterator<Anime>{
var position: Int = 0
private set
override fun hasNext(): Boolean {
return if (position >= animes.size || animes.getOrNull(position) == null) false else true
}
override fun next(): Anime {
val manga = animes[position]
position += 1
return manga
}
}
class NovelIterator(val novels: HashMap<String, Novel>): Iterator<Novel> {
var position: Int = 0
private set
override fun hasNext(): Boolean {
return if (position >= novels.values.size || novels.values.elementAt(position) == null) false else true
}
override fun next(): Novel {
val novel = novels.values.elementAt(position)
position += 1
return novel
}
}
class TrackerApp() {
fun <T> print(iterator: Iterator<T>) {
while (iterator.hasNext()) {
println(iterator.next())
}
}
}
fun main() {
val manga = listOf<Manga>(
Manga("Relife", "Slice of Life"),
Manga("Koe no katachi", "Slice of Life"),
Manga("Vagabond", "Action")
)
val anime = arrayOf(Anime("Konosuba", "Comedy"), Anime("Miss Koyobashi's Dragon maid", "Comedy"))
val novels =
hashMapOf<String, Novel>("Markus Zusak" to Novel("The book thief"), "Brandon Sanderson" to Novel("Mistborn"))
val mangaIterator = MangaIterator(manga)
val animeIterator = AnimeIterator(anime)
val novelIterator = NovelIterator(novels)
val trackerApp = TrackerApp()
trackerApp.print(mangaIterator)
trackerApp.print(animeIterator)
trackerApp.print(novelIterator)
Manga(name=Relife, genre=Slice of Life)
Manga(name=Koe no katachi, genre=Slice of Life)
Manga(name=Vagabond, genre=Action)
Anime(name=Konosuba, genre=Comedy)
Anime(name=Miss Koyobashi's Dragon maid, genre=Comedy)
Novel(name=The book thief)
Novel(name=Mistborn)
}
Since the logic for transversing the collection has been encapsulated within an Iterator class. The TrackerApp can traverse any kind of collection so fat they implement the Iterator interface.
The Iterator pattern shines when you have transverse different kinds of collections.
Top comments (0)