21 - Visitor
Tags: flutter, dart, oop, programming
Published: 2021-06-07 06:12:24

In the last article, I have analysed a behavioural design pattern that enables loose coupling between the sender of a request and its receiver — Chain of Responsibility. In this article, I would like to analyse and implement another behavioural design pattern that lets you separate algorithms from the objects on which they operate — it is Visitor.

## Table of Contents

* What is the Visitor design pattern?
* Analysis
* Implementation
* Your Contribution

## What is the Visitor design pattern?

![Unexpected Visitor](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4dsmzqrjtwy97e9l8pg3.png)

**Visitor** belongs to the category of behavioural design patterns. The intention of this design pattern is described in the [GoF book](https://en.wikipedia.org/wiki/Design_Patterns):

> Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates.

Let’s say we have a complex object structure, maybe it is a tree or collection, that consists of several different class components. Now, we want to add some kind of new functionality to these components without changing the classes themselves — is that even possible?

The key idea here is to define a double-dispatch operation (in the Visitor design pattern’s context, the operation is called *accept*) for each specific complex object class — I know, you could think that I lied to you about adding new operations without changing the existing code, but wait, there is a good reason for that! When clients traverse the object structure, the *accept* method is called on each element to delegate the request to the specific *visitor* object, which is passed to the method as a parameter. Then, the specific method of the visitor object is called (the request is delegated), hence performing the actual request. That’s the main idea of a double-dispatch operation — the client sends the request to the component, while the component delegates the request to the specific visitor’s method. It means, that it is enough to implement a single method to the component class and then you can define a new operation over an object structure simply by adding a new visitor. And this time, you could implement as many different visitor classes as you want without changing the existing code! How cool is that?

Also, the Visitor design pattern allows gathering related operations into a single class without spreading the implementation details across the whole object structure. That helps when you want to accumulate state while traversing an object structure — there is no need to pass the state to operations that perform the accumulation since the state is stored in the visitor object itself and is accessible by all the specific visitor methods.

At first, all the *visitor*, *accept*, *double-dispatch* terms could look confusing — don’t worry, it gets much clearer when you see the Visitor design pattern in action. Let’s move to the analysis and implementation parts to understand and learn the details about this pattern and how to implement it!

## Analysis

The general structure of the Visitor design pattern looks like this:

![Visitor Class Diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mk1f9bfnt2cz3aw1g2r3.png)

* *Visitor* — declares a visit operation for each *concrete element* class in the object structure. If the programming language supports [function overloading](https://en.wikipedia.org/wiki/Function_overloading), visit operations could have the same name (Dart does not support that at the moment), but the type of their parameters must be different. Usually, the operation’s name and signature is different and identifies the class (*concrete element*) that sends the visit request to the visitor;
* *Concrete visitors* — implements each operation declared by *Visitor*;
* *Element* — declares an *accept* method that takes *Visitor* as an argument;
* *Concrete elements* — implements the acceptance method. The implementation should rely on redirecting the request to the proper visitor’s method corresponding to the current element class;
* *Client* — usually contains a collection or a complex object structure, initialises the *concrete visitor* object and then traverses the object structure by visiting each element with the visitor.

### Applicability

The primary purpose of the Visitor design pattern is to separate algorithms from the objects on which they operate, hence cleaning up the business logic. This way, the classes of your app could focus on their main job while auxiliary behaviours are extracted into a set of visitor classes. Also, visitors allows keeping the related operations together by defining them in one class.

Furthermore, the Visitor design pattern should be used when you want to execute an operation on all elements of a complex object structure and you do not want to change the interface(s) of concrete classes. Different visiting method implementations are executed on different classes which accept the visitors, hence the specific implementation details could be changed or new specific visitor implementations could be added without interfering with the existing code base of the object structure and its components.

Finally, there is one important thing to note: the Visitor design pattern only makes sense for object structures that rarely change (as always, take it with a grain of salt). If you just want to change or add new implementations of visitor — that’s fine. However, changing the object structure classes requires redefining the interface to all visitors which could become cumbersome and violates the Open-Closed (the letter **O** in [SOLID](https://en.wikipedia.org/wiki/SOLID) principles). A simple solution to this problem is just defining the operations in those classes without extracting them to a visitor.

## Implementation

![Let's Dive In](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jg5a9tvcztu489myi6bo.gif)

For the implementation part, we will use the Visitor design pattern on the already implemented complex object structure which was introduced with the Composite design pattern. In my opinion, it would be a great example of how different design patterns could complement each other and how to reuse/extend the already existing codebase.

Our complex object structure is a file system that consists of directories and files of various types (audio, video, text, etc.). Let’s say that this kind of structure is already implemented using the Composite design pattern. Now, we want to add a possibility to export such file structure in two different formats: human-readable (just provide each file in a single, formatted list) and XML.

The first possible approach to implement this feature is to define the export method for each specific file type. In this case, this is wrong for several reasons:

* For each specific export option, we would need to implement a separate export method in each specific file class. Also, by adding a new export option in the future, we would need to add some extra code to each file class once again.
* It’s a violation of the [Single-responsibility principle](https://en.wikipedia.org/wiki/Single-responsibility_principle). The export functionality is just an auxiliary operation applied on top of the file structure, hence each specific file should not care and store the implementation details inside the class itself.

As you could guess, these problems could be easily resolved by applying the Visitor design pattern and defining each specific export option in a separate visitor class which takes care of all the specific implementation details for all the file types in a single place. Let’s check the class diagram first and then implement the pattern!

### Class diagram

The class diagram below shows the implementation of the Visitor design pattern:

![Visitor Implementation Class Diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4b63oy97aeyft11enfxn.png)

*IFile* defines a common interface for both *File* and *Directory* classes:

* *getSize()* — returns the size of the file;
* *render()* — renders the component’s UI;
* *accept()* — delegates request to a visitor.

*File* class implements the *getSize()* and *render()* methods, additionally contains *title*, *fileExtension*, *size* and *icon* properties.

*AudioFile*, *ImageFile*, *TextFile* and *VideoFile* are concrete file classes implementing the *accept()* method from *IFile* interface and containing some additional information about the specific file.

*Directory* implements the same required methods as *File*, but it also contains *title*, *level*, *isInitiallyExpanded* properties and *files* list, containing the *IFile* objects. It also defines the *addFile()* method, which allows adding *IFile* objects to the directory (*files* list). Similarly as in specific file classes, *accept()* method is implemented here as well.

*IVisitor* defines a common interface for the specific visitor classes:

* *getTitle()* — returns the title of the visitor that is used in the UI;
* *visitDirectory()* — defines a visiting method for the *Directory* class;
* *visitAudioFile()* — defines a visiting method for the *AudioFile* class;
* *visitImageFile()* — defines a visiting method for the *ImageFile* class;
* *visitTextFile()* — defines a visiting method for the *TextFile* class;
* *visitVideoFile()* — defines a visiting method for the *VideoFile* class.

*HumanReadableVisitor* and *XmlVisitor* are concrete visitor classes that implement visit methods for each specific file type.

*VisitorExample* contains a list of visitors implementing the *IVisitor* interface and the composite file structure. The selected visitor is used to format the visible files structure as text and provide it to the UI.

### IFile

An interface that defines methods to be implemented by specific files and directories. The interface also defines an *accept()* method which is used for the Visitor design pattern implementation. Dart language does not support the interface as a class type, so we define an interface by creating an abstract class and providing a method header (name, return type, parameters) without the default implementation.

![ifile.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8aox08ztpwgjt83gsn4n.png)

### File

A concrete implementation of the *IFile* interface. In *File* class, *getSize()* method simply returns the file size, *render()* — returns the file’s UI widget which is used in the example screen.

![file.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/chbn0ucg3q13wimj2cm7.png)

### Concrete file classes

All of the specific file type classes implement the *accept()* method that delegates request to the specific visitor’s method.

* *AudioFile* — a specific file class representing the audio file type that contains an additional *albumTitle* property.

![audio_file.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z4gwwabol9ksrhybzmly.png)

* *ImageFile* — a specific file class representing the image file type that contains an additional *resolution* property.

![image_file.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pl9ivoa8oat59l34b7rv.png)

* *TextFile* — a specific file class representing the text file type that contains an additional *content* property.

![text_file.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d9prhgpb5quxpma5ih8l.png)

* *VideoFile* — a specific file class representing the video file type that contains an additional *directedBy* property.

![video_file.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nugwybqpvmkiocmut70j.png)

### Directory

A concrete implementation of the *IFile* interface. Similarly as in *File* class, *render()* returns the directory’s UI widget which is used in the example screen. However, in this class *getSize()* method calculates the directory size by calling the *getSize()* method for each item in the *files* list and adding up the results. Also, the class implements the *accept()* method that delegates request to the specific visitor’s method for the directory.

![directory.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oba2p8y7mpmb9kde5lty.png)

### Formatting extensions

Defines an extension method *indentAndAddNewLine* that adds *nTab* tabs at the beginning and a new line symbol at the end of a String.

![formatting_extensions.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/d4dllzrn012vvlmk60ub.png)

### IVisitor

An interface that defines methods to be implemented by all specific visitors.

![ivisitor.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ncu43gidc0t7384dohmz.png)

### Concrete visitors

* *HumanReadableVisitor* — implements the specific visitor that provides file information of each file type in a human-readable format.

![human_readable_visitor.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cc24aprc50z9jkum24zm.png)

* *XmlVisitor* — implements the specific visitor that provides file information of each file type in XML format.

![xml_visitor.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qbn7fjo3hx1te0pcmdy7.png)

### Example

First of all, a markdown file is prepared and provided as a pattern’s description:

![Visitor Markdown](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vwgfz2h1twwdebr4tfp6.gif)

The *VisitorExample* widget contains the *buildMediaDirectory()* method which builds the file structure for the example. Also, it contains a list of different visitors and provides it to the *FilesVisitorSelection* widget where the index of a specific visitor is selected by triggering the *setSelectedVisitorIndex()* method.

![visitor_example.dart](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9r5unr09ucqaq8dkldf8.png)

When exporting files’ information and providing it in the modal via the *showFilesDialog()* method, the example widget does not care about the concrete selected visitor as long as it implements the *IVisitor* interface. The selected visitor is just applied to the whole file structure by passing it as a parameter to the *accept()* method, hence retrieving the formatted files’ structure as text and providing it to the opened *FilesDialog* modal.

![Visitor Example](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tvxycutvinwsevdn5cfu.gif)

As you can see in the example, by selecting the specific visitor (export as text or XML option), the file structure is exported in the corresponding text format and provided to the user.

All of the code changes for the Visitor design pattern and its example implementation could be found [here](https://github.com/mkobuolys/flutter-design-patterns/pull/22).

## Your Contribution

💖 or 🦄 this article to show your support and motivate me to write better!
💬 Leave a response to this article by providing your insights, comments or wishes for the next topic.
📢 Share this article with your friends, colleagues on social media.
➕ Follow me on dev.to or any other social media platform.
⭐ Star the [Github](https://github.com/mkobuolys/flutter-design-patterns) repository.