In Wikipedia sample and in GoF book, usage of Visitor pattern is started by calling accept
method on some acceptor. But why is it this way? Why can't we start calling visit
method with desired acceptor as an argument? We can still make visitor behavior depend on 2 types -- of visitor and of acceptor (double dispatch) -- and we can eliminate redundant call (as it seems to me).
Here's sample code to illustrate this:
public interface Visitor {
void visit(AcceptorA acceptor);
void visit(AcceptorB acceptor);
}
//
// Visitor which sings
//
class SingingVisitor implements Visitor {
public void visit(AcceptorA acceptor) {
System.out.println("sing A");
}
public void visit(AcceptorB acceptor) {
System.out.println("sing B");
}
}
//
// Visitor which talks
//
class TalkingVisitor implements Visitor {
public void visit(AcceptorA acceptor) {
System.out.println("talk A");
}
public void visit(AcceptorB acceptor) {
System.out.println("talk B");
}
}
//
// Acceptor subclasses
//
class AcceptorA implements BaseAcceptor {
}
class AcceptorB implements BaseAcceptor {
}
//
// Launcher class
//
class VisitorMain {
public static void main(String[] args) {
Visitor v = new TalkingVisitor();
AcceptorA a = new AcceptorA();
AcceptorB b = new AcceptorB();
v.visit(a);
v.visit(b);
v = new SingingVisitor();
v.visit(a);
v.visit(b);
}
}
Consider:
class House implements HouseAcceptor {
HouseAcceptor kitchen;
HouseAcceptor livingRoom;
void accept(HouseVisitor visitor) {
visitor.visit(this);
kitchen.accept(visitor);
livingRoom.accept(visitor);
}
}
class Kitchen implements HouseAcceptor {
void accept(HouseVisitor visitor) {
visitor.visit(this);
}
}
class LivingRoom implements HouseAcceptor {
void accept(HouseVisitor visitor) {
visitor.visit(this);
}
}
class SpeakingHouseVisitor implements HouseVisitor {
void visit(HouseAcceptor acceptor) {
System.out.println("Inside a HouseAcceptor");
}
void visit(House acceptor) {
System.out.println("Inside a House");
}
void visit(Kitchen acceptor) {
System.out.println("Inside a Kitchen");
}
void visit(LivingRoom acceptor) {
System.out.println("Inside a LivingRoom");
}
}
...
HouseAcceptor acceptor = new House();
HouseVisitor visitor = new SpeakingHouseVisitor();
...
// Doing it your way
visitor.visit(acceptor);
// Output: Inside a HouseAcceptor
// Doing it the right way
acceptor.accept(visitor);
// Output:
// Inside a House
// Inside a Kitchen
// Inside a LivingRoom
Note that if you do it your way, the runtime type of your acceptor will not make a difference: the static type will be used. By doing double dispatch you ensure that both runtime types are used.
Using your version, the following will not compile:
List<BaseAcceptor> list = ...
for(BaseAcceptor ba: list)
vi.visit(ba)
The java compiler cannot determine (statically) what ba will be, so it cannot decide at compile time which visit method to call. You would need to write an additional method:
public void visit(BaseAcceptor ba){
if(ba instanceof AcceptorA)
visit((AcceptorA)ba);
else if(ba instanceof AcceptorB)
visit((AcceptorB)ba);
}
This is not necessary using the visitor pattern.
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