Making Visitor Pattern Obsolete using Swift

The visitor pattern is a design pattern from the infamous Gang of Four, which I’ve chanced upon using a number of times to solve some tricky problems. It mostly solved the problem, but I can’t say I ever liked the visitor design pattern.

It creates many of its own problems which you can solve by creating an even more complicated visitor pattern. This is one of my issues with the pattern. It quickly gets complicated and I hate writing complex code.

Lets look at some cases, which I solved in C++ using a visitor pattern and how we can completely avoid using a visitor when writing the same code using Swift.

Predicate Editor

A predicate is an operator or function which returns a boolean value. Below is a predicate combining two expression with a comparison operator:

a + 3 > 4 - 2b

In memory we can represent this as a tree of objects. Each color here represent a different category of objects or class.

On Mac OS X there are many examples of applications which support the creation of a predicate through a GUI editor. An example is smart playlists in iTunes.

Imagine we want to create such an editor in C++. So we need a GUI which allows us to compose a predicate. Once we got the predicate we might want to filter a list of MP3 songs to find the ones which can be categorized as 90s music.

Lets just consider something simpler. How would we express a predicate for 25 most played songs? Below is a way iTunes does it.

So lets narrow it down to the predicate:

plays > 0

In C++ code we could express this as:

Expression *plays = AttributeExpression("plays");
Expression *zero = ConstantExpression(0);
Predicate *pred = LargerThanPredicate(plays, zero);
playedsongs = allsongs.filter(pred);

So to use predicates we deal with Expression and Predicate objects. However to build predicates using a GUI we need different objects to represent the GUI of the individual rows.

We could have a PredEditorRow class to represent the GUI for each row. However there are many different kinds of rows. Predicates can be arbitrarily nested so we need composite rows.

Let us revisit our first music smart list. The 90’s Music predicate would look something like this:

1990 < year < 1999 && (mediakind == "music" || mediakind == "music video")

So the year range could be represented by a PredEditorLeafRow and the “media kind” rows must have a parent object which could be a PredEditorCompositeRow.

So we represent the predicate editor with a composite design pattern. The rightmost part of the row will represent some form of expression we are comparing an attribute to. That could be represented as simply a number or string in a textfield. But it could also be a list of legal values, a color from a palette etc. So to handle the needed flexibility we could use a sort of strategy pattern. In our diagram we have represented it as a ExpressionEditor. So imagine each row points to an expression editor which can be swapped for another one. The details of this is not important as we are mainly interested in the implementation of the visitor pattern. This is just to give a rough idea.

Visitor

The challenge is given a tree of objects representing a predicate how do we turn this into a tree of GUI elements for a predicate editor. This problem would arise if we e.g. read a predicate off disk and want to display an editor for it.

Such a tree of objects will not be homogenous because it will not only consist of both expression and predicate objects which would again have their own subclasses. Expression can be subdivided into e.g. attribute expression and constants. Since it is not homogenous, there is no easy way to traverse such a structure in a generic manner.

This is why a visitor is helpful. We can design our expression and predicate classes to accept visitors of type PredVisitor. A PredEditorVisitor subclass could then construct a GUI from a predicate while e.g. a PredJSONVisitor subclass could store the predicate to disk in JSON format.

The PredEditorVisitor would need two stacks to keep track of parent nodes as we build the GUI. For a predicate we get hold of the top parent node first, and so we must build from the top down. A complication is that there are different kinds of parent nodes. An expression node has a comparison predicate as parent. While a comparison predicate must have a compound predicate like AND, OR, XOR etc as parent node. Thus we maintain two different stacks compositeStack and leafStack:

class PredEditorVisitor : PredVisitor {
virtual void visit(AttributeExpression *exp);
virtual void visit(ConstantExpression *exp);
virtual void visit(LargerThanPredicate *pred);
private:
Stack<PredEditorCompositeRow *> compositeStack;
Stack<PredEditorLeafRow *> leafStack;
MetaData *metaData; // Describe data we are filtering
}

Which one to add child nodes to is easy, as each time we visit a node we know whether it is an expression, comparison or compound predicate. This is what it would look like when visiting a compound predicate node:

void
PredEditorVisitor::Visit(CompoundPredicate *pred) {
PredEditorCompositeRow *row = new PredEditorCompositeRow(pred);
if (!compositeStack.empty())
compositeStack.top()->addChild(row);

compositeStack.push_back(row);
for (Predicate *child: pred) {
pred->Accept(this);
}
compositeStack.pop_back();
}

When visiting a comparison predicate node, we utilize the leaf stack:

void
PredEditorVisitor::Visit(ComparisonPredicate *pred) {
PredEditorLeafRow *row = new PredEditorLeafRow(metaData->attributes, operationsForAttribute);

if (!compositeStack.empty())
compositeStack.top()->addChild(row);

leafStack.push_back(row)
pred->leftExpression->accept(this)
row->selectOperator(pred->operator)
pred->rightExpression->accept(this)

leaf_stack_.pop_back();
}

