We all know the why Dependency Injection is awesome because it makes code less coupled, easier to test, and much nicer to read! And then some decide to use a
Let's say that all of your classes are going to be called via the DI container.
First a little comparative case: If you go to a store to buy a pair of trousers, then you are using DI at several moments. For instance you do not have to tell what size you need, the people that help you have the expertise to find out. You will have to say what kind of type of trousers you want to have, the people of the store will present you some trousers according to your wishes (or let you know that that is impossible). An example which is not DI is the answer to the question where all those trousers come from. For a customer entering the store is the answer to that question totally irrelevant. It will never be the reason to enter the shop, where the need for a pair of trousers of a certain type is. You can ask unspecified questions of a certain type and you will get exact answers. That is the core of DI. How it is done is not of your business nor your concern. But you can not ask anything. You can't buy bread in a cloth store for instance. The questions must be related to the specific DI subject (interface).
Pimple is an associative array. It is not a DI - container. As far as you know, will the DI - container return an interface, where the DI - container knows which implementation to load. Let me give you an example to show you where Pimple is not a DI - container. You are in the store, you have chosen a pair of trousers and you want to buy it. Now you are going to pay. How? That is DI again. For you it is no problem, you have several methods for the transaction available and you will choose one. How the system will answer is not interesting for you: you will say nothing, just get your wallet and start paying.
In Pimple you will have to select the object to use for payments. The client will have to say 'I need the payment object'. In a true DI - container will you just have to hand over the money in a legal way (interface behavior) and based on the input data will the DI - container know which implementation to choose (cash, credit card, master card). You do not have to worry about that. If you still would have to worry about that, why call it dependency injection? Pimple is at his best a service locator, but to use DI you will have to use an interface. Otherwise is there nothing to hide, no dependency to inject. :-)
So, don't use Pimple for DI. It is not a DI - container. If you are going to use Pimple (and it can organize your classes well) then do not call it DI. It is at its best a service locator, but it is not hiding the implementation using interfaces. Hence it can not be DI.
Try to organize your codebase. What are the functions of the different classes? Which classes need DI? I suggest you only use DI for classes that execute functional requirements. When you go back to the case of the store: all functions in which the store personnel is communicating with the client directly. Not for the implementation how to walk to the back end trying to find another pair of trousers. Not for the process how to open or close the shop. But yes for how to welcome a client and asking what type of trousers someone wants to have. In your application: are there interfaces/classes which are used directly interacting with the visitors of the application and could you create some type of contract how to describe the interaction? That is the design of that DI - container. I would not use DI all over the place. DI comes with a performance penalty and a maintenance problem. The more DI you use, the more layers you will have, the less you will know what happens where. Use DI preferrably where it is most beneficial and that is where it is most likely that the implementation will change yet the caller will not know that the interface has changed nor is the caller interested in such a change. If you take that as a guideline, then can you make distinctions which classes to hide via a DI - container and which classes not.
I'd like to answer this myself now.
Dependency Injection Containers do not contain every object in your application. The fact that they are labelled containers is the main problem here. "DiC's" like Symfony's Pimple are not dependency injectors. They are glorified associative arrays used to hold objects and primarily advocate the Service Locator anti-pattern.
See $this->get('something')
in your code, like in Symfony? That's a service locator. Use that and you are hiding class dependencies and making a liar of your object API. This includes your controllers!
Object requirements should be readable from the constructor or method signatures of an object only, and a provided to this object via Dependency Injection providing Inversion of Control through your codebase helping testability, maintainability and flexibility with greatly simplified polymorphism being made available through it.
Dependency injection containers should be renamed to injectors, because that is what they should be doing - injecting dependencies for you. You should not be pulling objects out of them when you require. How are you going to use DI and inversion of control if you just pull them out when you need them using a service locator?
In real life you wouldn't build a house by transporting the entire hardware store (hopefully) to the construction site so you can access any parts you need. Instead, the foreman (
__construct()
) asks for the specific parts that will be needed (Door
andWindow
) and goes about procuring them. Your objects should function in the same way; they should ask only for the specific dependencies required to do their jobs. Giving theHouse
access to the entire hardware store is at best poor OOP style and at worst a maintainability nightmare. rdlowrey - auryn
You could use reflection to read object requirements then instantiate and inject dependencies automatically. Auryn is a fantastic injector for this. You set it up in your bootstrap and controller resolver and then you can write SOLID code for conrete auto-injection based on object typehints throughout your codebase. Any abstract classes / interfaces? No problem, you alias
concretes to these either directly in-code or by reading them from a config file (preferable) and aliasing them in a loop that way. Using a configuration file means you can have config-based polymorphism at your disposal - the ability to switch out one concrete implementation with another simply by creating the new object and changing one string in a config file is fantastic.
In conclusion, Dependency Injection Containers never become "too big" unless you're doing it wrong and using them as a service locator or a glorified associative array of objects! You don't shove all your objects in there in order to pull them out when you need them. I don't care if they're lazy-loaded. Use an actual injector. And don't even mention Laravel or "facades".
Well, there are a few things to consider when thinking about answering this question. But the primary one (which I think should answer your point):
Quite often, we have large applications, but the typical portions of it that are used as dependencies are actually typically quite a lot smaller. The reason is that a large number of classes tend to be domain objects. Objects that represent data or business cases in the application. Those classes are almost never dependencies, but are created using factories, mappers and the like.
Additionally, things like Views and other functionality-specific classes and code will likely not be managed by the DIC.
So you'll have a large portion of code that doesn't need to be managed by the container.