Search Unity

Dependency injection and abstractions

When we want to test something in isolation it means that we want to decouple it. Loose coupling is what we need. It is so easy to embed hidden dependencies into your game and it is so hard to break them. This article will help you understand loose coupling and dependency injection within a project in Unity, using the example project on github.

MoveHorizontally method uses static Unity API (Input class) without telling you. It considers this call to be his private business and you can’t control or influence the situation. It makes SpaceshipMotor class tightly coupled with Unity static API, and you can’t verify the behaviour of the SpaceshipMotor class unless you physically press the key on the keyboard. It’s annoying.

Now lets take this situation under control. You are in charge here.

The SpaceshipMotor class is using only horizontal axis, so we can define a short description what kind of functionality it expects from user input.

public interface IUserInputProxy
{
float GetAxis(string axisName);
}

Then you can substitute the call to real Input with the call to our abstraction.

Now you are in charge of the situation! The class can’t operate unless you provide it IUserInputProxy implementation.

This is called Dependency Injection (DI). When the dependency (Input in our case) is passed to the dependant object(SpaceshipMotor class) and it becomes part of it’s state (a field in our case).

There are several options of passing a dependency: constructor injection, property injection, method injection.

Constructor injection is considered to be the most popular and the most robust approach as when the dependency is passed in the construction phase our chances to have object in uninitialized state is minimal.
public class SpaceshipMotor : MonoBehaviour
{
private readonly IUserInputProxy userInputProxy;

29 评论

Hey I am so glad I found your web site, I really found you
by error, while I was searching on Aol for something else, Anyhow I am here now and would just like to say many
thanks for a tremendous post and a all round thrilling blog (I also love the theme/design), I don’t
have time to look over it all at the moment but I have bookmarked it and also included
your RSS feeds, so when I have time I will be back to read more,
Please do keep up the excellent b.

I’ve had an idea lately, thinking about DI and Unity. Do you think it would be possible to integrate a DI framework directly into the Unity Engine? The reason for that is simple: each and every MonoDevelop is instantiated by Unity. If the engine would have control over the application context (or DI Container, depending on your background), then the ENGINE itself could do the injecting of [Inject]-Attributed fields in MonoBehaviours!

Please, help me understand why this would be better, then just having a separate SpaceShipInput class with methods for making the ship say, move and a public master ProcessInput() method and a separate SpaceShipLogic class which would have a single reference to SpaceShipInput instance and call its ProcessInput() method every frame?

In this case within SpaceShipInput you have a bool “isFakeInput” attribute which determines which input each method processes – real GetAxis or a value from some attribute in the SpaceShipInput class itself, say, public int horisFakeInput which is exposed in the inspector.

Granted, you will have to basically have a condition in each internal method to switch between real or fake input base on the “isFakeInput”, but you won’t depend on external interfaces and can go live by just setting isFakeInput = false.

But surely if Unity API supported DI you wouldn’t need all this bull**t.

@ALAN, “universal cure” doesn’t exist. The purpose of the article was to give an introduction to DI. I’ve already promised to write another one about DI frameworks. DI is good for building both simple and complex systems. When you are using a framework, wiring complex hierarchies is not a problem. While loose coupling makes testing and reuse of your classes easier. Circular references in constructors do make problems which are easily solved in Java but might be an issue in C#. If you depend on interfaces they are quite resolvable, though.

Also, what you didn’t mention are the shortcomings of DI. DI works perfectly when wiring up singleton classes (e.g. UnityEngine.Input would be very nice, but since it only offers static methods, you need to do this artificial proxy thing as shown above), however DI really runs into problems when wiring non-static objects, ESPECIALLY if they have constructor arguments and cyclic dependencies.

DI is great. But it’s not the “universal cure” for all architecture problems, as it appears to be from reading the article.

@DMITRIY MINDRA
I find that this way of thinking scales for my uses. The move script is a component that can be reused by anything that needs to move that particular way. The actual call to move may come from a Unity Input component script, from some AI component script or via a test class. In all cases move works the same without dependancy, although in this example move would depend on a Transform or Rigidbody to actually move. I guess I just don’t see where an interface fits into the Unity component way of doing things.

@RICHARD, you are absolutely right. The component is not usable without wiring code. I like the mechanism that is implemented in ASP.NET MVC, where you can provide your implementation of IDependencyResolver that would resolve dependencies for your application. Maybe we need something similar for Unity?

This is great right up until the point that SpaceshipMotor is now unusable without some other code to wire it up. It’s not even the case that we can just wire it up to a RealUserInput component in the Inspector and forget about it, because interface references aren’t serialised…

I have been wrapping all of Unity’s static classes so they can be testable, although – I follow one rule because about 2 years ago when I attempted DI for my first time in Dueling Blades, it is waaaayyy too inefficient to inject dependencies into a scene with tons of MonoBehaviours.

So my rule is to Have MonoBehaviours be responsible for one thing: Managing Game Objects. There is 0 logic to my mono behaviours other than creating an API to doing specific tasks in regards to manipulating game objects/monobehaviours.

I use Controllers and Services (Usually singletons) to instantiate, destroy and manage Mono Behaviours/Unity3d Objects.

Following this rule and wrapping some of Unity’s APIs for unit testing has made life amazing with Unity.

Feel free to check out the dependency injection framework I use (works on Unity 4.2+ for ios/android/pc) without any issues:

@MAT, thanks for the reference to StrangeIoC. Next blogpost would be about DI frameworks.
@MARTIJN, I would be glad to see your refactoring and discuss it.
@LIOR, I definitely agree with you that making Engine API mockable would make life easier.

Agree with Dmitriy here. Mocking is simply a way to mask the issues that actually exist in the code. While this allows you to test the code. If someone else comes along and doesn’t have your mocking suite they’re stuffed.

@Dmitriy – I Agree, that’s exactly what i said. I only added that the API Unity provides for Input could’ve been one that allows easier injection by not being a static class in the first place. In this article you “simulate” an input API that is interface based that hides Unity’s static class to allow replacing those calls with fake calls. That could’ve been easier to do if (like i said above) the API was provided as some singleton or something like that. Then, You could’be substituted the returned instance with a fake one. If there are .NET tools that allow mocking calls on static classes. Then you could just fake Input.GetAxis(…) or any other static call. I am not sure NSubstitute could do that (although i think other .NET tools can, but are probably not compatible with Unity)

@Lior, @Victor – there are many situations where you depend on something you can’t control. Input is just one example of static class that can’t be inherited from and you can’t test the behavior of classes that depend on it. But when you create an abstraction for Input, the situation changes and you can pass any class that implement interface including wrapper that calls “real” system API. Depending on abstractions means that object knows nothing about concrete implementation of it’s dependency and should be able to work with any object that implements the interface (Barbara Liskov substitution principle).