double dispatch pattern

Discussion in 'iOS Programming' started by MrFusion, Mar 27, 2014.

  1. MrFusion macrumors 6502a

    Joined:
    Jun 8, 2005
    Location:
    West-Europe
    #1
    The sprite kit reference guide mentions the double dispatch pattern and refers to this wikipedia article.

    It's not entirely clear to me how to implement this double dispatch pattern.
    As far as I understand it: You have two objects. You call the first object with a reference to the second object as a parameter. You end up in a different function based on the type of the second object. Based on the function you are in, you call a method on the second object.

    Some code should make it easier to understand. Suppose I have two custom classes A and B.
    Keep in mind that you don't know (and don't have to know) which classes your objects have.
    You also don't know which is the first or second object in the contact.

    Code:
    -(void) didBeginContact:(SKPhysicsContact *)contact
    {
    	SKNode *firstNode  = contact.bodyA.node;  //could be class A or B
    	SKNode *secondNode = contact.bodyB.node; //could be class A or B
    	[firstNode didBeginContact:contact
    		  with:&secondNode];
    }
    
    implementation of A:
    Code:
    -(void) didBeginContact:(SKPhysicsContact *) contact
    				   with:(ClassA **) node
    {
     // class A with A contact => do nothing
    }
    
    -(void) didBeginContact:(SKPhysicsContact *) contact
    		  with:(ClassB **) node
    {
     // class A with B contact => reverse course of A and B
     [self reverseCourse];
      [*node reverseCourse]; 
    }
    
    
    implementation of B:
    Code:
    -(void) didBeginContact:(SKPhysicsContact *) contact
    		   with:(ClassA **) node
    {
     // class B with A contact => reverse course of A and B
      [self reverseCourse];
      [*node reverseCourse]; 
    }
    
    -(void) didBeginContact:(SKPhysicsContact *) contact
    		   with:(ClassB **) node
    {
     // class B with B contact => explode both objects
        [self explode];
        [*node explode];
    }
    
    
    But of course this code will not work because objective -c doesn't look at the argument types. It just sees twice didBeginContact:with: in both classes.

    My question: how is double dispatch implemented? What am I missing here?
     
  2. ChOas macrumors regular

    Joined:
    Nov 24, 2006
    Location:
    The Netherlands
    #2
    Would examining the classtype within the function work for you ?

    Example for your class A
    Code:
    
    -(void) didBeginContact:(SKPhysicsContact *) contact
    				   with:(id *) node {
     if ([*node isMemberOfClass:[ClassB class]]) 
     {
       [self reverseCourse];
       [*node reverseCourse]; 
     }
    }
    
     
  3. MrFusion thread starter macrumors 6502a

    Joined:
    Jun 8, 2005
    Location:
    West-Europe
    #3
    Doesn't that go against the double dispatch pattern?
    It's not that I can't hack together something functioning, I want to follow the correct principle.
     
  4. ChOas macrumors regular

    Joined:
    Nov 24, 2006
    Location:
    The Netherlands
    #4
    Hmmm. As far as I know (and please, anyone, correct me because I want to learn) you can't really do that within Objective-C.
     
  5. MrFusion thread starter macrumors 6502a

    Joined:
    Jun 8, 2005
    Location:
    West-Europe
    #5
    From the sprite kit programming guide:

    "When your game needs to work with contacts, you need to determine the outcome based on both bodies in the collision. Consider studying the Double Dispatch Pattern and using it to farm out the work."

    I kinda agree with you, but if the wizards at Apple mention this particular pattern then surely there is probably some way to do this that doesn't require introspection of the object?
     
  6. ChOas, Mar 27, 2014
    Last edited: Mar 27, 2014

    ChOas macrumors regular

    Joined:
    Nov 24, 2006
    Location:
    The Netherlands
    #6
    Hmmm I just read those docs. And as for me I'm reading something like: "Don't let your scenecontroller/director figure everything out. But leave it to the objects themselves like using double dispatch."

    Then again, that is just my interpretation :)
     
  7. MrFusion thread starter macrumors 6502a

    Joined:
    Jun 8, 2005
    Location:
    West-Europe
    #7
    Maybe it's the visitor pattern that is supposed to be implemented.

    This works and looks (relatively) elegant.

    Code:
       NSLog(@"Hello, World!");
       ClassA *a = [[ClassA alloc] init];
    		ClassB *b = [[ClassB alloc] init];
    	 	[a acceptVisitor:a
    					with:@"aa"];
    
    		[a acceptVisitor:b
    					with:@"ab"];
    		
    		
    		[b acceptVisitor:a
    					with:@"ba"];
    		
    		[b acceptVisitor:b
    					with:@"bb"];
    

    Class A:
    Code:
    
    -(void) acceptVisitor:(id) visitor
    				 with:(id) contact
    {
    	[visitor didVisitClassA:self
    					   with:contact];
    }
    
    
    -(void) didVisitClassA:(id) visited
    				  with:(id) contact
    {
    	//visitor (=self) is classA, the visited is classA
    	//=> do nothing
    	NSLog(@"do nothing");
    }
    
    -(void) didVisitClassB:(id) visited
    				  with:(id) contact
    {
    	//visitor (=self) is classA, the visited is classB
    	[visited explodeWith:self];
    }
    
    
    //actions
    -(void) reverseCourse:(id) contact
    {
    	NSLog(@"reverse A (%@)",contact);
    }
    
    
    Class B:
    Code:
    
    -(void) acceptVisitor:(id) visitor
    				 with:(id) contact
    {
    	[visitor didVisitClassB:self
    					   with:contact];
    }
    
    -(void) didVisitClassA:(id) visited
    				  with:(id) contact
    {
    	//visitor (=self) is classB, the visited is classA
    	//=> explose
    	[self explodeWith:visited];
    }
    
    
    -(void) didVisitClassB:(id) visited
    				  with:(id) contact
    {
    	//visitor (=self) is classB, the visited is classB
    	//=> reverse course
    	[self reverseCourse];
    	[visited reverseCourse];
    }
    
    //actions
    -(void) explodeWith:(id) missle
    {
    	NSLog(@"exploding");
    }
    
    -(void) reverseCourse
    {
    	NSLog(@"reverse B");
    }
    
     

Share This Page