Haim Michael
blog.lifemichael.com
Goodbye Boilerplate
The Visitor Pattern Reimagined
in Modern Java
© 2025 Haim Michael 20251118
My Background
 My name is Haim Michael. I live in Tel-Aviv, and I am a
father for two kids.
 I am into programming for more than 40 years. It all started
with Basic (Sinclair ZX-81) and moved forward with Pascal,
Prolog and Lisp.
 My passion lies with teaching and endless
learning. I continuously learn and evolve.
© 2025 Haim Michael 20251118
I Love Teaching
 I focus on teaching advanced topics in software
development, both in academic institutions and in high tech
companies.
life michael
www.lifemichael.com
life michael pro
pro.lifemichael.com
life michael udemy
udemy.com/user/life-michael
© 2025 Haim Michael 20251118
I Love Programming
Java (30+ Years)
Scala (16+ Years) Kotlin (10 Years)
Swift (9+ Years) TypeScript (12+ Years)
PHP (15+ Years) JS (30+ Years)
30+ Years
I
C# (24+ Years)
C# C++
C++ (30+ Years)
© 2025 Haim Michael 20251125
Introduction
 The use of Record classes when combined with sealed
classes and pattern matching can significantly improve the
implementation of the Visitor design pattern in Java.
© 2025 Haim Michael 20251125
Table Of Content
 The Programming Problem
The programming problem the Visitor classic design pattern solves.
 The Traditional Implementation
The traditional implementation of the Visitor classic design pattern in Java.
 The Modern Implementation
The modern implementation of the Visitor classic design pattern in Java with Records,
Sealed Classes, and Pattern Matching.
 The Benefits of Using Records
The benefits of using Records, Sealed Classes, and Pattern Matching when implementing
the Visitor classic design pattern.
© 2025 Haim Michael 20251125
The Programming Problem (1 out of 4)
 In many object-oriented systems, we have a class hierarchy
(e.g., expression tree, shape hierarchy) that we want to
traverse or operate on in different ways - such as evaluating,
rendering, or pretty-printing.
 If we add new operations directly to these classes, we might
unintentionally clutter the new code with unrelated logic. In
addition, we will need to modify existing classes whenever
adding new operators.
© 2025 Haim Michael 20251125
The Programming Problem (2 out of 4)
 The Visitor classic design pattern aims at solving the following
programming problem:
How can we define new operations on a set of related object
types (e.g., binary tree nodes, shapes, expressions) without
modifying their source code and without duplicating logic
across types?
© 2025 Haim Michael 20251125
The Programming Problem (3 out of 4)
 The intent of the Visitor classic design pattern according to
the Gang of Four:
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.
© 2025 Haim Michael 20251125
The Programming Problem (4 out of 4)
 The Visitor pattern decouples the operations from the object
structure, allowing us to define new operations without
changing the element classes.
© 2025 Haim Michael 20251125
The Traditional Implementation (1 out of 4)
 Implementing the Visitor classic design pattern the traditional
way will require having an object that plays the role of a
visitor. That object will pays a visit to each one of the objects
by calling a method (the accept method) on each one of
these objects. Inside the accept method, the visit method
will be invoked on the Visitor object. The reference of the
visited object will be passed over to it.
© 2025 Haim Michael 20251125
The Traditional Implementation (2 out of 4)
public interface Expression {
Integer accept(Visitor visitor);
}
public interface Visitor {
Integer visit(Number num);
Integer visit(Total total);
Integer visit(Multiplication mul);
Integer visit(Difference diff);
}
public final class Number implements Expression {
public final int value;
public Number(int value) { this.value = value; }
public Integer accept(Visitor visitor) {
return visitor.visit(this);
}
}
© 2025 Haim Michael 20251125
The Traditional Implementation (3 out of 4)
public interface Expression {
Integer accept(Visitor visitor);
}
public final class Multiplication implements Expression {
public final Expression left, right;
public Multiplication(Expression left, Expression right) {
this.left = left;
this.right = right;
}
public Integer accept(Visitor visitor) {
return visitor.visit(this);
}
}
public class Total implements Expression { ... }
public class Subtraction implements Expression { ... }
© 2025 Haim Michael 20251125
The Traditional Implementation (4 out of 4)
public class SimpleVisitor implements Visitor {
public Integer visit(Number num)
{
return num.value;
}
public Integer visit(Total ob) {
return ob.left.accept(this) + ob.right.accept(this);
}
public Integer visit(Multiplication ob) {
return ob.left.accept(this) * ob.right.accept(this);
}
public Integer visit(Difference ob) {
return ob.left.accept(this) - ob.right.accept(this);
}
}
© 2025 Haim Michael 20251125
The Modern Implementation (1 out of 4)
 Implementing the Visitor classic design pattern when using
