[Design Pattern] Visitor Pattern — The Most Complicated Design Pattern With C# Code Sample

Thanh Le
Geek Culture
Published in
6 min readMar 16, 2021

--

Photo by Tran Phu on Unsplash

Context

A couple of weeks ago, I and my colleagues spent more than 1 hour during lunchtime just to discuss and find the answer to this question below:

“What is the most complicated design pattern that you have ever worked with?”

And, we mostly agreed that the most complicated one is “Visitor Pattern”. You might agree or disagree with us — that is your choice. But don’t leave this article at this moment, please read till the end with the sample I’m going to show you. Who knows? You might change your mind :)

In order to prove that our answer is correct, I decided to bring the question regarding the Visitor Design Pattern to 10 interviews that I joined as an interviewer. All candidates were Senior Developers (more than 5 years of experience). The result was:

  • 4 out of 10 candidates could explain to me what it is and how it works through samples.
  • 1 out of 4 used Visitor Pattern in a real project.
  • 4 out of 4 agreed with me that “Visitor Pattern” is possibly the most complicated one.

What is the Visitor Design Pattern?

Photo by Júnior Ferreira on Unsplash

Visitor” is a design pattern that belongs to the “Behavioral” design pattern type. Any pattern that is a Behavioral Pattern will focus on how classes and objects interact and distribute responsibilities.

Besides “Behavioral”, you might have experience with “Creational” and “Structural” type through very famous and popular ones like Singleton (Creational), Facade (Structural)…

The Visitor design pattern is a way of separating an algorithm from an object structure on which it operates. A practical result of this separation is the ability to add new operations to existing object structures without modifying the structures. It is one way to follow the open/closed principle. — Wiki.

Sounds complicated, right? Ok, let me give you a sample.

You have an object named “Student” which has a method named “Study”. But besides studying, the student also needs to play. Now you need to add another method named “Play” to the “Student” object, right? The problem here is that adding a new method to the “Student” object will violate the “Open/Closed” principle. By using the Visitor pattern, you can add behaviours for playing without modifying the Student object. In other words, you don’t have to modify the Student object when you have to add new behaviour.

Structure (UML Diagram) and Anatomy

figure 1: Visitor Design Pattern structure
  • IElement: That is the interface that has only a single method named “Accept”. The “Accept” method takes the parameter of “IVisitor” interface.
  • IVisitor: That is the interface that has methods for all classes which implement the “IElement” interface. As you can see in the above figure, “IElement” interface is implemented by “ConcreteElementClassA” and “ConcreteElementClassB” — that is the reason why have 2 methods IVisitor interface: VisitClassA(ClassA) and VisitClassB(ClassB).
  • ConcreateVisitor (ConcreateVisitor1 and ConcreateVisittor2): This implements all methods that declared by Visitor. Each ConcreateVisitor implements a behaviour for the corresponding class/object in the structure.
  • ConcreateElement (ConcreateElementClassA and ConcreateElementClassB): This implements “Accept” method that has method that takes a visitor as an argument.

Still unclear?

Ok, let’s move to the next section where we need to dig into the code.

Visitor Design Pattern Example

Photo by Arnold Francisca on Unsplash

Let’s imagine we’re working for an insurance company that has a unified platform — selling multiple types of insurance like MotorBike insurance, Car insurance… All insurance products share the same logics on getting a Quote… just using a different question set as well as we need to communicate with the buyer but Car uses email while MotorBike uses SMS. When you get a quote for your Car you need to provide the answer to the question “Is the vehicle right or left-hand drive?” while this question is not applicable when you get a quote for your Motor Bike.

Now, the UML Diagram for the sample will look like this

figure 2: Visitor Design for Insurance Company

Firstly, we need to create the “IInsurance” and “IVisitor” interface following the design

figure 3: IInsurance and IVisitor interface

After that, we need to create element classes that inherit from “IInsurance” interface

figure 4: Car Insurance element class
figure 5: Motor Bike Insurance element class

These classes have a different set of properties, you can also add different methods to them. But both of them have the “Accept” method.

Next, we need to implement Visitor classes. These classes will contain the specific logics for each Element class based on the provided parameter.

figure 6: Quote VIsitor class
figure 7: CustomerCommunication Visitor class

The last step, let’s call it in the client and see how it works

figure 8: Call Visitor classes

Here is the result

figure 9: Everything works as expected

Note: You can find the source code sample on my Github

Advantages and Disadvantages of the Visitor Design Pattern

Photo by Ivan Bandura on Unsplash

The Visitor Design Pattern brings to our application the following benefits:

  • New operations are easy to add without changing element classes (add a new concrete visitor). As a result, different concrete elements don’t have to implement their part of a particular algorithm.
  • Related behaviour only focuses on a single concrete visitor.
  • Visited classes are not forced to share a common base class.
  • Visitors can accumulate state as they visit each element, thus, encapsulating the algorithm and all its data.

But it also has the following issues:

  • We have to change all visitors for every new element.
  • Many visitors will have empty methods to comply with the interface.

Conclusion

Some people might don’t like the Visitor Pattern as it’s not easy to implement as well as its drawbacks.

However, you can consider applying the Visitor Pattern when:

  1. An object structure must have many unrelated operations to perform on it.
  2. An object structure cannot change but operations performed on it can change.
  3. The operations need to perform on the concrete classes of an object structure.
  4. Exposing the internal state or operations of the object structure is acceptable.
  5. Operations should be able to operate on multiple object structures that implement the same interface.

References

--

--

Thanh Le
Geek Culture

A Software Technical Architect — Who code for food and write for fun :)