Design Patterns – Benefits of Dependency Injection for Common Data Structures

dependency-injectiondesign-patternsobject-oriented

There are plenty of reasons why globals are evil in OOP.

If the number or size of the objects needing sharing is too large to be efficiently passed around in function parameters, usually everyone recommends Dependency Injection instead of a global object.

However, in the case where almost everyone needs to know about a certain data structure, why is Dependency Injection any better than a global object?

Example (a simplified one, to show the point generally, without delving too deep in a specific application)

There is a number of virtual vehicles which have a huge number of properties and states, from type, name, color, to speed, position, etc. A number of users can remote control them, and a huge number of events (both user-initiated and automatic) can change a lot of their states or properties.

The naive solution would be to just make a global container of them, like

vector<Vehicle> vehicles;

which can be accessed from anywhere.

The more OOP-friendly solution would be to have the container be member of the class which handles the main event loop, and be instantiated in its constructor. Every class which needs it, and is member of the main thread, will be given access to the container via a pointer in their constructor.
For example, if an external message comes in via a network connection, a class (one for each connection) handling the parsing will take over, and the parser will have access to the container via a pointer or reference. Now if the parsed message results in either a change in an element of the container, or requires some data out of it to perform an action, it can be handled without the need of tossing around thousands of variables through signals and slots (or worse, storing them in the parser to be later retrieved by the one who called the parser). Of course, all classes which receive access to the container via dependency injection, are part of the same thread. Different threads will not directly access it, but do their job and then send signals to the main thread, and the slots in the main thread will update the container.

However, if the majority of classes will get access to the container, what makes it really different from a global? If so many classes need the data in the container, isn't the "dependency injection way" just a disguised global?

One answer would be thread safety: even though I take care not to abuse the global container, maybe another developer in the future, under the pressure of a close deadline, will nevertheless use the global container in a different thread, without taking care of all the collision cases.
However, even in the case of dependency injection, one could give a pointer to someone running in another thread, leading to the same problems.

Best Answer

in the case where almost everyone needs to know about a certain data structure, why is Dependency Injection any better than a global object?

Dependency injection is the best thing since sliced bread, while global objects have been known for decades to be the source of all evil, so this is a rather interesting question.

The point of dependency injection is not simply to ensure that every actor who needs some resource can have it, because obviously, if you make all resources global, then every actor will have access to every resource, problem solved, right?

The point of dependency injection is:

  1. To allow actors to access resources on a need basis, and
  2. To have control over which instance of a resource is accessed by any given actor.

The fact that in your particular configuration all actors happen to need access to the same resource instance is irrelevant. Trust me, you will one day have the need to reconfigure things so that actors will have access to different instances of the resource, and then you will realize that you have painted yourself in a corner. Some answers have already pointed such a configuration: testing.

Another example: suppose you split your application into client-server. All actors on the client use the same set of central resources on the client, and all actors on the server use the same set of central resources on the server. Now suppose, one day, that you decide to create a "standalone" version of your client-server application, where both the client and the server are packaged in a single executable and running in the same virtual machine. (Or runtime environment, depending on your language of choice.)

If you use dependency injection, you can easily make sure that all the client actors are given the client resource instances to work with, while all the server actors receive the server resource instances.

If you do not use dependency injection, you are completely out of luck, as only one global instance of each resource can exist in one virtual machine.

Then, you have to consider: do all actors really need access to that resource? really?

It is possible that you have made the mistake of turning that resource into a god object, (so, of course everyone needs access to it,) or perhaps you are grossly overestimating the number of actors in your project that actually need access to that resource.

With globals, every single line of source code in your entire application has access to every single global resource. With dependency injection, each resource instance is only visible to those actors that actually need it. If the two are the same, (the actors that need a particular resource comprise 100% of the lines of source code in your project,) then you must have made a mistake in your design. So, either

  • Refactor that great big huge god resource into smaller sub-resources, so different actors need access to different pieces of it, but rarely an actor needs all of its pieces, or

  • Refactor your actors to in turn accept as parameters only the subsets of the problem that they need to work on, so they do not have to be consulting some great big huge central resource all the time.