Implementation

First of all, to understand the implementation part better, you should be familiar with the tree data structure. If you are not following the series, I have explained it when analysing the Composite design pattern, but if you have already read this article — 10 points for Gryffindor!

This time we will investigate one of the fundamental parts in graph theory — the graph traversal. The graph traversal is defined as:

In computer science, graph traversal (also known as graph search) refers to the process of visiting (checking and/or updating) each vertex in a graph. Such traversals are classified by the order in which the vertices are visited. Tree traversal is a special case of graph traversal.

This time, we will focus only on the tree traversal case. There are several graph/tree traversal algorithms available out there:

Depth-first search (DFS) — visits the child vertices before visiting the sibling vertices; that is, it traverses the depth of any particular path before exploring its breadth;

— visits the child vertices before visiting the sibling vertices; that is, it traverses the depth of any particular path before exploring its breadth; Breadth-first search (BFS) — visits the sibling vertices before visiting the child vertices.

BFS vs DFS (source)

Let’s say you want to experiment with the tree traversal using different algorithms. Furthermore, the visualization of these algorithms should be provided to the UI where you can switch between different algorithms easily, but use the same tree data structure for both of them. Also, you want to hide the implementation details of how the tree data structure should be iterated for each algorithm. To implement this, the Iterator design pattern is an obvious choice.

Class diagram

The class diagram below shows the implementation of the Iterator design pattern:

Class Diagram — Implementation of the Iterator design pattern

ITreeCollection defines a common interface for all the specific tree collections:

createIterator() — creates an iterator for the specific tree collection;

getTitle() — returns the title of the tree collection which is used in the UI.

DepthFirstTreeCollection and BreadthFirstTreeCollection are concrete implementations of the ITreeCollection interface. DepthFirstTreeCollection creates the DepthFirstIterator while BreadthFirstTreeCollection creates the BreadthFirstIterator. Also, both of these collections stores the Graph object to save the tree data structure itself.

ITreeIterator defines a common interface for all specific iterators of the tree collection:

hasNext() — returns true if the iterator did not reach the end of the collection yet, otherwise false;

getNext() — returns the next value of the collection;

reset() — resets the iterator and sets the current position of it to the beginning.

DepthFirstIterator and BreadthFirstIterator are concrete implementations of the ITreeIterator interface. DepthFirstIterator implements the depth-first algorithm to traverse the tree collection. Correspondingly, BreadthFirstIterator implements the breadth-first algorithm. The main difference between these two algorithms is the order in which all of the nodes are visited. Hence, the depth-first algorithm is implemented using the stack data structure while the breadth-first algorithm uses the queue data structure to store nodes (vertices) which should be visited next.

IteratorExample references both interfaces — ITreeCollection and ITreeIterator — to specify the required tree collection and create an appropriate iterator for it.

Graph

A class which stores the adjacency list of the graph. It is stored as a map data structure where the key represents the node’s (vertex) id and the value is a list of vertices (ids of other nodes) adjacent to the vertex of that id (key). Also, this class defines the addEdge() method to add an edge to the adjacency list.

ITreeCollection

An interface which defines methods to be implemented by all specific tree collection classes. Dart language does not support the interface as a class type, so we define an interface by creating an abstract class and providing a method header (name, return type, parameters) without the default implementation.

Tree collections

DepthFirstTreeCollection — a tree collection class which stores the graph object and implements the createIterator() method to create an iterator which uses the depth-first algorithm to traverse the graph.

BreadthFirstTreeCollection — a tree collection class which stores the graph object and implements the createIterator() method to create an iterator which uses the breadth-first algorithm to traverse the graph.

ITreeIterator

An interface which defines methods to be implemented by all specific iterators of the tree collection.

Tree iterators

DepthFirstIterator — a specific implementation of the tree iterator which traverses the tree collection by using the depth-first algorithm. This algorithm uses the stack data structure to store vertices (nodes) which should be visited next using the getNext() method.

BreadthFirstIterator — a specific implementation of the tree iterator which traverses the tree collection by using the breadth-first algorithm. This algorithm uses the queue data structure to store vertices (nodes) which should be visited next using the getNext() method.

Example

First of all, a markdown file is prepared and provided as a pattern’s description:

IteratorExample widget is responsible for building the tree (graph) using the Graph class and contains a list of tree collection objects. After selecting the specific tree collection from the list and triggering the traverseTree() method, an appropriate iterator of that particular tree collection is created and used to traverse the tree data structure.

As you can see in the traverseTree() method, all the implementation details of the tree collection’s traversal are hidden from the client, it only uses the hasNext() and getNext() methods defined by the ITreeIterator interface to iterate through all of the vertices (nodes) of the built Graph object (tree).

The final result of the Iterator design pattern’s implementation looks like this:

As you can see in the example, by selecting the specific tree collection and creating its iterator, the algorithm to traverse the tree also changes and it is visually noticeable in the demonstration.

All of the code changes for the Iterator design pattern and its example implementation could be found here.