Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Visitor Pattern Hard

The visitor pattern separates an algorithm from the object structure on which it operates. It allows you to add new operations to existing object structures without modifying the structures themselves.

Implementation

package visitor

import "fmt"

// Shape is the element interface that accepts a visitor.
type Shape interface {
	Accept(Visitor) string
}

// Visitor defines operations for each concrete element type.
type Visitor interface {
	VisitCircle(*Circle) string
	VisitRectangle(*Rectangle) string
}

// --- Concrete elements ---

type Circle struct {
	Radius float64
}

func (c *Circle) Accept(v Visitor) string {
	return v.VisitCircle(c)
}

type Rectangle struct {
	Width, Height float64
}

func (r *Rectangle) Accept(v Visitor) string {
	return v.VisitRectangle(r)
}

// --- Concrete visitors ---

type AreaCalculator struct{}

func (a *AreaCalculator) VisitCircle(c *Circle) string {
	area := 3.14159 * c.Radius * c.Radius
	return fmt.Sprintf("Circle area: %.2f", area)
}

func (a *AreaCalculator) VisitRectangle(r *Rectangle) string {
	area := r.Width * r.Height
	return fmt.Sprintf("Rectangle area: %.2f", area)
}

type PerimeterCalculator struct{}

func (p *PerimeterCalculator) VisitCircle(c *Circle) string {
	perim := 2 * 3.14159 * c.Radius
	return fmt.Sprintf("Circle perimeter: %.2f", perim)
}

func (p *PerimeterCalculator) VisitRectangle(r *Rectangle) string {
	perim := 2 * (r.Width + r.Height)
	return fmt.Sprintf("Rectangle perimeter: %.2f", perim)
}

Usage

shapes := []visitor.Shape{
	&visitor.Circle{Radius: 5},
	&visitor.Rectangle{Width: 3, Height: 4},
}

area := &visitor.AreaCalculator{}
perim := &visitor.PerimeterCalculator{}

for _, s := range shapes {
	fmt.Println(s.Accept(area))
	fmt.Println(s.Accept(perim))
}
// Circle area: 78.54
// Circle perimeter: 31.42
// Rectangle area: 12.00
// Rectangle perimeter: 14.00

Rules of Thumb

  • Use visitor when you need to perform many unrelated operations on an object structure and you don’t want to pollute the element classes with these operations.
  • Adding a new element type requires updating every visitor — the pattern works best when the element hierarchy is stable.
  • Visitor can accumulate state as it traverses a structure, making it useful for compilers, serializers, and report generators.