My company's legacy code is suffering from prevalent usage of instanceof switch-casing, in the form of:
if(object instanceof TypeA) {
TypeA typeA = (TypeA) object;
...
...
}
else if(object instanceof TypeB) {
TypeB typeB = (TypeB) object;
...
...
}
...
...
To make things worse, several of the TypeX classes in questions are actually wrappers of classes found in 3rd party libraries.
The suggested approach of using the visitor design pattern and dedicated visitor design pattern wrappers on 3rd party classes as seen here (instanceof -> Visitor DP) and here (Visitor DP with 3rd party classes) seems like a good approach.
However, during a code review session where this approach was suggested, the question of the additonal overhead of boilerplate code required by each refactoring of the instaceof switch-casing has lead to this mechanism being declined.
I would like to fix this ongoing issue and I am considering a generic approach to the problem:
A utility class which will wrap the visitor design pattern with generic referencing to the visited objects. The idea is to implement the generic core of the visitor utility class once and only once, and provide specific implementations to the TypeX object behaviour where needed - hopefully even reusing some implementations via OO extention of the functionality implementing classes.
My question is - has anyone here done something similiar? If not - can you point out any pros/cons that might be relevant?
EDIT : Too much boilerplate code = implementing the visitor design pattern specifically for each instance of an instanceof switch-case. This is obviously redundent and will cause a lot of code duplication, if the visitor DP is not implemented using generics.
As for the generic visitor DP utility I had in mind :
First of all, usage of reflection with the visitor DP as seen here.
Second, the following usage of generics (based on the reflective visitor):
public interface ReflectiveVisitor<GenericReturn,GenericMetaData>
{
public GenericReturn visit(Object o, GenericMetaData meta);
}
public interface ReflectiveVisitable<A,B>
{
public GenericReturn accept(Visitor visitor, GenericMetaData meta);
}
GenericReturn and GenericMetaData are interfaces aimed at providing any additionally required meta data for specific logics to be implemented, and to provide versatility for return types returned by the visitor DP.
Thanks in advance!
EDIT : Boiler plate coding when refactoring from instanceof to the visitor :
A common use case I'd have to handle is instanceof switchcasing in order to perform single API calls of concrete implementations :
public class BoilerPlateExample
...
if(object instanceof TypeA) {
((TypeA) object).specificMethodTypeA(...)......;
}
else if(object instanceof TypeB) {
((TypeB) object).completeyDifferentTypeBMethod(...)......;
}
...
...
As for the visitor design handling this?
public interface Visitor
{
// notice how I just binded my interface to a specific set of methods?
// this interface will have to be generic in order to avoid an influx of
// of dedicated interfaces
public void visit(TypeA typeA);
public void visit(TypeB typeB);
}
public interface Visitable
{
public void accept(Visitor visitor);
}
public class BoilerPlateExampleVisitable<T> implements Visitable
{
// This is basically a wrapper on the Types
private T typeX;
public BoilerPlateExampleVisitable (T typeX) {
this.typeX = typeX;
}
public void accept(Visitor visitor) {
visitor.visit(typeX);
}
}
public class BoilerPlateExampleVisitor implements Visitor
{
public void visit(TypeA typeA) {
typeA.specificMethodTypeA(...)......;
}
public void visit(TypeB typeB) {
typeB.completeyDifferentTypeBMethod(...)......;
}
}
public static final BoilerPlateExampleVisitor BOILER_PLATE_EXAMPLE_VISITOR = new BoilerPlateExampleVisitor();
public static void main(....) {
TypeA object = .....; // created by factory
BoilerPlateExampleVisitable boilerPlateVisitable = VisitableFactory.create(object); // created by dedicated factory, warning due to implicit generics
boilerPlateVisitable.accept(BOILER_PLATE_EXAMPLE_VISITOR);
}