I make regular use of the visitor pattern in my code. When a class hierarchy has a visitor implemented, I use it as an alternative to instanceof
and casting. However it leads to some pretty awkward code which I'd like to improve.
Consider the contrived case:
interface Animal {
void accept(AnimalVisitor visitor);
}
class Dog implements Animal {
void accept(AnimalVisitor visitor) {
visitor.visit(this);
}
}
class Cat implements Animal {
void accept(AnimalVisitor visitor) {
visitor.visit(this);
}
}
interface AnimalVisitor {
default void visit(Cat cat) {};
default void visit(Dog dog) {};
}
In the majority of cases, to do something specific to dogs only (for example) I implement a visitor that implements the logic in its visit
method - just as the pattern intends.
There are case, however, in which I want to return an optional dog from the visitor to use outside.
In these case I end up with some pretty ugly code:
List<Dog> dogs = new ArrayList<>();
animal.accept(new AnimalVisitor() {
void visit(Dog dog) {
dogs.add(dog);
}
}
Optional<Dog> possibleDog = dogs.stream().findAny();
I can't assign possibleDog
directly inside the visitor because it's not a final variable, hence the list.
This is pretty ugly and inefficient just to get around requirement for effective finality. I'd be interested in ideas of alternatives.
Alternatives I've considered:
Turning the visitor into a generic which can be given a return value
interface Animal {
<T> T accept(AnimalVisitor<T> visitor);
}
interface AnimalVisitor <T> {
default Optional<T> visit(Dog dog) { return Optional.empty(); }
default Optional<T> visit(Cat cat) { return Optional.empty(); }
}
Creating an abstract visitor that contains most of the code and can be trivial extended to set the optional directly
abstract class AnimalCollector implements AnimalVisitor <T> {
private Optional<T> result = Optional.empty;
protected void setResult(T value) {
assert !result.isPresent();
result = Optional.of(value);
}
public Optional<T> asOptional() {
return result;
}
}
Use a stream builder instead of a list
Stream.Builder<Dog> dogs = Stream.builder();
animal.accept(new AnimalVisitor() {
void visit(Dog dog) {
dogs.accept(dog);
}
}
Optional<Dog> possibleDog = dogs.build().findAny();
But I don't find these particularly elegant. They involve a lot of boilerplate just to implement basic asA
logic. I tend to use the second solution in my code to keep the usage clean. Is there a simpler solution I'm missing?
Just to be clear, I'm not that interested in answers with some variant of "use instanceof and casts". I realise it would work in this trivial case but the situations I'm considering have quite complex use of visitors that include visiting composites and delegates which make casting impractical.
Visitor design pattern is one of the behavioral design patterns. It is used when we have to perform an operation on a group of similar kind of Objects. With the help of visitor pattern, we can move the operational logic from the objects to another class.
The Visitor design pattern is one of the twenty-three well-known Gang of Four design patterns that describe how to solve recurring design problems to design flexible and reusable object-oriented software, that is, objects that are easier to implement, change, test, and reuse.
The Visitor pattern represents an operation to be performed on the elements of an object structure without changing the classes on which it operates. This pattern can be observed in the operation of a taxi company. When a person calls a taxi company (accepting a visitor), the company dispatches a cab to the customer.
By splitting the AnimalCollector
idea in the question in two, I think we can create something quite succinct.
(These examples are based around the original AnimalVisitor
interface in the question - with such as void visit(Cat cat);
as the methods).
The 'collect to Optional' part works well extracted to a standalone class:
public class OptionalCollector<T> {
private Optional<T> result = Optional.empty();
public void setResult(T value) {
result = Optional.of(value);
}
public Optional<T> asOptional() {
return result;
}
}
Another possibility though is a bit of 'visit by lambda' style coding. A few static factory methods enable a visitor to be easily defined without declaring methods in an anonymous inner class.
These are some example factory methods:
import java.util.function.Consumer;
public class AnimalVisitorFactory {
static AnimalVisitor dogVisitor(Consumer<Dog> dogVisitor) {
return new AnimalVisitor() {
@Override
public void visit(Dog dog) {
dogVisitor.accept(dog);
}
};
}
static AnimalVisitor catVisitor(Consumer<Cat> catVisitor) {
return new AnimalVisitor() {
@Override
public void visit(Cat cat) {
catVisitor.accept(cat);
}
};
}
}
These two parts can then be combined:
import static AnimalVisitorFactory.*;
OptionalCollector<Dog> collector = new OptionalCollector<>();
animal.accept(dogVisitor(dog -> collector.setResult(dog)));
Optional<Dog> possibleDog = collector.asOptional();
This is digressing beyond what's needed for the case in the question, but note the idea could be taken a bit further in a fluent API style. With similar dogVisitor()
and catVisitor()
default methods on the AnimalVisitor
interface too, several lambdas could then be chained together to build a more complete visitor.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With