Proof of concept GameServiceContainer usage

So I’ve just tried out using the GameServiceContainer to store several of my generally used manager/system classes. While I was at it, i figured I throw the ContentManager in there as well.

When reading about the ServiceContainer, the recommended patterns always seem to add your services as interfaces, something like:

Services.AddService( typeof( IMyService ), myobject );

However, I just added my classes directly as Objects:

        Services.AddService(_tmxManager);
        Services.AddService(_inputManager);
        Services.AddService(Content);

Then I pass the serviceContainer as a parameter and just fetch them wherever I need them:

        _inputManager = services.GetService<InputManager>();
        _tmxManager = services.GetService<TMXManager>();
        var contentManager = services.GetService<ContentManager>();

To me this seems like the most straight forwad usage of the ServiceContainer - yet I have not found any examples doing it this way. Are there any drawbacks, performance or otherwise, to consider - doing it like above?

Added:
Also, if I then need the GraphicsDevice I fetch it via the ContentManager (using an extension) like this:

    public static GraphicsDevice GetGraphicsDevice(this ContentManager content)
    {
        return ((IGraphicsDeviceService)content.ServiceProvider.GetService(typeof(IGraphicsDeviceService))).GraphicsDevice;
    }

_graphicsDevice = contentManager.GetGraphicsDevice();

Same here, if theres any obvious drawbacks doing like this please let me know.

You should google the phrase “programming towards a contract instead of implementation”, it’ll give you tons of answers to this specific question. Here’s the first result I got which explains a lot Programming to an Interface: A Simple Explanation - NDepend

Short answer is that the code that looks the most most straightforward today can give you trouble down the end of development because when you make crucial changes to a type due to performance limits or anything else it suddenly means you have to make change everywhere it’s used as well, but you avoid that when you’re programming towards an interface.

Testing is a common scenario where programming towards an interface helps you out. Let’s say you want to write an automatic test on a class the relies on InputManager via Services , the input manager probably relies on user keyboard input or something, so making it “automatic” is quite difficult. However if you had programmed it using an interface you could have written a mock implementation that simulates keyboard input.

You can add it like this

var mockInputManager = new MockInputManager();
Services.AddService(typeof(IInputManager), mockInputManager);

Your class that uses it will then get the mock instead of the usual input manager

You can then simulate keypresses by having simulation methods on MockInputManager that other implementations of IInputManager doesn’t need to know about

_inputManager = services.GetService<IInputManager>();
Assert.That(_game.MenuGUI.Visible, Is.False);
_inputManager.PressEsc(); // Should open Menu GUI
Assert.That(_game.MenuGUI.Visible, Is.True);
2 Likes

In addition to replacing functionality with other implementations, you can replace it with a pure mock object (generated by a mocking framework such as NSubstitute) allowing you to unit test your code. It’s a great way to build confidence in your implementations and to ensure that any refactor work you perform actually keeps functionality intact.

Your usage of ServiceContainer is very similar to a dependency container, which has a lot of resources (and implementations) online.