Record Classes, Sealed Classes and Pattern Matching might
become simpler.
 We will define a method that will take an Expression object
that using pattern matching will evaluate it according to its
type.
© 2025 Haim Michael 20251125
The Modern Implementation (2 out of 4)
public sealed interface Expression
permits Number, Total, Difference, Multiplication {}
public record Number(int value)
implements Expression {}
public record Total(Expression left, Expression right)
implements Expression {}
public record Difference (Expression left, Expression right)
implements Expression {}
public record Multiplication (Expression left, Expression right)
implements Expression {}
© 2025 Haim Michael 20251125
The Modern Implementation (3 out of 4)
public class Evaluator {
public int evaluate(Expression expression) {
return switch (expression) {
case Number(int value) -> value;
case Total(Expression left, Expression right) ->
evaluate(left) + evaluate(right);
case Multiplication(Expression left, Expression right) ->
evaluate(left) * evaluate(right);
case Difference(Expression left, Expression right) ->
evaluate(left) - evaluate(right);
};
}
}
© 2025 Haim Michael 20251125
The Modern Implementation (4 out of 4)
public class Program {
public static void main(String[] args) {
Expression expression = new Multiplication(
new Total(new Number(3),new Number(2)),
new Total(new Number(6),new Number(4)));
System.out.println(new Evaluator().evaluate(expression));
}
}
© 2025 Haim Michael 20251125
The Benefits
 With sealed classes, we explicitly restrict which subclasses
can extend a type. That ensured every subclass is handled.
 We get less boilerplate thanks to using records. When
defining a record type, we automatically get a constructor,
getters, setters, equals, hashCode and toString.
© 2025 Haim Michael 20251125
The Benefits
 The use of pattern matching eliminates double dispatch.
Traditionally, implementing the visitor pattern meant having
double dispatch, requiring us to define a separated version of
visit(c) for each variant.
 We enjoy better performance with pattern matching & switch
expressions. The pattern matching in Java is optimized by the
JVM.
© 2025 Haim Michael 20251125
The Benefits
 Using pattern matching, the logic is explicitly expressed in one
place rather than scattered across many visitor classes. Our
code becomes clearer and simpler to maintain.
© 2025 Haim Michael 20251125
Questions & Answers
Thanks for Your Time!
Haim Michael
haim.michael@lifemichael.com
WhatsApp +972+54+6655837
https://meetup.com/lifemichael https://tinyurl.com/javamonthly

