Coupling and Callbacks(Python)

Started by
2 comments, last by mikeman 17 years, 2 months ago
I will give a very simple example of my situation. Let's say we have a World class, an Actor class, and a Bullet class. The World can contain several Actors and Bullets. Furthermore, the Actor should be able to shoot bullets into the world, but not know anything about the World class, to avoid coupling. The way I've been doing this is that there is an Actor::shootBullets() method that returns a list of bullets that the actor has shot. The World can call this method, get the result and append it to its list of bullets. While this works, yesterday it dawned to me that I could use an alternative method, using callbacks. Consider:

class Actor:
    def update(self,dt,shoot_bullet_callback):
        if (self.is_shooting):
            shoot_bullet_callback(Bullet())

class World:
   def addbullet(self,bullet):
       self.bullets.add(bullet)
   def update(self,dt):
       for actor in self.actors:
           actor.update(dt,self.addbullet) 


In this example, the World calls the Actor::Update method, and passes the function World::addbullet as a callback. The Actor can use this to shoot bullets. I see it like a "pipe" that connects World and Actor, but Actor does not need to know about the World. All the Actor needs to work is to provide it with a callback with the right interface, and nothing more. How does this sound to everyone? I keep thinking it over and I can't seem to find anything wrong with it. But something in my mind nags me that I somehow violate an OO rule here and can't put my finger on it. What do you think?
Advertisement
I think the callback approach is perfectly fine. It has two advantages over the "return a list of bullets" approach, IMO:

  • Easier to understand.
  • Less error prone. For example, who would be responsible for removing fired bullets from the list? Would it be always the same list, or always a new list? What happens if you forget to update the list?


On the subject of decoupling, your callback requires the actor to make its own bullets. If you have lots of different weapon types used by several types of actors, you might not want to do this. Another problem: What happens if you try to shoot the same bullet object twice? Instead, you could pass a "gun" object with a "fire(position, direction)"-method to the actor. Then the gun would be responsible for creating bullets and placing them in the world. This would make adding weapons that fire multiple projectiles (shotgun!) relatively painless.

However, this might be overkill for a simple game.
I like signals/callbacks, but I don't like the details of your implementation. Why is the same callback re-passed-in each frame? What if more than one thing cares about the Actor firing a bullet? What if nobody cares? See, you're not coupling Actor to World, but you're "not coupling" it with a nod and a wink and the provision of exactly what World needs and no more.

I'd suggest you upgrade this to a full signal/slot system, or at least the bits that you need. Have an Actor method that allows a shoot_bullet callback to be added to the list of callbacks. When Actor shoots a bullet, any and all of those callbacks are called. Here's an example signal implementation, though you may be able to get away with something considerably simpler.
Ok guys, thanks for the comments. Sneftel, the example was made just to demonstrate the idea, but you're right that you don't need to pass the callback every frame. Thanks for the link, too.

This topic is closed to new replies.

Advertisement