If all we ever do is adding new sorts of visitors, then this kind of solution works fairly well. However if one adds new kinds of predicates and expressions then it becomes hard to manage as you need to update every single visitor.

Swift’s Solution

The reason why we have visitors in this case is that we can’t know in advance what sort of GUI we might want to create for our predicates or what sort of format we want to serialize it to. We also want to separate concerns and not mix in GUI code with model code.

With Swift you can solve that sort of problem with class extensions. A class extension allows us to add functionality to an existing class. So assume we bought predicate and expression objects from another company, we can still add functionality to them through extensions. The existing predicate and expression code will not rely on these extensions and thus there is no tight coupling in this direction.

Predicate Editor

Let us revisit some of the cases from the visitor solution. First we looked at how a nested row could be created from a compound predicate using a visitor.

With Swift we simply add a method for creating a GUI row to each predicate and expression subclass. First we got to add a default method to expressions and predicates in case we add a new subclass and forget to implement our GUI row creating code. We always need access to a description of the data we are going to create predicates and expression for, since e.g. comparison operators available for string data is different for e.g. numbers. Thus we always have to provide some sort of MetaData object.

extension Expression {
func createExpressionEditor(meta: MetaData) -> ExpressionEditor {
var attr = self.attribute(meta)
return ExpressionEditor.editorFor(attr, attr.defaultOperator)
}
func attribute(meta: MetaData) -> Attribute {
return meta.defaultAttribute
}
}

extension Predicate {
func createPredEditorRow(meta: MetaData) -> PredEditorRow? {
return nil
}
}

Then for each subclass we implement the code for creating a GUI row for that particular kind of predicate or expression. E.g. lets look at the ComparisonPredicate:

extension ComparisonPredicate {
func createPredEditorRow(meta: MetaData) -> PredEditorRow? {
let row = PredEditorLeafRow(meta.attributes, meta.operationsForAttribute)
row.selectAttribute(leftExpression.attribute(meta))
row.selectOperator(self.operator(meta))
row.expressionEditor = rightExpression.createExpressionEditor(meta)
return row
}
}

Getting the details are not important, but lets just get a rough idea what is going on here. We create the GUI for a single row. We need to configure this row so on the left it shows a dropdown list of possible attributes of the objects we will be applying the predicate too. E.g. that could be songs, emails or images. If we are filtering emails then meta would contain meta data about email objects such as what attributes an email might have:

  • sender
  • recipient
  • subject
  • priority

Which of these attributes have been selected is important in deciding which operators are available for comparison.

What is important is seeing how we can easily add GUI specific code as an extension to a non-GUI class. That does not break separation of concerns as the same class can easily be reused later without any GUI dependency. An extension is not able to add member variables or change the behavior of existing methods.

We can deal with all compound predicates such as AND, OR, XOR by implementing an extension in their shared base class:

extension CompoundPredicate {
func createPredEditorRow(meta: MetaData) -> PredEditorRow? {
let row = PredEditorCompositeRow(pred)
for childpred in self {
row.addChild(childpred.createPredEditorRow(meta))
}
}
}

You can see we don’t need any sort of casting when iterating over the child predicates, because the compiler knows that every predicate has a createPredEditorRow since we added it as an extension to the Predicate protocol.

JSON

Implementing JSON serialization is a lot easier than to recreating a GUI. Extensions allow us to put the serialization code in completely separate files from the rest of the predicate code.

As before we need to make JSON creation code in the base class or protocols so that the compiler will know that createJSONRepresentation() is available for all subclasses.

extension Expression {
func createJSONRepresentation() -> Any? {
return nil
}
}

extension Predicate {
func createJSONRepresentation() -> Any?
return nil
}
}
extension AttributeExpression {
func createJSONRepresentation() -> Any? {
return ["type" : "AttributeExpression", "name", self.name]
}
extension ConstantExpression {
func createJSONRepresentation() -> Any? {
return ["type" : "ConstantExpression", "value", self.value]
}
extension ComparisonPredicate {
func createJSONRepresentation() -> Any? {
return ["type" : "ComparisonPredicate",
"operator" : self.comparisonOperator,
"leftExpression" : self.leftExpression.createJSONRepresentation(),
"rightExpression" : self.rightExpression.createJSONRepresentation(),
]
}

Conclusion

Admittedly the predicate editor example might not be easy to follow. But that underscores another fact which is that solving these kinds of problems is quite complex and they get even more complex when you need to rely on patterns such as the visitor pattern. Because Swift supports class extensions, functions are first class etc, a lot of the common design patterns disappear or become irrelevant. Which is a good thing. Design patterns are fundamentally a language smell. They indicate features lacking in the language which requires inventing design patterns.

Written by

Geek dad, living in Oslo, Norway with passion for UX, Julia programming, science, teaching, reading and writing.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store