C# Design Patterns: The Visitor Pattern
My last article introduced expressions in C# and explained a bit why they were useful. Today I’m going to build upon that article and introduce you to the Visitor Pattern, a common pattern used when dealing with expressions. If you’re not familiar with expressions or a bit rusty, I would recommend reading the previous article first.
The Visitor Pattern
Wikipedia defines the visitor pattern as “the visitor design pattern is a way of separating an algorithm from an object structure on which it operates”.
What does that mean? When I normally define classes in C#, they look something like this.
Inside of my class I’m defining my data and my methods or algorithms that operate on my class.
But if you’re using the Visitor Pattern, you break apart the class and the methods into something like this.
Now the data and the methods are defined separately, decoupling them has advantages which we’ll see in a moment.
As a note, breaking apart the data and methods in this example really doesn’t do anything for you, also I’m not using the Visitor Pattern terminology, but we’ll explore that next.
Printing Expressions
The code that follows builds upon the example shown in this Wikipedia article reference.
Lets demo this pattern by defining expressions to represent simple mathematical equations and a visitor to print out our equations.
To start with our pattern, I’m going to define two interfaces. IExpressionVisitor
defines what types of expressions our visitor supports. Anything inheriting from IExpression
are the objects that are going to be “visited”. The Accept
method is the standard name for the method that allows the visitor to be accepted into the IExpression
class.
Next lets define our first expression called Literal. It’s responsibility is to hold the number values in our equations.
You’ll notice that it inherits from IExpression
, and implements the Accept
method, all it does it pass itself into the visitor by the this
keyword.
Next I’ll need a Addition
expression defined as follows.
I’m defining two IExpressions
, Left and Right, to be the two expressions that will be added together.
Now that I have two expressions, I’m going to need a Visitor to be able to visit those expressions and print out the equation.
Lets call our visitor InfixExpressionPrinter
and give it the following implementation.
This has two methods for how to handle both of my expression types. If it’s a Literal
, all I need is to print the value. If it’s an Addition
, it generates the values of both the Left and Right expressions and adds them together.
Now lets put that all together in order to print out our expression.
I’m creating a simple Addition equation, calling the Accept
method visits the entire expression and prints the equation represented by our expression tree. Running this prints (5+6)
, which reflects the equation we built with expressions.
We can also print out more complex expressions, such as the following.
Running this outputs (((5+6)+20)+(1+3))
, also the mathematical representation of our expression.
The astute reader will notice that our expression printer prints equations in Infix notation. What if we wanted to print Prefix? Or Postfix?
This is really the power of the Visitor Pattern. Since the logic for printing isn’t contained in our expression classes, we’re free to add another printer as long as we inherit from the IExpressionVisitor
method.
Lets define a printer for Prefix notation.
When the second example is run using the second equation the result is + + + 5 6 20 + 1 3
. Creating a new visitor allows us to convert the same expression into a different output.
Benefits of the Visitor Pattern
As you can see in our examples, printing different forms of the equations doesn’t require any changes to our Literal
or Addition
classes. The Visitor Pattern allows some other class to define how to print expressions, and it hands off that responsibility to whoever implements the IExpressionVisitor
interface.
This is a pretty trivial example of the Visitor Pattern. If you want to see an enterprise example of this pattern, look through the Entity Framework Core code base. It uses this pattern (and a lot more) to turn a Linq statement into a Database specific query.
Instead of simple classes like Literal
, Addition
, InfixExpressionPrinter
and PrefixExpressionPrinter
, they have complex classes like SqlServerQuerySqlGenerator which contains this method:
That method looks pretty similar to the examples that we created in this article.
Entire SQL projects are devoted to converting expressions into each particular data store. Whether that be SQL Server, Sqlite, Oracle, Postgres or others.
Entity Framework Core obviously has a lot of complexities to it, but like most other software, it’s built upon principles or patterns that most of us can familiarize ourselves. The Visitor Pattern happens to be one of those patterns.
Closing Thoughts
I’ve heard it said that design patterns are “experience reuse”. As an industry, we encounter very common problems across technologies and business domains. Patterns exist to solve those problems that have been born from both pains and successes in the past.
Knowing Design Patterns helps us learn how to avoid making the same mistakes again. Design Patterns are great tools in the developer toolbox and I’m glad to have shared about one today.