I have following class diagram (visitor pattern implementation):
Expected result:
1) WiredVisitor should visit only Router and WiredNetworkCard
2)
You could do this for example:
public interface IVisitor<T extends INetworkCard> {
public void visit( T card );
}
And then you'd define your visitors as:
public class WiredVisitor implements IVisitor<WiredNetworkCard> {
...
}
public class WirelessVisitor implements IVisitor<WirelessNetworkCard> {
...
}
This may not be the textbook solution but it's very clean and very readable.
you can just define some interfaces and slap one on each of the sets of elements that you want it to visit...the advantages of doing it this way are:
here is an implementation of what i'm talking about for your example:
visitors:
interface Wired {
<R> R accept(Visitor<R> v);
interface Visitor<R> {
R visit(Router router);
R visit(WiredNetworkCard wiredNetworkCard);
}
}
interface Wireless {
<R> R accept(Visitor<R> v);
interface Visitor<R> {
R visit(Router router);
R visit(WirelessNetworkCard wirelessNetworkCard);
}
}
elements:
class Router implements Wired, Wireless {
@Override
public <R> R accept(Wired.Visitor<R> v) {
return v.visit(this);
}
@Override
public <R> R accept(Wireless.Visitor<R> v) {
return v.visit(this);
}
}
class WiredNetworkCard implements Wired {
@Override
public <R> R accept(Wired.Visitor<R> v) {
return v.visit(this);
}
}
class WirelessNetworkCard implements Wireless {
@Override
public <R> R accept(Wireless.Visitor<R> v) {
return v.visit(this);
}
}
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.
I think the problem here is not with the Visitor but when the Client calls accept on WiredNetworkCard
by passing a WirelessVisitor
which is a bug. It should be handled then by raising an exception at that point in time.
But all this is happening at runtime and biziclop's solution handles it at compile time itself. But what I don't like about biziclop's solution is that WiredVisitor
which implements IVisitor<WiredNetworkCard>
will also have visit(router:Router)
method which is in no way related to WiredNetworkCard