This is the second post in a loosely scheduled series of articles about the Visitor Pattern. Please find the first one here.
One of those things we've learned last time is the simple fact, that the Visitor will only visit "leafs" in a complex class hierarchy. Otherwise the polymorphic type dispatching will not work. Well, but we are sometimes faced with situations, where this limitation is not acceptable. Let's look at some fancy example, to illustrate my explanations. And since we all spent our two years military service with the imperial guards, here's an obvious taxonomy of different star wars vehicle types. Usually complex type systems consist of several levels of abstraction, like our example. Here we have three:
- Either everything is the same, namely a Vehicle
- or it is a Vehicle Class like StarShip or Walker,
- or it is a concrete Vehicle like, well ... the BWing, for instance.
// StarShips... public abstract class StarShip extends Vehicle{ public void fly(){ System.out.println("Yeaahhh, I'm flying, Baby."); } } // Walker public abstract class Walker extends Vehicle{ public void walk(){ System.out.println("I'm going for a walk."); } }As a fleet commander we know for sure that we can utilize the Visitor Pattern to command our
Vehicle [] fleet = { new BWing(), new XWing(), new AtAt() }
.
A fleet lieutenant would do it like this:
// clumsy fleet lieutenant's way to command his units public class ClumsyVisitor implements Visitor{ public void visit(BWing bwing) { bwing.fly(); } public void visit(XWing xwing) { xwing.fly(); } public void visit(AtAt atat) { atat.walk(); } }But we would not have become commander if we would unnecessarily repeat ourselves. As you've already realized, this is code duplication since we don't operate on the correct level of abstraction. The proper way is calling methods on the Vehicle Classes, not on the concrete Vehicles. But again, how to we do that? Here's the solution:
public abstract class AbstractVehicleClassVisitor implements Visitor { public abstract void visit(Walker walker); public abstract void visit(StarShip starShip); public void visit(BWing bwing) { visit((StarShip) bwing); } public void visit(XWing xwing) { visit((StarShip) xwing); } public void visit(AtAt atat) { visit((Walker) atat); } }Tata: We've just introduced the proper level of abstraction to our Visitor as well by deriving a new type from Visitor, the AbstractVehicleClassVisitor. Or to be more precise, we defined the missing
visit(Walker)
and visit(StarShip)
methods and delegate the 'leaf visits' to these new abstract methods. Visiting Vehicle Classes is now done by extending the AbstractVehicleVisitor. Hence our fleet commander command simply looks like:
// that's how we command! public class MoveVisitor extends AbstractVehicleClassVisitor{ public void visit(Walker walker) { walker.walk(); } public void visit(StarShip starShip) { starShip.fly(); } }Calling for the troops breaks down to
Vehicle [] fleet = { new BWing(), new XWing(), new AtAt() }; for(Vehicle unit : fleet) { unit.accept(new MoveVisitor()); } /* Which would output Yeaahhh, I'm flying, Baby. Yeaahhh, I'm flying, Baby. I'm going for a walk. */Yep, that's all. Easy, isn't?
No comments:
Post a Comment