Deep Dive
low level designdesign patternsoop

Visitor Pattern: Add Operations Without Editing the Classes

When your class hierarchy is stable but you keep bolting new operations onto every node — typecheck, pretty-print, evaluate, export — the Visitor pattern moves each operation into its own class so the nodes never change again.

·15 min read
Hard

Every compiler team hits the same wall. You have an AST: a dozen node types — Num, Add, FunctionCall, IfExpr — and they are done. The grammar is frozen; nobody’s inventing new node kinds this quarter. What is not done is the list of things you need to do to that tree.

First you eval() it. Then product wants a pretty-printer, so you add prettyPrint() to all twelve nodes. Then the type system lands, so you add typecheck() to all twelve. Then a bytecode target, so compile() — all twelve again. Each new operation is a shotgun edit across the entire hierarchy, and worse, one operation’s logic is now smeared across twelve files. The type-checker for Add lives in Add.java, the type-checker for IfExpr lives in IfExpr.java, and reading “how does typechecking work” means opening a dozen tabs.

The nodes are stable. The operations are what multiply. Your code is organized along the wrong axis — and the Gang of Four named the fix.

The intuition: the tax auditor

A tax auditor visits businesses all day — a restaurant, a farm, a software startup. The auditor is one operation (“audit”), but the audit behaves differently per business type: for the restaurant they check food-safety receipts, for the farm they check subsidy filings, for the startup they check stock-option accounting.

Notice who holds the logic. The restaurant doesn’t know how to audit itself; it just opens its books and says “here, I’m a restaurant — do your restaurant thing.” The knowledge of what an audit means lives entirely in the auditor. Next year a new auditor shows up — a fire-safety inspector — and none of the businesses change at all. They just accept a different visitor.

That auditor is the visitor. Each business is an element. The element’s only job is to announce its concrete type and hand itself over; the operation lives in the visitor.

From here the members-only walkthrough takes over: the expression-problem tradeoff spelled out, the full code (the two contracts, the accept() bounce, and two drop-in visitors), the class-diagram structure, a step-by-step trace, the real-world visitors you can open right now, and the interview corner with a hands-on AST refactor challenge and a quiz.

Members only

Keep reading with Premium

You've reached the members-only part of this deep-dive — the full implementation, the interactive ring simulator, and the step-by-step walkthrough. Unlock it with a membership.

Related Articles