Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 3 of 4
Blogger Derek Greer believes that using double dispatch may indicate a design issue, which could impact an application's maintainability. Read Greer's "Double dispatch is a code smell" blog post and associated comments for details.
Wikipedia's entry on double dispatch provides a C++-based example that shows it to be more than function overloading. In Listing 6, I present the Java equivalent.
public class DDDemo
{
public static void main(String[] args)
{
Asteroid theAsteroid = new Asteroid();
SpaceShip theSpaceShip = new SpaceShip();
ApolloSpacecraft theApolloSpacecraft = new ApolloSpacecraft();
theAsteroid.collideWith(theSpaceShip);
theAsteroid.collideWith(theApolloSpacecraft);
System.out.println();
ExplodingAsteroid theExplodingAsteroid = new ExplodingAsteroid();
theExplodingAsteroid.collideWith(theSpaceShip);
theExplodingAsteroid.collideWith(theApolloSpacecraft);
System.out.println();
Asteroid theAsteroidReference = theExplodingAsteroid;
theAsteroidReference.collideWith(theSpaceShip);
theAsteroidReference.collideWith(theApolloSpacecraft);
System.out.println();
SpaceShip theSpaceShipReference = theApolloSpacecraft;
theAsteroid.collideWith(theSpaceShipReference);
theAsteroidReference.collideWith(theSpaceShipReference);
System.out.println();
theSpaceShipReference = theApolloSpacecraft;
theAsteroidReference = theExplodingAsteroid;
theSpaceShipReference.collideWith(theAsteroid);
theSpaceShipReference.collideWith(theAsteroidReference);
}
}
class SpaceShip
{
void collideWith(Asteroid inAsteroid)
{
inAsteroid.collideWith(this);
}
}
class ApolloSpacecraft extends SpaceShip
{
void collideWith(Asteroid inAsteroid)
{
inAsteroid.collideWith(this);
}
}
class Asteroid
{
void collideWith(SpaceShip s)
{
System.out.println("Asteroid hit a SpaceShip");
}
void collideWith(ApolloSpacecraft as)
{
System.out.println("Asteroid hit an ApolloSpacecraft");
}
}
class ExplodingAsteroid extends Asteroid
{
void collideWith(SpaceShip s)
{
System.out.println("ExplodingAsteroid hit a SpaceShip");
}
void collideWith(ApolloSpacecraft as)
{
System.out.println("ExplodingAsteroid hit an ApolloSpacecraft");
}
}
Listing 6 follows its C++ counterpart as closely as possible. The final four lines in the main() method along with the void collideWith(Asteroid inAsteroid) methods in SpaceShip and ApolloSpacecraft demonstrate and simulate double dispatch.
Consider the following excerpt from the end of main():
theSpaceShipReference = theApolloSpacecraft;
theAsteroidReference = theExplodingAsteroid;
theSpaceShipReference.collideWith(theAsteroid);
theSpaceShipReference.collideWith(theAsteroidReference);
The third and fourth lines use single dispatch to figure out the correct collideWith() method (in SpaceShip or ApolloSpacecraft) to invoke. This decision is made by the virtual machine based on the type of the reference stored in theSpaceShipReference.
From within collideWith(), inAsteroid.collideWith(this); uses single dispatch to figure out the correct class (Asteroid or ExplodingAsteroid) containing the desired collideWith() method. Because Asteroid and ExplodingAsteroid overload collideWith(), the type of argument this (SpaceShip or ApolloSpacecraft) is used to distinguish the correct collideWith() method to call.
And with that, we have accomplished double dispatch. To recap, we first called collideWith() in SpaceShip or ApolloSpacecraft, and then used its argument and this to call one of the collideWith() methods in Asteroid or ExplodingAsteroid.
When you run DDDemo, you should observe the following output:
Asteroid hit a SpaceShip
Asteroid hit an ApolloSpacecraft
ExplodingAsteroid hit a SpaceShip
ExplodingAsteroid hit an ApolloSpacecraft
ExplodingAsteroid hit a SpaceShip
ExplodingAsteroid hit an ApolloSpacecraft
Asteroid hit a SpaceShip
ExplodingAsteroid hit a SpaceShip
Asteroid hit an ApolloSpacecraft
ExplodingAsteroid hit an ApolloSpacecraft
Now that we've fully unpacked the double dispatch technique, let's return to the Visitor pattern.
Design Patterns uses a compiler context as the Visitor pattern's motivation. Although Visitor can be used in any hierarchical context where you don't want to mix operations on a given structure with the structure's elements, we'll follow the GoF example to study this pattern.
A compiler converts source code into executable code. The analysis phase scans characters into tokens, parses the tokens to verify syntactic correctness, and creates an abstract syntax tree (AST) that represents the program as a hierarchy of nodes.
The AST is then type-checked to verify semantic correctness (variables must be declared before they are used, for example). This data structure is also commonly used to drive code generation. These operations are often performed by visitors.
Consider an expression language involving addition, subtraction, multiplication, division, negation, floating-point numbers, and parentheses. An expression AST features nodes representing all of these items except for parentheses, which determine the tree structure.
AST visitors visit these nodes and are based on a class hierarchy that's rooted in an abstract class or interface. The root type abstracts methods for visiting the different kinds of nodes, and is extended by concrete subclasses that implement specific kinds of visitors, as shown in Figure 1.
Figure 1 reveals a convention in which each visitor method begins with the word visit and ends with the name of the node class. Furthermore, the argument passed to this method has the node class as its type.
Each concrete AST class declares an accept(Visitor) method that receives a visitor class instance as its argument. This method invokes the appropriate visitor method on this
argument, and passes a reference to itself as the invoked method's argument, as illustrated by Figure 2.
Figure 2 describes an AST for the expression: 4.0+2.0*3.0. For brevity, only the code executed by NumNode's accept(Visitor) method is shown. The getName() method is used to return the name of the node, and is only invoked by the print visitor.
David Geary's Java design patterns series on JavaWorld: Learn more of the Gang of Four patterns in Java code: