ConceptDifficulty: Intermediate3 min

Visitor Pattern: Adding Operations to Classes Without Changing Them

Export, price and validate a set of shapes without adding three methods to every shape.

The Visitor pattern moves an operation over a set of classes out of those classes and into a class of its own, called a visitor, with one method for each class it handles. A new operation is then a new visitor, and the classes it works on stay unchanged. This card covers the problem that calls for it, the intent, and when it fits.

The problem is an operation spread across many classes. A drawing program has circles, squares and triangles, and needs to compute their area, export them to SVG, and check them for errors. With a method per operation in each class, the export logic lives in three places, beside the area logic and the checks, and each new operation means editing every shape class again.

A method per operation in every class, and one visitor per operation

Without the patterneach shape holds every operation

py
class Circle:
    def area(self): ...
    def to_svg(self): ...
    def validate(self): ...

class Square:
    def area(self): ...
    def to_svg(self): ...
    def validate(self): ...

A fourth operation edits both classes again.

With the patterneach operation is one class

py
class SvgExport(Visitor):
    def visit_circle(self, c): ...
    def visit_square(self, s): ...

A fourth operation is a new visitor; Circle and Square keep only accept.

The Gang of Four state the intent as: represent an operation to be performed on the elements of an object structure, and let a new operation be defined without changing the classes of the elements on which it operates. The elements are the classes being visited, and the object structure is whatever holds them, often a list or a tree.

A tax inspector touring a shop, a factory and a farm is a familiar picture: one inspector, a different rule at each business, and the businesses do not change when a new kind of inspection is introduced. The picture breaks on who decides: the inspector looks at a business and decides what kind it is, while in Visitor the element tells the visitor what it is, through a method called accept.

Visitor is one of eleven behavioural patterns, the family concerned with which object does which part of the work, and how objects communicate.

The behavioural family, with this pattern marked
PatternWhat it does
Chain of ResponsibilityPasses a request along a chain of handlers until one handles it.
CommandTurns a request into an object that can be stored, queued or undone.
InterpreterRepresents a small language's grammar as classes and evaluates its sentences.
IteratorVisits the elements of a collection without exposing how it is stored.
MediatorRoutes the communication between objects through one central object.
MementoCaptures an object's state so it can be restored later.
ObserverNotifies many objects when one object changes.
StateChanges an object's behaviour when its internal state changes.
StrategySwaps one algorithm for another behind a common interface.
Template MethodFixes an algorithm's steps in a base class and lets subclasses fill some in.
Visitor (this topic)Adds operations to a class hierarchy without changing its classes.
Visitor

Use it when

  • The set of element classes is stable, and new operations over them keep arriving.
  • An operation needs different code for each element class, and belongs together as one unit.
  • The elements form a structure, such as a tree, that each operation walks the same way.

Avoid it when

  • New element classes are added often, since each one needs a method in every visitor.
  • There are only one or two operations, where a method on each class is simpler.
  • The language can choose a function by the argument's type, as Python's singledispatch and match do.