Example: Suppose you were manipulating geometric shapes (Bryce will love this example), and you wanted to determine the intersection of two arbitrary shapes.
def process(shape1, shape2)\n shape1.intersect(shape2) # This is the first dispatch\nendNow, the way an intersection is caculated is different depending on your shapes. For example, finding the intersection of two rectangles is done differently from finding the intersection of two circles, which in turn is different from a rectangle and a circle.
Now, if we know the type of one of the shapes, the algorithm is easy to write for any other shape. For example:
class Rectangle\n def intersect_rectangle(rect)\n puts "Intersecting a Rectangle and a Rectangle"\n end\n\n def intersect_circle(circle)\n puts "Intersecting a Rectangle and a Circle"\n end\n end\n\n class Circle\n def intersect_rectangle(rect)\n puts "Intersecting a Circle and a Rectangle"\n end\n\n def intersect_circle(circle)\n puts "Intersecting a Circle and a Circle"\n end\n end\nNotice that each type knows how to intersect itself with both a circle and a rectangle.
Suppose I execute the code: rect.intersect(shape)? We dispatch to the retangle's intersection (this is the first dispatch). We don't know the type of shape, but we do know the type of self (which is a rectangle). Well, the problem is simple if we know one of the type (which we do). We just dispatch again. Here is the intersection code for rectangle:
class Rectangle\n def intersect(shape)\n shape.intersect_rectangle(self) # this is the second dispatch\n end\n endThe code for circle is a mirror image.
class Circle\n def intersect(shape)\n shape.intersect_circle(self) # this is also a second dispatch\n end\n endFinally, we put it together:
c1 = Circle.new\n c2 = Circle.new\n r1 = Rectangle.new\n r2 = Rectangle.new\n\n process(c1,c2)\n process(r1, r2)\n process(r1, c1)\n process(c1, r1)And the output is
Intersecting a Circle and a Circle\n Intersecting a Rectangle and a Rectangle\n Intersecting a Circle and a Rectangle\n Intersecting a Rectangle and a CircleIn short, double dispatch is a way of doing type discovery.
Advantages: it is fast and bounded. It takes just two polymorphic calls to resolve the types. A dynamic cast in Java or C++ is generally slower and depends on the shape of the inheritance tree.
Disadvantages: the number of dispatching methods grows with the square of the number of types to distinquish.