The visitor pattern is exactly what you're trying to achieve. However, a "good old-fashioned polymorphism" should do just fine for what you need. For example :
abstract class BaseThing {
abstract public void doSomething();
}
class ThingA extends BaseThing {
public void doSomething() {
System.out.println("ThingA...");
}
}
class ThingB extends BaseThing {
public void doSomething() {
System.out.println("ThingB...");
}
}
class ThingC extends BaseThing {
public void doSomething() {
throw new UnsupportedOperationException("Cannot call this on ThingC");
}
}
and then
class ThingHandler {
public void doSomething(BaseThing thing) {
try {
thing.doSomething();
} catch (UnsupportedOperationException e) {
throw new IllegalArgumentException("Can't handle thing!");
}
}
}
Thus
ThingHandler handler = new ThingHandler();
handler.doSomething(new ThingA()); // -> ThingA...
handler.doSomething(new ThingB()); // -> ThingB...
handler.doSomething(new ThingC()); // -> IllegalArgumentException: Can't handle thing!
You have mentioned "it needs to sort out what specific kind of thing it is", so all you need now is have your BaseThing
have an abstract method that will return a Comparator
and each ThingA
, etc. will implement it and return the proper comparator for the ThingHandler
class to sort. Each BaseThing
implementation can perform the specific operations or return some kind of value that you'd need in ThingHandler
(you could even pass the ThingHandler
instance in the BaseThing.doSomething
method...)
But if the Visitor pattern is really what you need, here is an example for your need :
interface IThing {
public void accept(ThingHandler handler);
}
interface IThingHandler {
public void visit(ThingA a);
public void visit(ThingB b);
//...
}
class ThingA implements IThing {
public void accept(IThingHandler h) {
h.visit(this);
}
public String getSomeValueA() {
return "Thing A";
}
}
class ThingB implements IThing {
public void accept(IThingHandler h) {
h.visit(this);
}
public String getSomeValueB() {
return "Thing B";
}
}
// ...
class ThingHandler implements IThingHandler {
public void visit(ThingA thing) {
// sort according to ThingA
System.out.println(thing.getSomeValueA() + " has visited");
doSomething(thing);
}
public void visit(ThingB thing) {
// sort according to ThingB
System.out.println(thing.getSomeValueB() + " has visited");
doSomething(thing);
}
private void doSomething(IThing thing) {
// do whatever needs to be done here
}
}
Then
IThingHandler handler = new ThingHandler();
new ThingA().accept(handler); // -> Thing A has visited
new ThingB().accept(handler); // -> Thing B has visited
//...
But since this means maintaining the IThingHandler
interface every time a new IThing
class is implemented, I prefer suggesting the first modified/simplified implementation of the pattern. However, feel free to adapt the pattern for your need and don't stop yourself because it doesn't exactly look like the described visitor pattern.
The two questions to ask are
- "who is responsible to handle the operation?"
- "who is responsible to hold the necessary data to perform the operation?"
I usually prefer keeping most of the concrete at the same place and generalize elsewhere; it helps maintaining (i.g. adding and removing features). Although the visitor pattern helps to centralize the operation in a same class...