Initialization of components with interdependencies - possible antipattern?
- by Rosarch
I'm writing a game that has many components. Many of these are dependent upon one another. When creating them, I often get into catch-22 situations like "WorldState's constructor requires a PathPlanner, but PathPlanner's constructor requires WorldState."
Originally, this was less of a problem, because references to everything needed were kept around in GameEngine, and GameEngine was passed around to everything. But I didn't like the feel of that, because it felt like we were giving too much access to different components, making it harder to enforce boundaries.
Here is the problematic code:
/// <summary>
/// Constructor to create a new instance of our game.
/// </summary>
public GameEngine()
{
graphics = new GraphicsDeviceManager(this);
Components.Add(new GamerServicesComponent(this));
//Sets dimensions of the game window
graphics.PreferredBackBufferWidth = 800;
graphics.PreferredBackBufferHeight = 600;
graphics.ApplyChanges();
IsMouseVisible = true;
screenManager = new ScreenManager(this);
//Adds ScreenManager as a component, making all of its calls done automatically
Components.Add(screenManager);
// Tell the program to load all files relative to the "Content" directory.
Assets = new CachedContentLoader(this, "Content");
inputReader = new UserInputReader(Constants.DEFAULT_KEY_MAPPING);
collisionRecorder = new CollisionRecorder();
WorldState = new WorldState(new ReadWriteXML(), Constants.CONFIG_URI, this, contactReporter);
worldQueryUtils = new WorldQueryUtils(worldQuery, WorldState.PhysicsWorld);
ContactReporter contactReporter = new ContactReporter(collisionRecorder, worldQuery, worldQueryUtils);
gameObjectManager = new GameObjectManager(WorldState, assets, inputReader, pathPlanner);
worldQuery = new DefaultWorldQueryEngine(collisionRecorder, gameObjectManager.Controllers);
gameObjectManager.WorldQueryEngine = worldQuery;
pathPlanner = new PathPlanner(this, worldQueryUtils, WorldQuery);
gameObjectManager.PathPlanner = pathPlanner;
combatEngine = new CombatEngine(worldQuery, new Random());
}
Here is an excerpt of the above that's problematic:
gameObjectManager = new GameObjectManager(WorldState, assets, inputReader, pathPlanner);
worldQuery = new DefaultWorldQueryEngine(collisionRecorder, gameObjectManager.Controllers);
gameObjectManager.WorldQueryEngine = worldQuery;
I hope that no one ever forgets that setting of gameObjectManager.WorldQueryEngine, or else it will fail. Here is the problem: gameObjectManager needs a WorldQuery, and WorldQuery needs a property of gameObjectManager.
What can I do about this? Have I found an anti-pattern?