310 likes | 429 Views
Where Were We?. Attempting to spray ants with ant poison and (generic) insects with generic spray If there is not a complete match between the signatures of methods in base and derived classes there is no polymorphism Compiler determines the call by using the declared type Now apply this.
E N D
Where Were We? • Attempting to spray ants with ant poison and (generic) insects with generic spray • If there is not a complete match between the signatures of methods in base and derived classes there is no polymorphism • Compiler determines the call by using the declared type • Now apply this
public class Poison{ public void spray(Insect aBug){ System.out.println(“Generic Spray”); } public void spray(Ant antBug){ System.out.println(“Generic Ant Spray”); } }; public class AntPoison extends Poison{ public void spray(Ant antBug){ System.out.println(“Spraying Ant with Ant spray”); } };
Remarks • It appears that we are attempting to use polymorphism both on the Poison type and the Insect type • The only polymorphism that will work is with a static reference to Poison (and a run-time reference to either Poison or AntPoison) and a static reference to Ant. Period
Test This Poison container = new AntPoison(); Insect bugType = new Ant(); container.spray(bugType); bugType = new Insect(); container.spray(bugType); Produces Generic Spray Generic Spray
public class Poison{ public void spray(Insect aBug){ System.out.println(“Generic Spray”); } public void spray(Ant antBug){ System.out.println(“Generic Ant Spray”); } }; public class AntPoison extends Poison{ public void spray(Ant antBug){ System.out.println(“Spraying Ant with Ant spray”); } };
Why? • Because there is no spray method in Ant with an insect parameter • But, calling Poison container = new AntPoison(); Container.spray(new Ant()); Container.spray(new Insect());
Results • Produces Spraying ant with ant spray Generic spray • Which at least works on ants. • Doesn’t work properly on insects
Use Polymorphism Properly • Can’t use 2 different container references and 2 different parameter references at one time • Do it in two steps • Bounce from the Poison class to the Insect class using beingSprayed() methods in all classes • Make use of this, because it is statically determined
Fix up Insect public class Poison{ public void spray (Insect aBug){ System.out.println(“Generic Spray”); aBug.beingSprayed(this); } } public class AntPoison{…} //no //being Sprayed
Put in the Bounce Call public class Insect{ public void beingSprayed(Poison aPoison){ System.out.println(“Insect sprayed with poison”);} Public class Ant extends Insect{ public void beingSprayed(Poison aPoison){ System.out.println(“Ant sprayed with poison”); }}
Note • Because we used this, the actual parameter of beingSprayed is always of type Poison • Insect is now working correctly
Test Poison container = new AntPoison(); Insect aBug = new Ant(); container.spray(aBug); aBug = new Insect(); container.spray(aBug); Produces Generic Spray Ant being sprayed with poison Generic Spray Insect being sprayed with poison
Problem • We are only spraying with generic poison • In order to make use of a spray method in the AntPoison class we must repeat the spray() method with exactly the same signature in the AntPoison derived class • Furthermore, we need a beingSprayed() method of each poison type in both Insect and Ant
public class Poison{ public void spray(Insect aBug){ aBug.beingSprayed(this); } } ; public class AntPoison extends Poison{ public void spray(Insect aBug){ aBug.beingSprayed(this); } };
public class Insect{ public void beingSprayed(Poison aPoison){ System.out.println(“Insect sprayed with generic poison”); } public void beingSprayed(AntPoison aPoison){ System.out.println(“Insect sprayed with ant poison”); } }; Public class Ant extends Insect{ public void beingSprayed(Poison aPoison){ System.out.println(“Ant sprayed with generic poison”); } public void beingSprayed(AntPoison aPoison){ System.out.println(“Ant sprayed with ant poison”); } };
Remarks • To insure polymorphism after the bounce Insect and Ant must have 2 beingSprayed() methods with different signatures (i.e. parameters) • To have the ability to use ant poison the spray() method is repeated with the same signature in both Insect and Ant
Let’s Test This Poison container = new AntPoison(); Insect aBug = new Ant(); container.spray(aBug); aBug = new Insect(); container.spray(aBug); Poison container = new Poison(); Insect aBug = new Ant(); container.spray(aBug); aBug = new Insect(); container.spray(aBug);
Hooray! Ant sprayed with ant poison Insect sprayed with ant poison Ant sprayed with generic poison Insect sprayed with generic poison
What we Have Poison Insect beingSprayed(Poison) Spray(Insect) beingSprayed(AntPoison) AntPoison Ant beingSprayed(Poison) Spray(Insect) beingSprayed(AntPoison) • Methods in Poison and AntPoison are identical!! • Note output in Insect classes--Insect shouldn’t know anything about Poison
One Way Out • Let the beingSprayed methods call other methods inthe Poison classes • Call them sprayIt() • Param types are with Insect and Ant • Put the print statements into the Poison (Visitor) classes
We Obtain Poison Insect Spray(Insect) beingSprayed(Poison) SprayIt(Insect) SprayIt(Ant) print statements Ant AntPoison beingSprayed(Poison) SprayIt(Insect) SprayIt(Ant) Visitor Object Structure
Now Simplify • The above is the general solution to double dispatch • It is too general for our purposes • Make Insect and Poison abstract • Begin with the Object Structure and eliminate Spray() from Poison • SprayIt(Insect) is never called, so eliminate • We obtain
Poison Insect beingSprayed(Poison) SprayIt(Ant) Ant Ant beingSprayed(Poison) SprayIt(Ant) Visitor Object Structure Poison.sprayIt(this)
Now Add the Roaches • Call beingSprayed() sprayAnt(), because Insect is abstract • You will need 2 methods, sprayAnt(Ant) as well as sprayRoach(Roach) in Poison • Note the excess baggage, e.g. spraying a roach with ant poison
The Visitor Pattern • Is a GoF Pattern • Purpose: To implement operations on the objects of a structure without keeping those operations in the object themselves • Key Idea: The operations that are executed depend on both the type of the operation and the type of the node to which it is applied • Advantage: Extension is dead easy • Disadvantage: Methods grow like weeds
In Our Case • Insectis the object structure • Poison is the visitor • Note the duplication in code—dangerous! • The Insect classes have to know about the Poison classes • There is a better way! • Do a double bounce
Structure of the Visitor Poison sprayAnt(Ant); sprayRoach(Roach); AntPoison sprayAnt(Ant); sprayRoach(Roach); RoachPoison sprayAnt(Ant); sprayRoach(Roach);
The Visitor Pattern • Main idea: use an iterator or traversal to visit each node of the object structure and visit it with the corresponding node of the visitor structure • The object structure classes have no knowledge of the visitor classes • What gets carried out depends on two things • The type of the visitor • The type of the object node • Hence double dispatch
Object Structure Visited Insect getSprayed(Poison); Ant getSprayed(Poison p) Roach getSprayed(Poison p) p.sprayAnt(this); p.sprayRoach(this);
Structure of the Visitor Pattern Visitor VisitConcreteElementA(ConcreteElementA) VisitConcreteElementB(ConcreteElementB) ConcreteVisitor1 ConcreteVisitor2 VisitConcreteElementA(ConcreteElementA) VisitConcreteElementA(ConcreteElementA) VisitConcreteElementB(ConcreteElementB) VisitConcreteElementB(ConcreteElementB) The Visitor
Object Structure Element ObjectStructure Accept(Visitor) ConcreteElementA ConcreteElementA Accept(Visitor v) Accept(Visitor v) OperationA() OperationB() v->VisitConcreteElementA(this) v->VisitConcreteElementB(this)