34

Many of the more diligent software developers I know are moving to inversion of control and dependency injection to handle references to objects. Coming from a Flash games perspective I don't know all the ins and outs of AAA studios, so: are these used in the retail game world?

DMGregory
  • 134,153
  • 22
  • 242
  • 357
Iain
  • 6,518
  • 3
  • 33
  • 46
  • 1
    I think that in that world performance matters above all, since more users will be hit by low performance than programmers by bad code. – Bart van Heukelom Aug 25 '10 at 09:16
  • Dependency injection sounds like a component-based system, but would you have an example of how one would une inversion of control? – ADB Dec 27 '11 at 02:39
  • As most of this thread seems to be misinformed about what IoC is and what it solves and the OP is not really asking that in the first place I will point here for future visitors: http://www.sebaslab.com/ioc-container-unity-part-1/ – Boris Callens Mar 24 '18 at 19:37
  • 1
    Yes, they do. In fact, Rare had a talk about how they are implementing their testing in Sea of Thieves - https://www.youtube.com/watch?v=KmaGxprTUfI&t=1s. Unfortunately they didn't have too much to say about Inversion of Control in Unreal Engine, however, I've written a project that demonstrates how it works: https://github.com/jimmyt1988/UE-Testing – Jimmyt1988 May 23 '19 at 21:41
  • One of the LARGEST benefits of Dependency Injection (Rename of Inversion of control - same thing) was with a framework and a huge system, I can easily swap 'my remote API that talks to server' with a mock for testing. Without DI, you have to wire this through tons of layers. Also, when done right if I need instance 1 of class Y way over somewhere else, I no longer need to rework multiple layers of code. <- This probably only makes since to those who have used good DI frameworks though as it is hard to explain and only learned through experience. – Dean Hiller Jun 18 '23 at 06:30
  • Oh, we took this one level more. Since we use DI, we just simply bind our Executor(thread pool) into a single thread or a mock so that ALL OUR tests are single threaded. I have had 100's of people the last 10 years ask me how I made every system(even the most complex like gaming) single threaded for testing - well, thanks to DI, it made reaching in to the huge map of objects to swap anything out possible. – Dean Hiller Jun 18 '23 at 06:38

8 Answers8

41

You call it 'diligent software development', I call it 'painful overengineering'. That's not to say that inversion of control is bad - in fact, the basic definition of it is good - but the proliferation of entire frameworks and methods of working to achieve all this is little short of insane, especially combined with the way people are trashing perfectly good and clean interfaces in order to be able to inject interchangeable components that 99% of the time you'll never interchange. It's the sort of thing that could only originate from the Java enterprise environment and I'm glad it doesn't have as much of a foothold elsewhere.

Often the argument is that even if you don't interchange components, you want to be able to test them in isolation with mock objects and the like. However, I'm afraid I will never buy the argument that it's worth bloating and complicating an interface in order to be better able to test it. Testing proves one thing only - that your tests work. Clean and minimal interfaces on the other hand go a long way towards proving that your code works.

So, the short answer: yes, but not in the way you're thinking. Sometimes, when you need interchangeable behaviour, you'll pass in an object to a constructor that dictates part of the new object's behaviour. That's about it.

