design pattern

Behavioral Design Patterns: Visitor

Next up in our detailed Guide to Software Design Patterns series we’ll look at the visitor design pattern, which is one of the more complex patterns we’ll be discussing. The visitor pattern is ideal for adding new capabilities to existing objects, without modifying the classes on which those objects operate.

In this article we’ll be showing a real world and fully-functional C# code sample of the visitor pattern, so let’s jump right in!

In the Real World

The visitor pattern is made up of a number of components, but the primary ones are:

  • Visitor – Declares a Visit() method that accepts a passed Visitable argument. The method signature determines the object type that will be used when executing the method code. This allows the code to differentiate between different Visitable objects, and route subsequent logic accordingly.
  • Visitable – Declares an Accept() method, which expects a passed Visitor argument. Within this Accept() method, the Visitor parameter invokes its Visit() method, passing the calling Visitable object. This ties the two objects together, without either worrying about the specific data type of the other.

I know that’s a bit vague and perhaps difficult to follow, but we’ll clear things up when you see it in code. For now, let’s consider the real world example of visiting Disneyland (or the amusement park of your choice). Upon arrival, you buy a full day pass, which costs a pretty penny but gives you unlimited access to all the rides for the rest of the day. Woohoo!

Now that you have your pass, you don’t need to jump through any additional hoops to go on Space (or Splash) Mountain. You just hop in line and flash your pass to the attendant when the time comes for some fun. Likewise, the attendant doesn’t need to perform any extra checks; once he or she sees your pass, the unspoken contract has been agreed to.

Ignoring the fact that I haven’t personally been to Disneyland in years, and they may not even use a pass-based system anymore for all I know, the concept still holds true. The visitor design pattern is used for, well, visiting theme parks so that, once you have access, nobody inside needs to worry about selling you tickets for individual rides.

Full Code Sample

Below is the full code sample we’ll be using in this article. It can be copied and pasted if you’d like to play with the code yourself and see how everything works.

How It Works In Code

Our visitor pattern code sample is built around the basic concept of creating digital text documents. Each Document consists of a series of elements such as Text, Heading, BoldText, and so forth. We then want to format the document appropriately, depending whether the output should be in HTML, Markdown, or BBCode.

The simplest place to start is with the IVisitable interface, which is where we declare our Accept(IVisitor visitor) method. IVisitable also has a Text field, which we’ll use to store the primary text content of each element.

Visitable is an abstract class that implements IVisitable. This class allows us to define any default method logic, such as that for our Accept(IVisitor visitor) method:

As the comments state, one important note is that we’re using the dynamic keyword within Accept(IVisitor visitor), which allows both the Visitable and Visitor instances to bypass static type checking. This allows the appropriate Visit() method signature, within the appropriate Visitor class declaration, to be called at runtime. We’ll see this in action in a moment, but it allows our code to only include method declarations that are critical to behavior, while allowing the code to use defaults elsewhere.

Next we need to define some Visitables, all of which inherit the Visitable class:

I’ve left out the comments for these since they’re quite self-explanatory. We call : base(text) within each constructor, so we don’t need to reassign the passed string text parameter to the Text property of the base Visitable class. Other than that, each of these classes allows us to create unique element types within our Document class, which we’ll see in a moment.

With our Visitables out of the way, it’s now time to create our IVisitor interface, and the Visitor that that implements it:

As previously mentioned, the fundamental method we need is Visit(IVisitable visitable). As you’ll recall, this is invoked within the Visitable.Accept(IVisitor visitor) method, and is the primary means by which all Visitors interact with all Visitables. In this case, the default logic consists of simply concatenating the Text property from the passed IVisitable onto the string Content property of the Visitor instance.

Now we need to define the various types of document formatting we want, which is accomplished within unique Visitor-inherited classes:

Here we’re starting to see the visitor design pattern come to fruition. Each Visit(...) method signature above is contained within a unique Visitor class, and is differentiated by the unique Visitable data type it accepts. This combination of two unique instance types determining the logical behavior is the primary purpose of the visitor pattern, and is known as multiple dispatch (or double dispatch). This allows languages like C#, which are typically only single dispatch, to perform function calls based on the runtime types of multiple objects, as seen above.

Anyway, each of the Visitor classes above concatenates the passed element Text onto the parent Content property string, while also formatting it in a way that matches that particular Visitor style. For example, I’m writing these words right now in a Markdown document, so when I create a url like this, I format it in exactly the same way that MarkdownVisitor(Hyperlink element) formats its passed element.

What’s also critically important here is that we would typically need to declare a Visit(...) method overload for each and every Visitable class in the application. However, you’ll notice that we’ve left out such method declarations for some Visitor classes. For example, neither MarkdownVisitor nor BbVisitor have a Visit(Text element) method. This is because, as you may recall, we configured this example to use dynamic values when invoking our Visit(...) methods. This allows C# to dynamically determine if a matching method signature exists at runtime. If so, it passes execution along to that method, but if not, it defaults to the baseline Visitor.Visit(IVisitable visitable) method.

The last component to our example is the basic Document class, which isn’t necessary to the visitor pattern. However, it serves the purpose here of holding a List<Visitable> property called Elements, which is all the Visitable objects we’ve added to the document.

Accept(IVisitor visitor) iterates over all elements and invokes their respective Accept(IVisitor visitor), using the passed IVisitor instance. This will allow us Accept() a single Visitor with one call.

Now, with everything setup, let’s test this out in our Program.Main(string[] args) method:

The comments walk through what’s happening, but we basically start with a new Document and add some elements to it. We then create a few Visitor instances, and force the document instance to Accept() each of those, which, as we saw above, calls Accept() for each element in the Document. Now, when we output the Visitors to the log, we should see each element in the Document, but properly formatted to match the formatting rules specified in each unique Visitor class:

Sure enough, that’s exactly what we get! HTML all runs together since it doesn’t typically include linebreaks. Markdown has proper formatting, including linebreaks around the Paragraph element. BBCode has similar Paragraph linebreaks, but handles formatting of other types, like BoldText and Hyperlinks, quite differently.

There you have it! Hopefully this article provided a bit of insight into the capabilities of the visitor design pattern, and gave you some basic tools you can use in your own future projects. For more information on all the other popular design patterns, head on over to our ongoing design pattern series here!