Software Developers
The Iterator and Visitor design are patterns generally used to traverse collections or handle object tree navigation.
This means both actual trees or graphs such as XML documents, or inheritance tree navigation where we have a reference of a base class and want to handle a specific concrete type.
Sometimes we just want to select the concrete type when all we have is a base type reference, the visitor design pattern can help with this as well since we get the concrete instance type where we can perform specific operations. No control statements such as if or switch are used.
Iterators
This is probably extremely known, and probably even more used.
The iterator design pattern consists of two interfaces. One that provides iterator instances, a factory method, or a strategy in itself. And the second one which is the iterator itself.
The iterator is responsible for moving across a collection, item by item, by exposing a method that allows it to advance and another that gets the current item. All of this without giving away the details of how the collection is implemented.
This allows us to go over collections without knowing how they are implemented. Arrays, vectors, linked lists, and hash tables, they all look the same from this perspective.
Frameworks and languages provide iterator interfaces that allow instances implementing them to be used in foreach loops (e.g.: IEnumerable, see https://learn.microsoft.com/dotnet/csharp/iterators for more details).
We can even define general purpose methods based on this core interface and have all collections, future and present alike, be usable with them (e.g.: LINQ, see https://learn.microsoft.com/dotnet/csharp/linq for more details).
Visitors
Notice how iterators are great at processing collections of items of the same type. What happens when we need to go over a graph of objects that are of different types?
This is typical when we have a hierarchy of types that references one another to model nesting of different elements.
Markup languages, such as Markdown, are a good example of this.
In Markdown we have text, _emphasised text_ which contains plain text, we can mix **strong** text in there as well.
Besides this, we have paragraphs, lists, and tables that can contain different text elements. And on top of this, we have blockquotes which can contain all the above including other blockquotes!
This can be modeled using a hierarchy of classes that describes both the concrete elements of our markup language as well as the categories they belong to.
All text elements are inline elements, but an “InlineElement” class is most likely abstract as it is not enough on its own to represent anything specifically. “InilineElements” are plain text, emphasized text, hyperlinks, and so on.
Block elements, such as paragraphs, can contain inline elements, but not other block elements. This is where the abstractions come into play as we use them to describe the types of elements another can contain without being too specific.
When we define a new inline element then all paragraphs will be able to contain them implicitly.
The Problem
The problem now becomes traversing these objects. How can we know that the element we are currently processing is plain text, or a paragraph, or a list? We somehow got the objects by parsing a text file, but now we want to generate HTML out of it. How can we do this in a clean and type-safe way?
We can use pattern matching and test for each type in a big switch statement or multiple if statements. We could expose an “ElementType” property which has a unique value, preferably an enum, for each concrete element type in our hierarchy.
Both solutions are valid; however, the resulting code is not exactly pretty, it may be difficult to maintain as we add more concrete types. The compiler may not even tell us that we are missing a few cases which can lead to bugs.
From Data to Objects
This is where we stop thinking in terms of data, and start thinking in terms of behavior, in terms of objects.
- What if each object was able to tell us what it is by calling a specific method on a different object?
- What if this different object is a specific implementation that we provide?
- What if we skip the switches and use polymorphism? Wouldn’t that be a much better solution?
Object and Inheritance Chain Navigation
The visitor design pattern solves this very issue, when we need object tree navigation or hierarchy chain navigation we can apply this pattern and have a clean and extensible solution to our problem. No control statements are required and we can get full support of our compiler to handle all cases at all times.
Each of our objects that model the markup language exposes a method that we shall name “Accept” and takes just one parameter, an object that implements “MarkupVisitor”. As long as it ends in “Visitor” it will clearly indicate what we are doing here.
Our “MarkupVisitor” exposes methods for each concrete type of our “MarkupElement”. Actually the “MarkupElement” exposes an abstract method called “Accept”. Each concrete implementation of “MarkupElement” will call the corresponding “Visit” method exposed by a “MarkupVisitor”. For instance, the “PlainTextMarkupElement” will call “VisitPlainText”, passing itself as an argument to the method call, on the “MarkupVisitor” effectively selecting the appropriate method. The visitor implementation will handle that particular case.
From our perspective, all objects that model different elements in our Markup language are “MarkupElements”, they all expose an “Accept” method to which we provide our concrete visitor. In turn, the visitor will handle all cases and even more so, handle the traversal of the object graph.
For instance, when the “VisitParagraph” method gets called on our “HtmlWriterMarkupVisitor” we will write “<p>” to the output and then go over each inline element and have it accept our visitor (the current instance). Each concrete inline element will select the appropriate visitor method the same way. Once we’ve gone through them all, we will write “</p>” to the output and the generated HTML fragment should be valid.
Later on, if we need to generate a Word document from our markup elements, we will simply implement a different visitor that does exactly that.
Whenever we add a new concrete type of element, we will need to declare a corresponding method on our visitor. The compiler will make sure to tell us about not handling that case.
I first learned about the visitor design pattern when optimizing Entity Framework queries which use LINQ Expressions. The entire query, and more, in .NET, can be represented through Expression objects, Entity Framework uses visitors to generate a SQL query out of these expressions, this is another example of how powerful this pattern can be.
A Concrete Example, Grids
I’ve written about MVVM previously and how I consider it to be a good alternative to Redux and Flux-based architectures in general. MVVM to Flux and Back again is about my general experience with the two while Model-View-ViewModel in ReactJS presents how I’ve thought about applying MVVM in ReactJS.
So far, MVVM has helped us on the project with some performance improvements as well as type-safety. We clearly know how our data looks like and what our ViewModels can do for us.
Going forward, I’ve applied MVVM for our grids. Similar to forms, you define your grid structure using ViewModels, more specifically by using a “GridViewModel” which is composed of several “GridColumnViewModel” implementations. One for each type of data a column contains.
For instance, text columns are displayed as is, while number and date time columns are formatted. Action columns, rows that contain buttons such as edit or delete, display buttons for each action that the column defines.
I’ve used the visitor pattern to map each type of column accordingly. This allowed me to have different properties, where needed, for some columns but not for others. For instance, the “ActionsGridColumnViewModel” has an “actions” property which the others do not have.
The usage of the visitor design pattern, in this case, can be argued. However, another example where it solved a problem for me was when I modeled the filters. Depending on the column we have, we may configure different filters.
Modeling Filters
A text column displays text indeed. However, the filter options on a column can be just plain text with all the basic operators, or we can select items from a dropdown.
The column itself displays text, but it is the name of a related entity. When I filter by that column, I want to be able to select which related entities I want to filter by. In other cases, it can be the description where I do not have options to pick from.
In case of columns displaying users, I want to be able to select the current user as a filter option. I want to see which tasks are assigned to me or for which entries I am the contact person.
All these different types of filters need to be configurable for each individual column. This is something that a base interface would easily cover. Each concrete type of filter needs to be easily selectable and mapped to a specific UI component.
Visiting Filters
I’ve spent some time thinking on how to resolve this until I came to the, probably unsurprising, conclusion. This is a hierarchy tree navigation issue, which is solved using the visitor design pattern. This is when I thought about writing this article as well.
The base “IGridColumnViewModelFilter” exposes just one method, the “acceptVisitor” method, and the corresponding “IGridColumnViewModelFilterVisitor” interface exposes a method for each concrete type of filter.
When I’m rendering the grid filter component I am first checking if there is one. If we don’t have one it would make the column unfilterable. Otherwise, I use a visitor that for each method it gets me a concrete component for that filter.
I’m not really interested in traversing an object graph with this approach. I only want to select the concrete type and map it to a UI component. The visitor pattern does this well, same as before, when I add a new type of filter the compiler will make sure to tell me about it.
Conclusions
Grids are good examples for using these design patterns.
We use the iterator design pattern to go over the columns that we have defined, or are visible. This is the easy part.
We use the visitor design pattern to handle concrete column types and filters effectively resolving both object tree navigation and inheritance chain navigation.
This will maintain our code clean and any addition of concrete column types or filter types will automatically generate compilation errors because we have declared new methods on our visitors. We will know exactly where we need to handle the new functionality without the risk of forgetting about that one place which will lead to runtime errors and bugs.