Kylotan
  • 24,329
  • 3
  • 51
  • 94
  • 5
    +1 - I agree that the Java community seems to have vastly over-complicated what seems to be a very simple idea. – Chris Howe Aug 25 '10 at 14:25
  • 7
    The idea isn't necessarily that you'll need to swap out different implementations in production, but that you might want to inject special implementations to facilitate automated testing. In that regard, DI/IoC can certainly be useful in games, but you want to keep it reasonably scoped. You shouldn't need more than a few one-time service resolutions at a high level--you don't want to be resolving services for every little object in the game, and certainly not every frame or anything like that. – Mike Strobel Aug 25 '10 at 14:47
  • I never called it "diligent software development" - I said the developers were diligent, i.e. the kind of people who care about the quality of their code. Also, I did specifically say software developers not game developers. Personally, I don't see where it would fit into gamedev, but I wanted to know what AAA studios were doing. – Iain Aug 25 '10 at 15:25
  • 2
    Indeed, YAGNI comes to mind. – drxzcl Aug 25 '10 at 15:33
  • 1
    In my experience, if unit tests only prove themselves correct, they are not good unit tests and you should rewrite them. And DI at least (the principle of decoupling "new" calls from client code, not necessarily any framework) goes a long way to making code simpler and good unit ests easier to write. – Thomas Dufour Aug 26 '10 at 07:41
  • 2
    @Thomas Dufour: the concept of a test can never prove anything. It's just a data point. You can have a test pass 100000 times without proving that it will pass on run 100001. Proof comes from logical analysis of the subject matter. I would argue that these IoC containers and the like make testing easier at the expense of making proving correctness harder, and I believe that is a bad thing. – Kylotan Aug 26 '10 at 10:16
  • @Mike Stroble: I had hoped I'd covered that with my 2nd paragraph. – Kylotan Aug 26 '10 at 10:17
  • 18
    @Kylotan the concept of a test never tries to prove anything. It tries to demonstrate that the behavior with given inputs is as expected. The more behavior is shown to be correct, the greater your belief that overall function is correct but it's never 100%. The statement that a minimal interface proves anything is ludicrous. – dash-tom-bang Aug 26 '10 at 18:32
  • 1
    I know it doesn't try to prove anything. But software can be proven correct, and the simpler it is, the more practical that is. Testing, although valuable in itself, should not require the code to be made more complicated to facilitate it. – Kylotan Aug 27 '10 at 10:31
  • 3
    A component's code needn't be made more complicated to facilitate testing. And the point isn't necessarily to prove correctness, but to help identify regressions or subtle, unintended changes in behavior. – Mike Strobel Aug 27 '10 at 15:34
  • 2
    It sounds like people don't understand the goal of unit testing. Testing a unit of behavior will never prove the entire system correct, it says nothing about how different pieces of the system will interact, and it's too low level for scenario testing. If you want QA write scenario or integration tests.

    The reason why people advocate TDD and unit testing is because by following the process you end up with code which is better factored, more loosely coupled, and closer to single responsibility. The act of placing a piece of code under test forces the author to make better design decisions.

    – Alex Schearer Aug 27 '10 at 16:08
  • 1
    @Mike: we're going round in circles here. If you can meaningfully test an object without changing the interface, great. That's a case where the testing costs you little and buys you a lot. But we were talking specifically about changing interfaces to permit dependency injection purely for testing purposes. Obviously if your language supports swapping components out without imposing any restriction on how you make the aggregate object (eg. Python does), that's a different matter, and doesn't require explicit IoC containers or any of that fluff. – Kylotan Aug 27 '10 at 16:11
  • 5
    @Alex Schearer: I'm afraid I can't ever accept that needing to use a formalised IoC container system and typically creating additional constructor arguments or factory classes to facilitate testing is, in any way, making the code better factored, and certainly not making it looser coupled. In fact it pretty much tramples on encapsulation, a key aspect of OOP. Instead of relying on TDD to drive their design and hope that a good result emerges, people could just follow SOLID principles in the first place. – Kylotan Aug 27 '10 at 16:17
  • 2
    I personally enjoyed this thorough answer. Just wanted to say that. I think that Kylotan makes an excellent point, and often IoC does overly complicate things. There are cases where it makes a lot of sense (when a lot of DI makes sense), but wrapping everything in a proxy just for the sake of doing so should be considered bad practice. – Andy Sep 01 '11 at 13:46
  • +3 for very clear-headed answer... if only. – Engineer Sep 02 '11 at 09:17
  • 2
    I'm surprised to see this answer rated so highly. I've used DI in web-based business applications and the is certainly NOT just frivolous over-engineering in that context. I was hoping to read about pros and cons from a game engine perspective (where the architecture is based upon a loop updating state and drawing a screen), as opposed to a blanket criticism if the pattern. – Sprague Apr 24 '16 at 11:27
  • @Sprague: to be fair, the original question was 'do retail games use this' and the answer is 'no, barely at all'. We're going to have to agree to disagree on whether explicit IoC/DI is frivolous over-engineering (I too work on web apps, and would never use an IoC container) while at the other end of the spectrum there's no debate that some degree of modularity is good. Regarding your specific question, the fact there's a game loop and periodic updates has no relation to how objects get constructed or services get located so there isn't much game-specific to talk about here. – Kylotan Apr 25 '16 at 14:16
  • Regardless of testing and interchangeability, it makes code much more simple because instantiation of dependencies is moved to a single location elsewhere. – Ian Warburton Feb 15 '18 at 18:46
  • Or it makes code much harder to understand because you've added an extra layer of indirection to everything whether you needed it or not. – Kylotan Feb 21 '18 at 15:08
  • Hm. Well, tell that to Rare, who have implemented extensive use of patterns for testing their game as a service - https://www.youtube.com/watch?v=KmaGxprTUfI&t=1s – Jimmyt1988 May 23 '19 at 21:42
  • @Jimmyt1988: tell what to Rare? They are known as being a bit of an outlier in the gamedev world for being extremely process-heavy, and the result is quite slow development times. It's 9 years on from when I wrote this original answer and there is still no significant movement towards IoC/DI in game development. – Kylotan May 29 '19 at 22:26
  • Testing is so important. Saves money, prevents bugs, helps developers test pre QA so doesn't waste time and money. It's an important tool in the developer toolbox. TDD is a great way of coding! Be pragmatic, ofcourse... But be wise of the benefits. Once you start, it's hard to stop. – Jimmyt1988 May 30 '19 at 00:41
  • A game can't be "correct", it's simply either fun and stable or it isn't. People are confusing QA/play testing with unit testing, using IOC only helps unit testing (and some integration testing) , it has no impact on the need for other testing. – Ash Jan 08 '21 at 03:45
  • The huge benefit of IOC for larger games is being able to run specific parts of your game quickly and easily by mocking out the rest of the game. When you run these "tests" you're almost never looking for "correctness" (you rarely even need Asserts), you most often checking if a some result of this code is "good", (if not, tweak code, run again). Another benefit is performance testing of just that part of the code. – Ash Jan 08 '21 at 03:45
