Reinterpreting the Visitor Pattern via sealed classes and type pattern matching for switch – Sealed and Hidden Classes
173. Reinterpreting the Visitor Pattern via sealed classes and type pattern matching for switch
The Visitor Pattern is part of Gang of Four (GoF) design patterns and its goal is to define a new operation on certain classes without the need to modify those classes. You can find on the Internet many excellent resources on this topic, so for the classical implementation we will provide here only the class diagram of our example, while the code is available on GitHub:

Figure 8.7 – Visitor Pattern class diagram (use case)
In a nutshell, we have a bunch of classes (Capacitor, Transistor, Resistor, and ElectricCircuit) that are used to create electrical circuits. Our operation is shaped in XmlExportVisitor (implementation of ElectricComponentVisitor) and consists of printing an XML document containing the electrical circuit specifications and parameters.Before continuing, consider getting familiar with the traditional implementation and output of this example available in the bunded code. Next, let’s assume that we want to transform this traditional implementation via Sealed Classes and Type Pattern Matching for switch. The expected class diagram is simpler (has fewer classes) and it looks as follows:

Figure 8.8 – Visitor Pattern reinterpreted via Sealed Classes and switch patterns
Let’s start the transformation with the ElectricComponent interface. We know that this interface is implemented only by Capacitor, Resistor, Transistor, and ElectricCircuit. So, this interface is a good candidate to become sealed as follows:
public sealed interface ElectricComponent
permits Capacitor, Transistor, Resistor, ElectricCircuit {}
Notice that we deleted the accept() method from this interface. We no longer need this method. Next, the Capacitor, Resistor, Transistor, and ElectricCircuit become final classes and the accept() implementation is deleted as well.Since we don’t rely on the traditional Visitor Pattern, we can safely remove its specific artifacts such as ElectricComponentVisitor and XmlComponentVisitor.Pretty clean, right? We remained with a sealed interface and four final classes. Next, we can write a switch that visits each component of a circuit as follows:
private static void export(ElectricComponent circuit) {
StringBuilder sb = new StringBuilder();
sb.append(“<?xml version=\”1.0\” encoding=\”utf-8\”?>\n”);
export(sb, circuit);
System.out.println(sb.toString());
}
The export(StringBuilder sb, ElectricComponent… comps) is the effective visitor:
private static String export(StringBuilder sb,
ElectricComponent… comps) {
for (ElectricComponent comp : comps) {
switch (comp) {
case Capacitor c ->
sb.append(“””
<capacitor>
<maxImpedance>%s</maxImpedance>
<dielectricResistance>%s</dielectricResistance>
<coreTemperature>%s</coreTemperature>
</capacitor>
“””.formatted(c.getMaxImpedance(),
c.getDielectricResistance(),
c.getCoreTemperature())).toString();
case Transistor t ->
sb.append(“””
<transistor>
<length>%s</length>
<width>%s</width>
<threshholdVoltage>%s</threshholdVoltage>
</transistor>
“””.formatted(t.getLength(), t.getWidth(),
t.getThreshholdVoltage())).toString();
case Resistor r ->
sb.append(“””
<resistor>
<resistance>%s</resistance>
<clazz>%s</clazz>
<voltage>%s</voltage>
<current>%s</current>
<power>%s</power>
</resistor>
“””.formatted(r.getResistance(), r.getClazz(),
r.getVoltage(), r.getCurrent(),
r.getPower())).toString();
case ElectricCircuit ec ->
sb.append(“””
<electric_circuit_%s>
%s\
</electric_circuit_%s>
“””.formatted(ec.getId(),
export(new StringBuilder(),
ec.getComps().toArray(ElectricComponent[]::new)),
ec.getId()).indent(3)).toString();
}
}
return sb.toString();
}
Mission accomplished! You can find the complete example in the bundled code.