views:

141

answers:

3

I have a circular dependency in my code, and I'm not sure how to resolve it.

I am developing a game. A NPC has three components, responsible for thinking, sensing, and acting. These components need access to the NPC controller to get access to its model, but the controller needs these components to do anything. Thus, both take each other as arguments in their constructors.

ISenseNPC sense = new DefaultSenseNPC(controller, worldQueryEngine);
IThinkNPC think = new DefaultThinkNPC(sense);
IActNPC act = new DefaultActNPC(combatEngine, sense, controller);
controller = new ControllerNPC(act, think);

(The above example has the parameter simplified a bit.)

Without act and think, controller can't do anything, so I don't want to allow it to be initialized without them. The reverse is basically true as well. What should I do?

ControllerNPC using think and act to update its state in the world:

public class ControllerNPC {
   // ...
           public override void Update(long tick)
        {
            // ...
            act.UpdateFromBehavior(CurrentBehavior, tick);

            CurrentBehavior = think.TransitionState(CurrentBehavior, tick);
        }
   // ...

}

DefaultSenseNPC using controller to determine if it's colliding with anything:

 public class DefaultSenseNPC {
       // ...
            public bool IsCollidingWithTarget()
            {
                return worldQuery.IsColliding(controller, model.Target);
            }
       // ...
    }
A: 

Use two-phase construction, whereby the objects are constructed with null references to their related objects, and you then call set methods to set the references:

ISenseNPC sense = new DefaultSenseNPC(worldQueryEngine);
IThinkNPC think = new DefaultThinkNPC();
IActNPC act = new DefaultActNPC(combatEngine);
controller = new ControllerNPC();

sense.setController(controller);
think.setSense(sense);
act.setSense(sense);
act.setController(controller);
controller.setAct(act);
controller.setThink(think);

// And now the objects are ready to use.
RichieHindle
How can I ensure that this happens? Doesn't this make the code less stable, because another programmer could forget the second phase is necessary?
Rosarch
also, I'd rather not let `Controller.Act` or `Controller.Think` be set other than at initialization time.
Rosarch
@Rosarch: As soon as this programmer tries to make his erroneously-constructed objects do anything, he'll find out his mistake. To be thorough, you could make these things assert that the relevant `set` method have been called when you ask them to do stuff, and that can inform you^H^H^H^H him of exactly what the problem is.
RichieHindle
+4  A: 

Separate the model of the controller from the concrete controllerService using an interface.

This is about project references in domain driven design, I wrote a small blog about this problem some time earlier:

http://www.mellekoning.nl/index.php/2010/03/11/project-references-in-ddd/

Hace
Can you show me a code sample of what this would look like?
Rosarch
A: 

Would it be possible to use events for some of the communication between objects?

Foole
What do you envision this looking like?
Rosarch