Understanding Multiple Dispatch

I’ve been reading around trying to understand multiple dispatching, and why it’s so special.

On Wikipedia I came across this simple example:

 (defmethod collide-with ((x asteroid) (y asteroid))
   ;; deal with asteroid hitting asteroid
 (defmethod collide-with ((x asteroid) (y spaceship))
   ;; deal with asteroid hitting spaceship
 (defmethod collide-with ((x spaceship) (y asteroid))
   ;; deal with spaceship hitting asteroid
 (defmethod collide-with ((x spaceship) (y spaceship))
   ;; deal with spaceship hitting spaceship

What I don’t understand however, is how it’s different from just doing something like this:

class Asteroid {
  def collide(y: Asteroid)  = // deal with asteroid hitting asteroid
  def collide(y: SpaceShip) = // deal with asteroid hitting spaceship

class SpaceShip {
  def collide(y: Asteroid)  = // deal with spaceship hitting asteroid
  def collide(y: SpaceShip) = // deal with spaceship hitting spaceship

On the technical level, what is the difference? I know one uses multiple dispatch, and the other uses single dispatch with method overloading.

As I understand it, the visitor pattern is a solution to get around not having multiple dispatch in single-dispatch OOP languages, which just confuses me more since the overloading solution seems to be working just fine without employing this pattern.

So can someone explain the technical differences between doing one vs the other?

To understand the difference, there’s one more piece of the puzzle you need to understand: dynamic dispatch.

Let’s slightly modify your example by having both Asteroid and SpaceShip inherit from Sprite (as you might if this was a game), which has the same two methods, but is abstract. Now imagine some code like:

Sprite ship = new SpaceShip();

Here we can say that the static type of ship is Sprite. The compiler won’t let you pass it to a method that requires a paramter of type SpaceShip(), and it wouldn’t let you call any method that’s on SpaceShip but not Sprite.

However, the dynamic type is SpaceShip. What does that actually mean? It means that if you call ship.collide(anotherShip), it will know to use SpaceShip.collide instead of Asteroid.collide or any other implementation. Using the dynamic type to choose which method to call is called “dynamic dispatch”.

So, given that, the answer is that the second example you give can’t handle multiple dispatch based on dynamic types. E.g.

Sprite ship = new SpaceShip();
Sprite asteroid = new Asteroid();

This will fail to compile, because the compiler will only pick a method based on the dynamic type of ship and the static type of asteroid, and it won’t find one.

According to the Wikipedia article, the first example is from a language which will pick between those four methods with dynamic dispatch. If the second example was from some language which would pick between overloads on those two classes based on dynamic type, then it would also be multiple dynamic dispatch, and the difference that the article is trying to illustrate wouldn’t exist. At that point the choice would be about more general design decisions as the other answers describe.

The issue with the second solution is that each of Asteroid and Spaceship need knowledge about each other now, creating a strong dependency.
If additional classes are mixed in, each single class belonging to the group needs to be touched.

Having a central, multi dispatch class means that only two files at most need to be modified in order to add a new class to the group.

In addition, multi dispatch allows to refactor the dispatch class for a generic, weaker typed generalization later on, without changing the API.

It doesn’t appear any different when you write it as in your simplified example, but in a real application, Asteroid and Spaceship are going to be in two completely separate files, with a bunch of other unrelated methods and data mixed in. Multiple dispatch allows you to easily group all your collision logic in one place, sharing common collision-related functionality, without the distraction of code not related to that purpose.

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *