In the spirit of the acyclic visitor pattern, separate your visitors into subclass specific ones. Note that you'll still need the type check, but it's contained to the type being visited:
Here are the visitor interfaces:
interface IVisitor {
}
interface IRouterVisitor extends IVisitor {
void visit(Router router);
}
interface INetworkCardVisitor extends IVisitor {
}
interface IWirelessNetworkCardVisitor extends INetworkCardVisitor {
void visit(WirelessNetworkCard card);
}
interface IWiredNetworkCardVisitor extends INetworkCardVisitor {
void visit(WiredNetworkCard card);
}
The concrete visitors will look like this:
class WiredVisitor implements IWiredNetworkCardVisitor, IRouterVisitor {
// ...
}
class WirelessVisitor implements IWirelessNetworkCardVisitor, IRouterVisitor {
// ...
}
And the visited objects:
interface INetworkElement {
void accept(IVisitor visitor);
}
class Router implements INetworkElement {
@Override
public void accept(IVisitor visitor) {
if (visitor instanceof IRouterVisitor) {
((IRouterVisitor)visitor).visit(this);
}
}
}
interface INetworkCard extends INetworkElement {}
class WiredNetworkCard implements INetworkCard {
@Override
public void accept(IVisitor visitor) {
if (visitor instanceof IWiredNetworkCardVisitor) {
((IWiredNetworkCardVisitor)visitor).visit(this);
}
}
}
class WirelessNetworkCard implements INetworkCard {
@Override
public void accept(IVisitor visitor) {
if (visitor instanceof IWirelessNetworkCardVisitor) {
((IWirelessNetworkCardVisitor)visitor).visit(this);
}
}
}
In those type checks, you can also throw an error if the type is not the expected one, depending on what you'd like to happen in that case.
Router <--> IRouterVisitor
– Merry