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, you can 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):
Assuming you have your legacy objects Legacy1
and Legacy2
which you cannot change, which have specific business methods:
public final class Legacy1 {
public void someBusinessMethod1(){
...
}
}
and
public final class Legacy2 {
public void anotherBusinessMethod(){
...
}
}
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);
}
}
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();
}
}
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: