Firstly, I want to restrict this question to web development only. So this is language agnostic as long as the language is being used for web development. Personally, I am coming at this from a background in PHP.
Often we need to use an object from multiple scopes. For example, we might need to use a database class in the normal scope but then also from a controller class. If we create the database object in normal scope then we cannot access it from inside the controller class. We wish to avoid creating two database objects in different scopes and so need a way of reusing the database class regardless of scope. In order to do so, we have two options:
- Make the database object global so that it can be accessed from anywhere.
- Pass the database class to the controller class in the form of, for example, a parameter to the controller's constructor. This is known as dependency injection (DI).
The problem becomes more complex when there are many classes involved all demanding objects in many different scopes. In both solutions, this becomes problematic because if we make each one of our objects global, we are putting too much noise into the global scope and if we pass too many parameters into a class, the class becomes much more difficult to manage.
Therefore, in both cases, you often see the use of a registry. In the global case, we have a registry object which is made global and then add all of our objects and variables to that making them available in any object but only putting a single variable, the registry, into the global scope. In the DI case, we pass the registry object into each class reducing the number of parameters to 1.
Personally, I use the latter approach because of the many articles that advocate it over using globals but I have encountered two problems. Firstly, the registry class will contain huge amounts of recursion. For example, the registry class will contain database login variables needed by the database class. Therefore, we need to inject the registry class into the database. However, the database will be needed by many other classes and so the database will need to be added to the registry, created a loop. Can modern languages handle this okay or is this causing huge performance issues? Notice that the global registry does not suffer from this as it is not passed into anything.
Secondly, I will start passing large amounts of data to objects that don't need it. My database doesn't care about my router but the router will get passed to the database along with the database connection details. This is made worse through the recursion problem because if the router has the registry, the registry has the database and the registry and the registry is passed to the database, then the database is getting passed to itself via the router (i.e. I could do $this->registry->router->registry->database
from inside the database class`).
Furthermore, I don't see what the DI is giving me other than more complexity. I have to pass an extra variable into each object and I have to use registry objects with $this->registry->object->method()
instead of $registry->object->method()
. Now this obviously isn't a massive problem but it does seem needless if it is not giving me anything over the global approach.
Obviously, these problems don't exist when I use DI without a registry but then I have to pass every object 'manually', resulting in class constructors with a ridiculous number of parameters.
Given these issues with both versions of DI, isn't a global registry superior? What am I losing by using a global registry over DI?
One thing that is often mentioned when discussing DI vs Globals is that globals inhibit your ability to test your program properly. How exactly do globals prevent me from testing a program where DI would not? I have read in many places that this is due to the fact that a global can be altered from anywhere and thus is difficult to mock. However, it seems to me that since, at least in PHP, objects are passed by reference, changing an injected object in some class will also change it in any other class into which it has been injected.