18

Strategy Pattern, Composition, Dependency Injection, are all very closely related.

Since the Strategy Pattern is a form of Dependency Injection, if you take a look at engines like Unity for example they are completely based off this principle. Their use of Components(Strategy Pattern) is deeply embedded into their whole engine.

One of the main benefits aside from reuse of components is to avoid the dreaded deep class hierarchies.

Here is an article by Mick West who talks about how he introduced this type of system into the Tony Hawk series of games by Neversoft.

Evolve Your Hierarchy

Up until fairly recent years, game programmers have consistently used a deep class hierarchy to represent game entities. The tide is beginning to shift from this use of deep hierarchies to a variety of methods that compose a game entity object as an aggregation of components...

David Young
  • 3,310
  • 1
  • 22
  • 41
  • 2
    +1, Evolve Your Hierarchy is a great link which I'd completely forgotten about. The Scott Bilas slides are good too -- correct link for these is ( http://scottbilas.com/files/2002/gdc_san_jose/game_objects_slides.pdf ). There's one more set of related slides, but I've forgotten where... – leander Aug 25 '10 at 18:51
  • Also the article in Games Gems 5 (I think) by Bjarne Rene. – Chris Howe Aug 25 '10 at 20:07
15

There seems to be a lot of confusion about the Inversion of Control (IoC) pattern. A number of people have equated it with the Strategy Pattern or a Component Model, but these comparison don't really capture what IoC is about. IoC is really about how a dependency is obtained. Let me give you an example:

class Game {
    void Load() {
        this.Sprite.Load(); // loads resource for drawing later
    }
}

class Sprite {
    void Load() {
        FileReader reader = new FileReader("path/to/resource.gif");
        // load image from file
    }
}

In the above it's clear that Sprite.Load has a dependency on a FileReader. When you want to test the method you need the following:

  1. A file system in place
  2. A test file to load from the file system
  3. Ability to trigger common file system errors

The first two are obvious, but if you want to ensure that your error handling works as expected you really need #3, too. In both cases you've potentially slowed your tests down quite a bit as they now need to go to disk and you've likely made your testing environment more complicated.

The goal of IoC is to decouple the use of behavior from its construction. Note how this differs from the Strategy pattern. With the Strategy pattern the goal is to encapsulate a re-usuable chunk of behavior so that you can easily extend it in the future; it has nothing to say about how strategies are constructed.

If we were to rewrite the Sprite.Load method above we would likely end up with:

class Sprite {
    void Load(IReader reader) {
        // load image through reader
    }
}

Now, we have decoupled the construction of the reader from its use. Therefore, it's possible to swap in a test reader during testing. This means that your testing environment no longer needs a file system, test files, and can easily simulate error events.

Note that I did two things in my rewrite. I created an interface IReader which encapsulated some behavior -- i.e. implemented the Strategy pattern. In addition, I moved the responsibility for creating the right reader to another class.

Maybe we don't need a new pattern name to describe the above. It strikes me as a mix of the Strategy and Factory patterns (for IoC containers). That being said, I'm not sure on what grounds people are objecting to this pattern as it's clear that it solves a real problem, and, certainly, it's not obvious to me what this has to do with Java.

Alex Schearer
  • 1,609
  • 11
  • 17
  • 2
    Alex: nobody is objecting to passing in already-created objects to drive custom functionality where needed. Everybody does it, just like in your example. The problem is where people are using entire frameworks that exist purely to handle this stuff, and religiously code every aspect of functionality to depend on this functionality. They first add IReader as a parameter to Sprite construction, and then end up needing special SpriteWithFileReaderFactory objects to facilitate this, etc. This attitude does originate from Java and things like the Spring IoC containers, etc. – Kylotan Aug 27 '10 at 16:24
  • 4
    But we aren't talking about IoC containers -- at least I don't see it in the original post. We're just talking about the pattern itself. You're argument, as best I can tell, is "Because some tools in the wild are bad we should not use the underlying concepts." That strikes me as throwing the baby out with the bath water. Hitting the right balance between simplicity, getting things done, and maintainability/testability is a hard problem likely best treated on a project by project basis. – Alex Schearer Aug 27 '10 at 17:54
  • I suspect many people are against IoC because it means : – phtrivier May 04 '11 at 11:33
