CS 535 Object-Oriented Programming & Design Spring Semester, 1999 Double Dispatching |
||
---|---|---|
© 1999, All Rights Reserved, SDSU & Roger Whitney San Diego State University -- This page last updated 06-May-99 |
Double-Dispatching
We have the following class structure
Different types of potions will have different effect on different creatures. For example a TrollPotion may kill a Troll, but have no effect on an Ant. An AntPotion may kill an ant, make a troll very upset and have no effect on other creatures.
We have a spray method in the Potions classes
class Potion { public void spray( Creature aCreature ) {} }We would like the spray method to perform differently depending on the type of potion and the type of creature without using instanceof and cases statements on types.
In particular we would like the following code to perform properly
Potion container = either Potion, TrollPotion or AntPotion; Creature aCreature = either Creature, Troll or Ant; container.spray( aCreature );What we want is double-dispatching. Java and C++ directly support single-dispatch. That is when a method is called the type of the receiver and the method name (signature) determines what method will actually be called. In double-dispatching the method called depends on the method name (signature), and the actual types of two receivers. CLOS supports this directly. In Java and C++ we need to do some work to achieve that. Solution 1 and 2 do this.
First Attempt
public class Potion { public void spray( Creature aType ) { System.out.println( "Generic spray" ); } } public class TrollPotion extends Potion { public void spray( Troll aTroll ) { System.out.println( "TrollPotion's spray with Troll" ); } } public class Creature { } public class Troll extends Creature { }Test 1 Potion container = new TrollPotion(); container.spray( new Troll() ); container.spray( new Creature () );
Output Generic spray Generic sprayTest 2 TrollPotion container = new TrollPotion(); container.spray( new Troll() ); container.spray( new Creature() );Output TrollPotion's spray with Troll Generic spray
Note that this only works when we have explicit reference to a TrollPotion.
Second Attempt
The Potion needs to have a spray method for each creature type. This will allow the code to work properly when we have a variable of type Potion contain a TrollPotion.
public class Potion { public void spray( Creature aType ) { System.out.println( "Generic spray" ); } public void spray( Troll aType ) { System.out.println( "Generic troll spray" ); } public void performSpray( Creature aType ) { System.out.println( "Potion's performSpray with Creature" ); } } public class TrollPotion extends Potion { public void spray( Troll aTroll ) { System.out.println( "TrollPotion's spray with Troll" ); } }
Test 1 Potion container = new TrollPotion(); container.spray( new Troll() ); container.spray( new Creature() );Output TrollPotion's spray with Troll Generic spray
Test 2 Potion container = new TrollPotion(); Creature aCreature = new Troll(); container.spray( aCreature ); aCreature = new Creature(); container.spray( aCreature );
Output Generic spray Generic sprayNow the problem is that if we have a creature varible with a Troll object our code does not work properly.
A Bounce Use polymorphism on the creature type solves that problem. Here is a simple solution.
public class Potion { public void spray( Creature aType ) { aType.beingSprayed( this ); } } public class TrollPotion extends Potion { } public class Creature { public void beingSprayed( Potion aPotion ) { System.out.println( "Creature's beingSprayed with Potion" ); } } public class Troll extends Creature { public void beingSprayed( Potion aPotion ) { System.out.println( "Troll's beingSprayed with Potion" ); } }Test Potion container = new TrollPotion(); Creature aCreature = new Troll(); container.spray( aCreature ); aCreature = new Creature(); container.spray( aCreature );Output Generic spray Troll's beingSprayed with Potion Generic spray Creature's beingSprayed with PotionNow we have the Creature type take care of. Since types are always determined statically in java in Potion method the "this" in "aType.beingSprayed( this )" is always treated as type Potion.
Solution 1
Adding the spray method in TrollPotion will solve this latest problem. This is a bit strange looking. All subclasses of Potion must implement the same method and it can be identical to the parent’s method. If the Potion class is abstract (or an interface) or the subclasses perform additional operations it will not look so bad. However, this requirement needs to be documented in the code. It if is not, it will cause problems later on - someone will remove the identical code or not add it to new subclasses.
public class Potion { public void spray( Creature aType ) { aType.beingSprayed( this ); } } public class TrollPotion extends Potion { public void spray( Creature aType) { aType.beingSprayed( this ); } } public class Creature { public void beingSprayed( Potion aPotion ) { System.out.println( "Creature's beingSprayed with Potion" ); } public void beingSprayed( TrollPotion aPotion ) { System.out.println( "Creature's beingSprayed with TrollPotion" ); } } public class Troll extends Creature { public void beingSprayed( Potion aPotion ) { System.out.println( "Troll's beingSprayed with Potion" ); } public void beingSprayed( TrollPotion aPotion ) { System.out.println( "Troll's beingSprayed with TrollPotion" ); } }Note that these tests work properly.
Test Potion container = new TrollPotion(); Creature aCreature = new Troll(); container.spray( aCreature ); aCreature = new Creature(); container.spray( aCreature );Output Troll's beingSprayed with TrollPotion Creature's beingSprayed with TrollPotion
Test Potion container = new Potion(); Creature aCreature = new Troll(); container.spray( aCreature ); aCreature = new Creature(); container.spray( aCreature );
Output Troll's beingSprayed with Potion Creature's beingSprayed with Potion
Solution 2 – Double Bounce
In the first solution the Creature classes knew about the Potion classes. While the Potion classes do not have any methods that directly know about the Creature classes, the Potion class structure already mirrors the Creature classes. In this solution, the Creature classes have no direct knowledge of any Potion classes. The Potion classes have direct knowledge of the Creature classes.
public class Potion { public void spray( Creature aType ) { aType.beingSprayed( this ); } public void performSpray( Creature aType ) { System.out.println( "Potion's performSpray with Creature" ); } public void performSpray( Troll aType ) { System.out.println( "Potion's performSpray with Troll" ); } } public class TrollPotion extends Potion { public void performSpray( Creature aType ) { System.out.println( "TrollPotion's performSpray with Creature" ); } public void performSpray( Troll aType ) { System.out.println( "TrollPotion's performSpray with Troll" ); } } public class Creature { public void beingSprayed( Potion aPotion ) { aPotion.performSpray( this ); } } public class Troll extends Creature { public void beingSprayed( Potion aPotion ) { aPotion.performSpray( this ); } }
Test Potion container = new TrollPotion(); Creature aCreature = new Troll(); container.spray( aCreature ); aCreature = new Creature(); container.spray( aCreature );Output TrollPotion's performSpray with Troll TrollPotion's performSpray with Creature
Copyright ©, All rights reserved.
1999 SDSU & Roger Whitney, 5500 Campanile Drive, San Diego, CA 92182-7700 USA.
OpenContent license defines the copyright on this document.
Previous    visitors since 06-May-99    Next