Implementing Visitor Pattern in PHP and Symfony

In my latest article I showed my implementation of the Pattern Builder with support of Symfony’s Service Tag.
I love this Symfony service, and this time I would like to show you how to use it to implement another design pattern: The Visitor pattern, less known than the Builder pattern but also very nice and useful.

Source: Wikipedia

To do this we start from the excellent example of refactoring.guru, but going to modify it slightly to fit our purpose.

Imagine you have three entities: Company, Departmentand Employee. And for all of these entities we want to know the salary (for an employee) or the total wage bill for other entities.
Each of these entities will have a method (getCost, or getSalary) that will do the job.

But to mutualize the code we want to create a service that has this responsibility. A SalaryReport class will have the task of carrying out the calculation in string format.

Ok perfect this class works, but honestly there are several things that are not right. The first of these does not respect the SOLID principles.

Because there is no shared interface or abstraction between types, when adding on more entities, this type checking will pile on and can become quite cumbersome and unreadable. And we throw an exception when the entity type is unknown to avoid improper use. — https://doeken.org/blog/visitor-pattern

This is where the Visitor pattern can be very useful.

First let’s create an interface implemented by each of the entities:

The accept method takes as parameter another interface (VisitorInterface) which is implemented by the SalaryReport class.

Let’s look at the accept methods of entities now.

And here’s how the SalaryReport class changes now

Well, that’s better, methods are typed and each method has a single responsibility. But we can do even better. For example, we don’t want to modify the class every time we add an entity by adding a new method on SalaryReport class, because in this way we don’t respect the O of SOLID.

Why not “borrow” the Director concept from the Builder pattern and use the Symfony Service Tag?

And of course now we’ll have three different classes implementing the same interface.

The interface implemented by the entities now changes like this

And the accept method will be the same for all entities

Using the Symfony service tag we were able to eliminate the drawback of the visitor pattern. In fact we will no longer have to modify the VisitorInterface interface when we add a new Entity. And furthermore, since all entities use the same method, the code can be unified with a Trait or an abstract class.

--

--

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