9

I'm writing this at a time when the accepted answer is by a contributor who strongly opposes the concept and I wish to provide a different view:

Dependency injection is indeed not widely used in game development.

In my humble opinion, the reason for this is simply that most game developers are self-taught and know very little about software architecture. Most just wing it or even grow attached to hacks such as the singleton anti-pattern.

Dependency injection is, in games as well, a useful technique that will vastly improve a project's structure:

  • Reduce inter-system dependencies because these are accessed through interfaces

  • Result in easier-to-understand code because you'll design interfaces to your systems rather than have them happen by accident

  • Reduce complexity because each system can be understood on its own and accesses other systems only by intentional interface

  • Improve testability by allowing modules to be isolated via mocks (stand-in dummies for the actual dependencies)

  • Better testabilityu allows more tests, thus making it easier to refactor and reuse

  • The resulting smaller code units are easier to maintain and help uphold the single responsibility principle

You can find IoC add-ons for Unity (i.e. Zenject), Godot and Xenko game engine.


Also relevant is that most games eventually find the need to have some kind of services available throughout the game, whether it's for querying time-of-day or weather in the game world, enumerating save slots or reading the difficulty settings. And most game developers then reinvent the wheel:

Unity developers rely on static variables in DontDestroyOnLoad classes or a master scene that's always remains loaded (or Zenject).

Godot developers have "AutoLoad" scenes that are always loaded before any other scene. Services that should be available throughout the game can be placed in that scene.

Unreal developers extend the GameInstance class to add game-wide services (and the engine offers some services of its own).


Source: being a full-time software developer (visualuation / real time) for 25 years & moonlighting game developer for 30 years.

Cygon
  • 222
  • 2
  • 7
  • 2
    This is a great answer and I wish it was ranked higher. The accepted answer doesn't actually seem to understand what Inversion of Control and Dependency Injection are but spends a large amount of effort trashing something unrelated. – Zeelot Apr 17 '21 at 23:36
4

I would say that it is one tool among many, and it is used on occasion. As tenpn said any method that introduces vtables (and generally any extra indirection) can have a performance penalty, but this is something that we should only worry about for low-level code. For high-level structural code that isn't really an issue, and IoC can have positive benefits. Anything to reduce dependencies between classes and make your code more flexible.

Chris Howe
  • 516
  • 4
  • 7
  • 2
    To be precise I'd worry about vtable penalties for any code that's called many many times a frame. This may or may not be low-level. – tenpn Aug 25 '10 at 11:52
2

Not in my experience. Which is a shame, as games code needs to be very adaptable, and these approaches would definitely help.

However it must be said that both methods could, in C++, introduce virtual tables where there were none before, bringing with them an appropriate performance hit.

tenpn
  • 5,524
  • 3
  • 32
  • 45
2

Gotta agree with Kylotan on this one. "Dependency injection" is an ugly Java solution to an ugly Java conceptual flaw, and the only reason anyone's looking at it in other languages at all is because Java's becoming a first language for a lot of people, when it really shouldn't.

Inversion of control, on the other hand, is a useful idea that's been around for a long time, and is very helpful when done right. (Not the Java/Dependency Injection way.) In fact, you probably do it all the time if you're working in a sane programming language. When I first read up on this whole "IOC" thing everyone was buzzing about, I was completely underwhelmed. Inversion of Control is nothing more than passing a function pointer (or method pointer) to a routine or object in order to help customize its behavior.

This is an idea that's been around since the 1950s. It's in the C standard library (qsort comes to mind) and it's all over the place in Delphi and .NET (event handlers/delegates). It enables old code to call new code without needing to recompile the old code, and it gets used all the time in game engines.

Mason Wheeler
  • 1,168
  • 10
  • 25
  • 3
    I was also of the "so this is what people are excited about?" when I read about DI, but the fact is that most game programmers could stand to learn about it and about how it would apply to the work that we do. – dash-tom-bang Aug 26 '10 at 18:39
1

I'm not a professionnal game dev, and only tried implementing IoC in C++ once, so this is just speculating.

However, I suspect game developers's would be suspicious about IoC because it means :

1/ designing lots of interfaces and lots of small classes

2/ having pretty much every function call being lately bound

Now, it might be a coincidence, but both tend to be a little bit more complicated and/or problematic for performances in C++ (which is widely used in game, isn't it ?) than in Java , where IoC became widespread (Probably because it was relatively easy to do, help people design saner object hierarchies, and because some believed it could turn writing an a application in Java into writing an application in XML, but that's another debate :P)

I'm interested in the comments if that does not make any sense at all ;)

phtrivier
  • 326
  • 1
  • 8