Code Better: Reference Containers for Change-Resistant Constructors

Proponents of dependency injection try to design classes so they can either work autonomously or get all services they rely on handed to them through their constructor. But even without dependency injection, the situation often arises where certain classes need to interact with a lot of other objects.

In these cases, you often end up with very complicated constructors and a lot of duplicate code:

public class RadarBuildingRenderer {
  public RadarBuildingRenderer(
    ISceneGraph sceneGraph,
    IContentManager contentManager,
    IAudioManager audioManager,
    RadarBuilding building
  ) {
    this.sceneGraph = sceneGraph;
    this.contentManager = contentManager;
    this.audioManager = audioManager;
    this.building = building;
  }
    
  private ISceneGraph sceneGraph;
  private IContentManager contentManager;
  private IAudioManager audioManager;
  private RadarBuilding building;
}

Above class takes care of rendering the visual and audible representations of a RadarBuilding in a computer game. As you can imagine, the same references will be required by other buildings, think TankFactoryBuilding, CommandCenterBuilding and so on – all duplicating the fields, their assignment and the complex constructor.

So what any sane programmer would do is consolidate this code into a common base class aptly named Renderer:

public class Renderer {
  public Renderer(
    ISceneGraph sceneGraph,
    IContentManager contentManager,
    IAudioManager audioManager
  ) {
    this.sceneGraph = sceneGraph;
    this.contentManager = contentManager;
    this.audioManager = audioManager;
  }
  
  protected ISceneGraph SceneGraph {
    get { return this.sceneGraph; }
  }
  
  protected IContentManager ContentManager {
    get { return this.contentManager; }
  }

  protected IAudioManager AudioManager {
    get { return this.audioManager; }
  }

  private ISceneGraph sceneGraph;
  private IContentManager contentManager;
  private IAudioManager audioManager;
}

public class RadarBuildingRenderer : Renderer {
  public RadarBuildingRenderer(
    ISceneGraph sceneGraph,
    IContentManager contentManager,
    IAudioManager audioManager,
    RadarBuilding building
  ) : base(sceneGraph, contentManager, audioManager) {
    this.building = building;
  }
    
  private RadarBuilding building;
}

Now the code duplication is mostly taken care of, but we added another drawback to the code instead: if at a later time you notice that some buildings, like your FlameThrowerBuilding and that the ParticleBeamBuildiner, need to access the IParticleManagerService, adding this service to the root Renderer class requires you to change the constructors for all of the buildings you implemented so far. Not good.

The solution to this new problem is to use a reference container which turns those references into a single object, making the building classes resistant to change as long as the services they access are provided for (of course, this is not an invitation to randomly add references into the container – the principles of object oriented design still apply!)

public class RendererReferences {
  public RendererReferences(
    ISceneGraph sceneGraph,
    IContentManager contentManager,
    IAudioManager audioManager
  ) {
    this.sceneGraph = sceneGraph;
    this.contentManager = contentManager;
    this.audioManager = audioManager;
  }
  
  public ISceneGraph SceneGraph;
  public IContentManager ContentManager;
  public IAudioManager AudioManager;
}

public class Renderer {
  public Renderer(RendererReferences references) {
    this.sceneGraph = references.SceneGraph;
    this.contentManager = references.ContentManager;
    this.audioManager = references.AudioManager;
  }
  
  protected ISceneGraph SceneGraph {
    get { return this.sceneGraph; }
  }
  
  protected IContentManager ContentManager {
    get { return this.contentManager; }
  }

  protected IAudioManager AudioManager {
    get { return this.audioManager; }
  }

  private ISceneGraph sceneGraph;
  private IContentManager contentManager;
  private IAudioManager audioManager;
}

public class RadarBuildingRenderer : Renderer {
  public RadarBuildingRenderer(
    RendererReferences references, RadarBuilding building
  ) : base(references) {
    this.building = building;
  }
    
  private RadarBuilding building;
}

For the price of a base class and a container class being added to the project, the constructors are simple once again and changing the references provided to a renderer can be done in a single place, without the need to edit all the renderer classes.

Needless to say that this pattern should only be applied if you have a lot of similar objects consuming the same services. In cases where there are only few variants required, a plain swithc statement or a set of methods might be a better option!

2 thoughts to “Code Better: Reference Containers for Change-Resistant Constructors”

  1. I’ve used this approach before but your example seems a little odd to me because I wouldn’t think a ‘Renderer’ would need to know about the audio manager?

    The approach I used is to have an IEngine interface that provides the services to the game objects. The IEngine would expose each of the subsystems like IGraphics, IPhysics, ISound, etc and then each game object just has a reference to the IEngine passed into the constructor.

    In my current implementatation though I’m using a component based approach. The idea is that each game object is a collection of components and each component has one simple task to do. Some of the components derive from the EngineComponent which gives them access to the subsystems that way. While others don’t need to know anything about the engine.

  2. I’ve seen the term “rendering” applied to audio, too. My Renderers are the glue between the logical representation of the game’s world and the audible scene (IAudioManager) as well as the visual scene (ISceneGraph). Maybe naming these things Renderers is altogether wrong – I just can’t think of a better alternative (eg. an IMaintainGameObjectRepresentationInSceneGraphAndAudioManager would be the most accurate, just not too friendly on the eyes) – so I picked the closest term and standardized its meaning to this throughout the project.

    Regarding the IEngine concept, I tried to keep the dependencies down as much as possible so I could easily mock them. I think IEngine is a bit too close to the service locator anti-pattern ([1], [2], [3]) in that it tends to hide the dependencies. For example, any object in the game world could theoretically ask the IEngine for its scene graph or audio manager, thus causing a dependency that wouldn’t be there if the world is being simulated on a server without audio and graphics (I had a little run in with a such a situation: MVC in Games).

    The component based approach (aka Composition over Inheritance, I believe) is something I really should move to. You’re way ahead of me there – I only learned the advantages of this design when I tried out Unity some time ago :)

    I wonder if it’s possible to keep the pure game logic separate from the graphics/audio stuff and use a composition-based architecture. Maybe components could be classified as affectors (= collision, physics, steering, scripts: game logic relevant) and observers (= no interaction with game object, only i.e. put a model in the scene graph for the game object). But then saving/loading the world becomes more difficult (for the game this example was based on, maps and save games are one and the same, I simply save state of the entire world). Ideas ideas… :D

Leave a Reply

Your email address will not be published. Required fields are marked *

Please copy the string Ia69Lm to the field below:

This site uses Akismet to reduce spam. Learn how your comment data is processed.