One of the reasons to consider the Visitor_pattern:
A practical result of this separation is the ability to add new operations to existing object structures without modifying those structures.
Assume that you don't have the source code of third party libraries and you have added one operation on related objects.
Since you don't have object, your elements (Third party classes) can't be modified to add Visitor.
In this case, double dispatch is not possible.
So which option is generally preferred?
Option 1: Extend one more inheritance hierarchy on top of third party class and implement pattern as show in picture with double dispatch?
For a given hierarchy of Class B which extends Class A, I will add
ElementA extends A
ElementB extends B
Now ConcreteElements are derived from ElementA instead of class A.
Cons: The number of classes will grow.
Option 2: Use Visitor class a central helper class and get the work done with single dispatch.
Cons: We are not really following Visitor patter as per UML diagram.
Correct if I am wrong.
You could combine a Wrapper and Visitor to solve your problems.
Using the wrapper to add a visit method allows you to increase the usability of these objects. Of course you get the full advantages (less dependency on the legacy classes) and disadvantages (additional objects) of a wrapper.
Here's a worked-up example in JAVA (because it is pretty strict, does not do double-dispatch by itself, and I'm quite familiar with it):
1) Your legacy Objects
Assuming you have your legacy objects Legacy1 and Legacy2which you cannot change, which have specific business methods:
public final class Legacy1 {
public void someBusinessMethod1(){
...
}
}
and
public final class Legacy2 {
public void anotherBusinessMethod(){
...
}
}
2) Prepare the Wrapper
You just wrap them in a VisitableWrapper which has a visit method that takes your visitor, like:
public interface VisitableWrapper {
public void accept(Visitor visitor);
}
With the following implementations:
public class Legacy1Wrapper implements VisitableWrapper {
private final Legacy1 legacyObj;
public Legacy1Wrapper(Legacy1 original){
this.legacyObj = original;
}
public void accept(Visitor visitor){
visitor.visit(legacyObj);
}
}
and
public class Legacy2Wrapper implements VisitableWrapper {
private final Legacy2 legacyObj;
public Legacy2Wrapper(Legacy2 original){
this.legacyObj = original;
}
public void accept(Visitor visitor){
visitor.visit(legacyObj);
}
}
3) Visitor, at the ready!
Then your own Visitors can be set to visit the wrapper like so:
public interface Visitor {
public void visit(Legacy1 leg);
public void visit(Legacy2 leg);
}
With an implementation like so:
public class SomeLegacyVisitor{
public void visit(Legacy1 leg){
System.out.println("This is a Legacy1! let's do something with it!");
leg.someBusinessMethod1();
}
public void visit(Legacy2 leg){
System.out.println("Hum, this is a Legacy 2 object. Well, let's do something else.");
leg.anotherBusinessMethod();
}
}
4) Unleash the power
Finally in your code, this framework would work like this:
public class TestClass{
// Start off with some legacy objects
Legacy1 leg1 = ...
Legacy2 leg2 = ...
// Wrap all your legacy objects into a List:
List<VisitableWrapper> visitableLegacys = new ArrayList<>();
visitableLegacys.add(new Legacy1Wrapper(legacy1));
visitableLegacys.add(new Legacy2Wrapper(legacy2));
// Use any of your visitor implementations!
Visitor visitor = new SomeLegacyVisitor();
for(VisitableWrapper wrappedLegacy: visitableLegacys){
wrappedLegacy.accept(visitor);
}
}
The expected output:
This is a Legacy1! let's do something with it!
Hum, this is a Legacy 2 object. Well, let's do something else.
Drawbacks:
Quite a lot of boilerplate. Use Lombok if you develop in Java.
Quite a lot of wrapper objects instances. May or may not be a problem for you.
You need to know the specific type of the objects beforehand. This implies you know their subtype, they aren't bundles in a List. If that's the case, you have no other option but to use reflection.
There should be a possibility to add new functionality to the classes of some hierarchy, without changing the base class interface. Kinds of possible behavior should be constant, while operations for different classes should execute differently.
The Visitor Pattern allows to concentrate all that operations in one class. There might be a lot of Concrete Element classes (from the diagram), but for each of them there will be implemented visit() method in Concrete Visitor class that will define his own algorithm.
Definition and implementation of method for each subclass of Element class:
public interface Visitor {
void visit(Element element);
}
public class ConcreteVisitor implements Visitor {
public void visit(Element element) {
// implementation
}
}
The Visitor Pattern is easily extended for new operations by implementing this interface by new class with his method implementation.
The following structure encapsulates the Element class:
public lass ObjectStructure {
private Element element;
// some methods
}
This ObjectStructure class could aggregate one or several instances of Element. Presentation that Visitor acts on:
public interface Element {
void accept(Visitor visitor);
}
And implementation of accept() method in the concrete entity:
public class ConcreteElement implements Element {
public void accept(Visitor visitor) {
visitor.visit();
}
}
Using of Visitor Pattern allows to save Element hierarchy from huge logical functionality or complicated configuration.
It is desirable to add the functionality to all the classes of hierarchy while defining a new Visitor subclasses. But there could be a problem: visit() should be overriden for every hierarchy type. To avoid this it's better to define AbstractVisitor class and all leave his all visit() method bodies empty.
Conclusion: using this pattern is good when class hierarchy of type Element keeps constant. If new classes add, it usually goes to considerable changes in classes of Visitor type.
My answer is very similar to Michael von Wenckstern's, with the improvements that we have a named accept method (more like the standard pattern) and that we handle unknown concrete classes -- there's no guarantee that at some point a concrete implementation we haven't seen before won't appear on the classpath.
My visitor also allows a return value.
I've also used a more verbose name for the visit methods -- including the type in the method name, but this isn't necessary, you can call them all visit.
// these classes cannot be modified and do not have source available
class Legacy {
}
class Legacy1 extends Legacy {
}
class Legacy2 extends Legacy {
}
// this is the implementation of your visitor
abstract class LegacyVisitor<T> {
abstract T visitLegacy1(Legacy1 l);
abstract T visitLegacy2(Legacy2 l);
T accept(Legacy l) {
if (l instanceof Legacy1) {
return visitLegacy1((Legacy1)l);
} else if (l instanceof Legacy2) {
return visitLegacy2((Legacy2)l);
} else {
throw new RuntimeException("Unknown concrete Legacy subclass:" + l.getClass());
}
}
}
public class Test {
public static void main(String[] args) {
String s = new LegacyVisitor<String>() {
#Override
String visitLegacy1(Legacy1 l) {
return "It's a 1";
}
#Override
String visitLegacy2(Legacy2 l) {
return "It's a 2";
}
}.accept(new Legacy1());
System.out.println(s);
}
}
First I had to made a few assumptions about the legacy code, since you didn't provide much details about it. Let's say I need to add a new method to Legacy without reimplementing everything. This is how I'll do it:
public interface LegacyInterface {
void A();
}
public final class LegacyClass implements LegacyInterface {
#Override
public void A() {
System.out.println("Hello from A");
}
}
First extends the "contract"
public interface MyInterface extends LegacyInterface {
void B();
}
And implement it in a "decorated" way
public final class MyClass implements MyInterface {
private final LegacyInterface origin;
public MyClass(LegacyInterface origin) {
this.origin = origin;
}
#Override
public void A() {
origin.A();
}
#Override
public void B() {
System.out.println("Hello from B");
}
}
The key point is MyInterface extends LegacyInterface: this is the guarantee the implementations will benefit from both the services from the legacy code and your personnal addings.
Usage
MyInterface b = new MyClass(new LegacyClass());
I think the best approach is the Option 1: Extend one more inheritance hierarchy on top of third party class and implement the visitor pattern with double dispatch.
The problem is the number of additional classes you need, but this can be resolved with a dynamic wrapper decorator.
The Wrapper Decorator is a way to add interface implementation, methods and properties to already existing obejcts: How to implement a wrapper decorator in Java?
In this way you need your Visitor interface and put there the visit(L legacy) methods:
public interface Visitor<L> {
public void visit(L legacy);
}
In the AcceptInterceptor you can put the code for the accept method
public class AcceptInterceptor {
#RuntimeType
public static Object intercept(#This WrappedAcceptor proxy, #Argument(0) Visitor visitor) throws Exception {
visitor.visit(proxy);
}
}
The WrappedAcceptor interface defines the method to accept a visitor and to set and retrieve the wrapped object
interface WrappedAcceptor<V> {
Object getWrapped();
void setWrapped(Object wrapped);
void accept(V visitor);
}
And finally the utility code to create the Wrapper around any obect:
Class<? extends Object> proxyType = new ByteBuddy()
.subclass(legacyObject.getClass(), ConstructorStrategy.Default.IMITATE_SUPER_TYPE_PUBLIC)
.method(anyOf(WrappedAcceptor.class.getMethods())).intercept(MethodDelegation.to(AcceptInterceptor.class))
.defineField("wrapped", Object.class, Visibility.PRIVATE)
.implement(WrappedAcceptor.class).intercept(FieldAccessor.ofBeanProperty())
.make()
.load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
.getLoaded();
WrappedAcceptor wrapper = (WrappedAcceptor) proxyType.newInstance();
wrapper.setWrapped(legacyObject);
If your library does not has accept methods you need to do it with instanceof. (Normally you do twice single-dispatching in Java to emulate double dispatching; but here we use instanceof to emulate double dispatching).
Here is the example:
interface Library {
public void get1();
public void get2();
}
public class Library1 implements Library {
public void get1() { ... }
public void get2() { ... }
}
public class Library2 implements Library {
public void get1() { ... }
public void get2() { ... }
}
interface Visitor {
default void visit(Library1 l1) {}
default void visit(Library2 l2) {}
default void visit(Library l) {
// add here instanceof for double dispatching
if (l instanceof Library1) {
visit((Library1) l);
}
else if (l instanceof Library2) {
visit((Library2) l);
}
}
}
// add extra print methods to the library
public class PrinterVisitor implements Visitor {
void visit(Library1 l1) {
System.out.println("I am library1");
}
void visit(Library2 l2) {
System.out.println("I am library2");
}
}
and now in any method you can write:
Library l = new Library1();
PrinterVisitor pv = new PrinterVisitor();
pv.visit(l);
and it will print to you "I am library1";
Related
I have 2 classes Workflow1.java and Workflow2.java. At a class Selection.java I want to be able to choose between instantiating one of the 2 classes as a static member however I cannot implement the factory pattern as Workflow1 and Workflow2 cannot be subclasses since their methods are not the same. Although they achieve the same end result they do so by doing entirely different operations. Is there a design pattern for this scenario?
Example: If the classes were WalkHelper.java and DriveHelper.java, the methods you need in each are entirely different but what you are trying to achieve is the same - reach a destination. I haven't created walk() and drive() as methods as WalkHelper.java has existed in our code base and I'm adding DriveHelper.java to it.
It sounds like you can still use a Factory pattern but you may have to use an Adaptor to make them equal... Without knowing more, it's a pretty difficult question to answer.
interface IFactory {
void run();
String getResult();
}
class Workflow1Adapter implements IFactory {
Workflow1 wf1 = new Workflow1();
public void run() {
wf1.doSomething();
}
public String getResult() {
wf1.doAnother();
}
}
class Workflow2Adapter implements IFactory {
Workflow2 wf2 = new Workflow2();
public void run() {
wf2.doThatThing();
}
public String getResult() {
wf2.doReturn();
}
}
class Workflow1 {
public void doSomething() {}
public String doAnother() {}
}
class Workflow2 {
public void doThatThing() {}
public String doReturn() {}
}
For a project, I have written the following interface:
public interface IManipulation {
void applyManipulation (double value);
}
Since I would like to force all implementing classes to use a certain constructor signature, I have been considering to change the interface into something like the following abstract class:
(edit: I forgot that it's not possible to have an abstract constructor, so I changed the "solution" below a bit)
public abstract class Manipulation {
private Signal signal;
public Manipulation (Signal signal) {
this.signal = signal;
}
public abstract void applyManipulation (double value);
protected Signal getSignal () {
return signal;
}
}
The reason for wanting to force this constructor is because every implentation should have an instance of Signal available. (and it should not be possible to reassign this signal)
Is this a valid reason to replace the interface with an abstract class (and live with the limitations that come with it), or are there any other potential solutions?
instead of an abstract class you should use an init method for that purpose.
public interface MyInterface{
public void init(YourParam p);
//... other methods
}
in the init you check, if the class is allready initialised if yes, just return.
So you have still an interface and can extend from other classes.
Instead of the constructor you will call the init method for your initialization
EDIT:
public interface IManipulation {
void init(Signal s);
void applyManipulation (double value);
}
You should use abstract classes only, if you have implementation details in it, which are shared by all subclasses. For Method signatures use interfaces
You can make empty constructor private in the abstract class:
abstract class AbstractManipulation {
private final Integer signal;
private AbstractManipulation() {
signal = null;
}
public AbstractManipulation (Integer signal) {
this.signal = signal;
}
}
class Manipulation extends AbstractManipulation {
public Manipulation(Integer signal) {
super(signal);
}
// Cannot redeclare
//public Manipulation() {
//}
}
Then:
public static void main(String[] args) {
// Will not work
//Manipulation m = new Manipulation();
// This one will
Manipulation m = new Manipulation(1);
}
You should not choose for technical reasons but rather logical, ie an abstract class is used when you have a realtion with the sub-classes like for example person: student, teacher. An interface is used when you want to impose a service contract for classes that may not have a relationship between them.
Let me start with an example.
Say I have an abstract Vehicle class.
public abstract class Vehicle {
public Vehicle() {}
public abstract void ride();
}
And classes Car and Bicycle that inherit from this abstract class.
public class Car extends Vehicle {
public Car() {}
#Override
public void ride() {
System.out.println("Riding the car.");
}
}
public class Bicycle extends Vehicle {
public Bicycle() {}
#Override
public void ride() {
System.out.println("Riding the bicycle.");
}
}
When I apply the ride() method to an object of type Vehicle whose actual type can only be determined at runtime, the JVM will apply the correct version of ride().
That is, in a curried method call of the sort v.ride(), polymorphism works the expected way.
But what if I have an external implementation in form of a method that only accepts a subtype of Vehicle as an argument? So, what if I have repair(Bicycle b) and repair(Car c) methods? The uncurried polymorphic method call repair(v) won't work.
Example:
import java.util.ArrayList;
import java.util.List;
public class Main {
private static void playWithVehicle() {
List<Vehicle> garage = new ArrayList<Vehicle>();
garage.add(new Car());
garage.add(new Car());
garage.add(new Bicycle());
garage.forEach((v) -> v.ride()); // Works.
garage.forEach((v) -> {
/* This would be nice to have.
repair(v.castToRuntimeType());
*/
// This is an ugly solution, but the obvious way I can think of.
switch (v.getClass().getName()) {
case "Bicycle":
repair((Bicycle) v);
break;
case "Car":
repair((Car) v);
break;
default:
break;
}
});
}
private static void repair(Bicycle b) {
System.out.println("Repairing the bicycle.");
}
private static void repair(Car c) {
System.out.println("Repairing the car.");
}
public static void main(String[] args) {
playWithVehicle();
}
}
I have to check for the class name and downcast. Is there a better solution to this?
Edit: My actual purpose is that I'm traversing an abstract syntax tree and I happened to notice that I want double dispatch.
Ast is an abstract class from which actual AST nodes like Assign, MethodCall, or ReturnStmt inherit. body is a polymorphic list of Asts.
Code snippet:
List<Ast> body;
body.parallelStream().forEach((ast) -> {
// This one won't work.
visit(ast);
// This one will work.
if (ast instanceof Assign) {
visit((Assign) ast);
} else if (ast instance of MethodCall) {
visit((MethodCall) ast);
} else if (ast instance of ReturnStmt) {
visit((ReturnStmt) ast);
}
// etc. for other AST nodes
});
private void visit(Assign ast) {
}
private void visit(MethodCall ast) {
}
private void visit(ReturnStmt ast) {
}
My only possibilities of achieving double dispatch is either checking the class and downcasting or properly implementing the visitor pattern, right?
Answer: There is no multiple dispatch in Java and it can be simulated by instanceof or by the visitor pattern.
See here:
Java method overloading + double dispatch
See also here: https://en.wikipedia.org/wiki/Multiple_dispatch#Examples_of_emulating_multiple_dispatch
On a sidenote, exactly this is possible in C# with dynamic calls: How to build double dispatch using extensions
And this is also possible in the many languages that are compiled to JVM bytecode, e.g. Groovy was mentioned.
I am trying to wrap my head around interfaces, and I was hoping they were the answer to my question.
I have made plugins and mods for different games, and sometimes classes have onUpdate or onTick or other methods that are overridable.
If I make an interface with a method, and I make other classes which implement the method, and I make instances of the classes, then how can I call that method from all the objects at once?
You'll be looking at the Observer pattern or something similar. The gist of it is this: somewhere you have to keep a list (ArrayList suffices) of type "your interface". Each time a new object is created, add it to this list. Afterwards you can perform a loop on the list and call the method on every object in it.
I'll edit in a moment with a code example.
public interface IMyInterface {
void DoSomething();
}
public class MyClass : IMyInterface {
public void DoSomething() {
Console.WriteLine("I'm inside MyClass");
}
}
public class AnotherClass : IMyInterface {
public void DoSomething() {
Console.WriteLine("I'm inside AnotherClass");
}
}
public class StartUp {
private ICollection<IMyInterface> _interfaces = new Collection<IMyInterface>();
private static void Main(string[] args) {
new StartUp();
}
public StartUp() {
AddToWatchlist(new AnotherClass());
AddToWatchlist(new MyClass());
AddToWatchlist(new MyClass());
AddToWatchlist(new AnotherClass());
Notify();
Console.ReadKey();
}
private void AddToWatchlist(IMyInterface obj) {
_interfaces.Add(obj);
}
private void Notify() {
foreach (var myInterface in _interfaces) {
myInterface.DoSomething();
}
}
}
Output:
I'm inside AnotherClass
I'm inside MyClass
I'm inside MyClass
I'm inside AnotherClass
Edit: I just realized you tagged it as Java. This is written in C#, but there is no real difference other than the use of ArrayList instead of Collection.
An interface defines a service contract. In simple terms, it defines what can you do with a class.
For example, let's use a simple interface called ICount. It defines a count method, so every class implementing it will have to provide an implementation.
public interface ICount {
public int count();
}
Any class implementing ICount, should override the method and give it a behaviour:
public class Counter1 implements ICount {
//Fields, Getters, Setters
#Overide
public int count() {
//I don't wanna count, so I return 4.
return 4;
}
}
On the other hand, Counter2 has a different oppinion of what should count do:
public class Counter2 implements ICount {
int counter; //Default initialization to 0
//Fields, Getters, Setters
#Overide
public int count() {
return ++count;
}
}
Now, you have two classes implementing the same interface, so, how do you treat them equally? Simple, by using the first common class/interface they share: ICount.
ICount count1 = new Counter1();
ICount count2 = new Counter2();
List<ICount> counterList = new ArrayList<ICount>();
counterList.add(count1);
counterList.add(count2);
Or, if you want to save some lines of code:
List<ICount> counterList = new ArrayList<ICount>();
counterList.add(new Counter1());
counterList.add(new Counter2());
Now, counterList contains two objects of different type but with the same interface in common(ICounter) in a list containing objects that implement that interface. You can iterave over them and invoke the method count. Counter1 will return 0 while Counter2 will return a result based on how many times did you invoke count:
for(ICount current : counterList)
System.out.println(current.count());
You can't call a method from all the objects that happen to implement a certain interface at once. You wouldn't want that anyways. You can, however, use polymorphism to refer to all these objects by the interface name. For example, with
interface A { }
class B implements A { }
class C implements A { }
You can write
A b = new B();
A c = new C();
Interfaces don't work that way. They act like some kind of mask that several classes can use. For instance:
public interface Data {
public void doSomething();
}
public class SomeDataStructure implements Data {
public void doSomething()
{
// do something
}
}
public static void main(String[] args) {
Data mydataobject = new SomeDataStructure();
}
This uses the Data 'mask' that several classes can use and have certain functionality, but you can use different classes to actually implement that very functionality.
The crux would be to have a list that stores every time a class that implements the interface is instantiated. This list would have to be available at a level different that the interface and the class that implements it. In other words, the class that orchestrates or controls would have the list.
An interface is a contract that leaves the implementation to the classes that implements the interface. Classes implement the interface abide by that contract and implement the methods and not override them.
Taking the interface to be
public interface Model {
public void onUpdate();
public void onClick();
}
public class plugin implements Model {
#Override
public void onUpdate() {
System.out.println("Pluging updating");
}
#Override
public void onClick() {
System.out.println("Pluging doing click action");
}
}
Your controller class would be the one to instantiate and control the action
public class Controller {
public static void orchestrate(){
List<Model> modelList = new ArrayList<Model>();
Model pluginOne = new plugin();
Model plugTwo = new plugin();
modelList.add(pluginOne);
modelList.add(plugTwo);
for(Model model:modelList){
model.onUpdate();
model.onClick();
}
}
}
You can have another implementation called pluginTwo, instantiate it, add it to the list and call the methods specified by the interface on it.
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.
Because Visitors have no knowledge of how to navigate the private internal fields of a composed Object.
If you called Visitor.visit(something) then it would have to figure out if that something had private fields which needed transversal. To do that, you need that something to accept your Visitor. Once you decide that navigation must be in the visited objects (and not the Visitor), then you realize that you need a call back to the Visitor to tell it what the next element in the navigation path is. Typically that's the accept(...) method; however, if you attempted to make accept(...) just a wrapper to initiate navigation (by delegation to the parameter), then you need a second set of methods to tell the Visitor you're entering X now, your entering Y now.
By using the GOF approach, one can safely subclass an item being visited and modify the visiting path to include or skip additional fields. This would not impact the existing Visitors because their interface would not change. One wouldn't need to recompile subclasses of the Visitor either.
By using your suggested approach, when one added a new type into the hierarchy of to-be-visited items, one would then need to recompile all the visitors, even the visitors had no interest in the new type.
A good compromise would be:
public interface Visitable {
public void accept(Visitor v);
}
were all your "data hierarchy" implements Visitable, and your Visitor has a "convenience method" like so
public abstract class Visitor {
public void initiate(Visitable v) {
v.accept(this);
}
public abstract void accept(...);
public abstract void accept(...);
public abstract void accept(...);
}
But it's up to you if having an interface is preferable to such a base class. To me I'd favor the more loosely coupled interface, but opinions differ.
you have no double dispatch. accept usually takes an abstract visitor as an argument.