Dependency Injection for Games — Appendix: Injection Container
Tags: gamedev, architecture, cpp, tutorial
Published: 2023-09-11 11:50:22


In the main [Dependency Injection for Games](https://dev.to/filippoceffa/dependency-injection-for-games-4dhb) article, we explained how **Dependency Injection** helps you organize your **game** or **game engine** architecture, but we did not address one inconvenient issue that affects it.

In large applications, Dependency Injection requires the user to write and maintain a lot of tedious and time-consuming **boilerplate code**.

The goal of this appendix is to demonstrate how this burden can be relieved by using a **Dependency Injection Container** library.

We will start by defining the problem, and providing a theoretical solution. Finally, we will show how a Container solves the problem in practice.

## Practical problem

When examining the `main()` function in our dummy game application, we see that Dependency Injection burdens the user with two tedious tasks:

 1. Construct systems in an appropriate order;

 2. Manually pass dependencies to their dependent systems.
```c++
int main()
{
    State state{};
    // 1: User must construct Physics before Simulation
    Physics physics{}; 
    Simulation simulation {state, physics};
    OpenGL openGL{};
    Rendering rendering{state, openGL};
    // 2: User must manually pass simulation and rendering to Loop
    Loop loop{simulation, rendering}; 
}
```
This is a negligible problem in our simple example, but the cost to write and modify this boilerplate code grows significantly in an application with hundreds of systems. Is there a better way than to do this work by hand?

## Theoretical solution

From a theoretical point of view, our problem is very clear — we are working with a **directed acyclic graph**, where nodes represent systems, and edges represent dependencies:


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mwxfhckqd6n5ey4k2d69.jpg)

This graph contains all the information to automate the two manual tasks:

 1. Systems construction order is determined by a depth-first traversal.

 2. Passing dependencies only requires knowing the edges of the graph.

How can we take advantage of this knowledge in our implementation?

## Dependency Injection Container

The **Container** is the software that implements our theoretical solution. Let’s take an overview at how it works:

* The user provides the list of services (the nodes) to the Container.

* The Container infers the dependencies (the edges) by looking at the parameters of the system constructors.

* The Container constructs and destructs systems in the correct order.

* The Container passes dependencies to the dependent systems.

What follows is a possible API for a **Container library**, written in **C++**. Please, be aware that the API refers to systems as “**components**”, a more common term in the context of Dependency Injection:
```c++
int main()
{
    // We register components with the ComponentSet helper class.
    // No construction happens here, and systems can be added in any order.
    ComponentSet components; 
    components.add<Loop>();
    components.add<Physics>();       
    components.add<Rendering>();
    components.add<Simulation>();
    components.add<OpenGL>();
    components.add<State>();

    // Systems are constructed in correct order in Container constructor
    Container container{components}; 

} // Systems are destructed in reverse order in Container destructor
```
The Container can also support [**Dependency Inversion**](https://dev.to/filippoceffa/dependency-injection-for-games-appendix-dependency-inversion-3coc), we only need to specify which system implements an interface:
```c++
// Rendering depends on IGraphics
components.add<Rendering>(); 

// OpenGL implements IGraphics
components.add<OpenGL>().implements<IGraphics>(); 
```
Integrating a Container in your application is **non-invasive**— the only file that depends on the Container is `main.cpp`.

## Conclusions

A Container is a great tool to reduce Dependency Injection boilerplate code. However, there are some drawbacks:

* It is an additional dependency to maintain in your project.

* An invalid graph is detected at runtime instead of compile time.

In the end, whether to use a Container or not is up to you — it is perfectly possible to adopt Dependency Injection without using one.

You will need to evaluate how much time you waste writing and modifying boilerplate code, and decide if using a Container is worth the tradeoff.

## Next steps

* If you wish to check out the **implementation** of the dummy game application using a Container, or are curious about the theory and implementation of a **C++ Container library**, please refer to the accompanying [**GitHub repository**](https://github.com/Ceffa93/dependency_injection_for_games).

* If you haven’t yet, read the appendix on [**Dependency Inversion**](https://dev.to/filippoceffa/dependency-injection-for-games-appendix-dependency-inversion-3coc).