Card 01/ 09
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.
Without the patterneach shape holds every operation
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
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.
| Pattern | What it does |
|---|---|
| Chain of Responsibility | Passes a request along a chain of handlers until one handles it. |
| Command | Turns a request into an object that can be stored, queued or undone. |
| Interpreter | Represents a small language's grammar as classes and evaluates its sentences. |
| Iterator | Visits the elements of a collection without exposing how it is stored. |
| Mediator | Routes the communication between objects through one central object. |
| Memento | Captures an object's state so it can be restored later. |
| Observer | Notifies many objects when one object changes. |
| State | Changes an object's behaviour when its internal state changes. |
| Strategy | Swaps one algorithm for another behind a common interface. |
| Template Method | Fixes 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. |
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
singledispatchandmatchdo.
← → move through sections and cards