Goodbye Boilerplate: The Visitor Pattern Reimagined in Modern Java | Haim Michael

  • 1.
    Haim Michael blog.lifemichael.com Goodbye Boilerplate TheVisitor Pattern Reimagined in Modern Java
  • 2.
    © 2025 HaimMichael 20251118 My Background  My name is Haim Michael. I live in Tel-Aviv, and I am a father for two kids.  I am into programming for more than 40 years. It all started with Basic (Sinclair ZX-81) and moved forward with Pascal, Prolog and Lisp.  My passion lies with teaching and endless learning. I continuously learn and evolve.
  • 3.
    © 2025 HaimMichael 20251118 I Love Teaching  I focus on teaching advanced topics in software development, both in academic institutions and in high tech companies. life michael www.lifemichael.com life michael pro pro.lifemichael.com life michael udemy udemy.com/user/life-michael
  • 4.
    © 2025 HaimMichael 20251118 I Love Programming Java (30+ Years) Scala (16+ Years) Kotlin (10 Years) Swift (9+ Years) TypeScript (12+ Years) PHP (15+ Years) JS (30+ Years) 30+ Years I C# (24+ Years) C# C++ C++ (30+ Years)
  • 5.
    © 2025 HaimMichael 20251125 Introduction  The use of Record classes when combined with sealed classes and pattern matching can significantly improve the implementation of the Visitor design pattern in Java.
  • 6.
    © 2025 HaimMichael 20251125 Table Of Content  The Programming Problem The programming problem the Visitor classic design pattern solves.  The Traditional Implementation The traditional implementation of the Visitor classic design pattern in Java.  The Modern Implementation The modern implementation of the Visitor classic design pattern in Java with Records, Sealed Classes, and Pattern Matching.  The Benefits of Using Records The benefits of using Records, Sealed Classes, and Pattern Matching when implementing the Visitor classic design pattern.
  • 7.
    © 2025 HaimMichael 20251125 The Programming Problem (1 out of 4)  In many object-oriented systems, we have a class hierarchy (e.g., expression tree, shape hierarchy) that we want to traverse or operate on in different ways - such as evaluating, rendering, or pretty-printing.  If we add new operations directly to these classes, we might unintentionally clutter the new code with unrelated logic. In addition, we will need to modify existing classes whenever adding new operators.
  • 8.
    © 2025 HaimMichael 20251125 The Programming Problem (2 out of 4)  The Visitor classic design pattern aims at solving the following programming problem: How can we define new operations on a set of related object types (e.g., binary tree nodes, shapes, expressions) without modifying their source code and without duplicating logic across types?
  • 9.
    © 2025 HaimMichael 20251125 The Programming Problem (3 out of 4)  The intent of the Visitor classic design pattern according to the Gang of Four: 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.
  • 10.
    © 2025 HaimMichael 20251125 The Programming Problem (4 out of 4)  The Visitor pattern decouples the operations from the object structure, allowing us to define new operations without changing the element classes.
  • 11.
    © 2025 HaimMichael 20251125 The Traditional Implementation (1 out of 4)  Implementing the Visitor classic design pattern the traditional way will require having an object that plays the role of a visitor. That object will pays a visit to each one of the objects by calling a method (the accept method) on each one of these objects. Inside the accept method, the visit method will be invoked on the Visitor object. The reference of the visited object will be passed over to it.
  • 12.
    © 2025 HaimMichael 20251125 The Traditional Implementation (2 out of 4) public interface Expression { Integer accept(Visitor visitor); } public interface Visitor { Integer visit(Number num); Integer visit(Total total); Integer visit(Multiplication mul); Integer visit(Difference diff); } public final class Number implements Expression { public final int value; public Number(int value) { this.value = value; } public Integer accept(Visitor visitor) { return visitor.visit(this); } }
  • 13.
    © 2025 HaimMichael 20251125 The Traditional Implementation (3 out of 4) public interface Expression { Integer accept(Visitor visitor); } public final class Multiplication implements Expression { public final Expression left, right; public Multiplication(Expression left, Expression right) { this.left = left; this.right = right; } public Integer accept(Visitor visitor) { return visitor.visit(this); } } public class Total implements Expression { ... } public class Subtraction implements Expression { ... }
  • 14.
    © 2025 HaimMichael 20251125 The Traditional Implementation (4 out of 4) public class SimpleVisitor implements Visitor { public Integer visit(Number num) { return num.value; } public Integer visit(Total ob) { return ob.left.accept(this) + ob.right.accept(this); } public Integer visit(Multiplication ob) { return ob.left.accept(this) * ob.right.accept(this); } public Integer visit(Difference ob) { return ob.left.accept(this) - ob.right.accept(this); } }
  • 15.
    © 2025 HaimMichael 20251125 The Modern Implementation (1 out of 4)  Implementing the Visitor classic design pattern when using Record Classes, Sealed Classes and Pattern Matching might become simpler.  We will define a method that will take an Expression object that using pattern matching will evaluate it according to its type.
  • 16.
    © 2025 HaimMichael 20251125 The Modern Implementation (2 out of 4) public sealed interface Expression permits Number, Total, Difference, Multiplication {} public record Number(int value) implements Expression {} public record Total(Expression left, Expression right) implements Expression {} public record Difference (Expression left, Expression right) implements Expression {} public record Multiplication (Expression left, Expression right) implements Expression {}
  • 17.
    © 2025 HaimMichael 20251125 The Modern Implementation (3 out of 4) public class Evaluator { public int evaluate(Expression expression) { return switch (expression) { case Number(int value) -> value; case Total(Expression left, Expression right) -> evaluate(left) + evaluate(right); case Multiplication(Expression left, Expression right) -> evaluate(left) * evaluate(right); case Difference(Expression left, Expression right) -> evaluate(left) - evaluate(right); }; } }
  • 18.
    © 2025 HaimMichael 20251125 The Modern Implementation (4 out of 4) public class Program { public static void main(String[] args) { Expression expression = new Multiplication( new Total(new Number(3),new Number(2)), new Total(new Number(6),new Number(4))); System.out.println(new Evaluator().evaluate(expression)); } }
  • 19.
    © 2025 HaimMichael 20251125 The Benefits  With sealed classes, we explicitly restrict which subclasses can extend a type. That ensured every subclass is handled.  We get less boilerplate thanks to using records. When defining a record type, we automatically get a constructor, getters, setters, equals, hashCode and toString.
  • 20.
    © 2025 HaimMichael 20251125 The Benefits  The use of pattern matching eliminates double dispatch. Traditionally, implementing the visitor pattern meant having double dispatch, requiring us to define a separated version of visit(c) for each variant.  We enjoy better performance with pattern matching & switch expressions. The pattern matching in Java is optimized by the JVM.
  • 21.
    © 2025 HaimMichael 20251125 The Benefits  Using pattern matching, the logic is explicitly expressed in one place rather than scattered across many visitor classes. Our code becomes clearer and simpler to maintain.
  • 22.
    © 2025 HaimMichael 20251125 Questions & Answers Thanks for Your Time! Haim Michael haim.michael@lifemichael.com WhatsApp +972+54+6655837 https://meetup.com/lifemichael https://tinyurl.com/javamonthly