What is the purpose of IOC Containers? The combined reasons for it can be simplified to the following:
When using OOP/SOLID Development principles, Dependency Injection gets messy. Either you have the top-level entry points managing dependencies for multiple levels below themselves and passing dependencies recursively through construction, or you have somewhat duplicate code in factory/builder patterns and interfaces that build dependencies as you need them.
There is no OOP/SOLID way to perform this AND have super pretty code.
If that previous statement is true, then how do IOC Containers do it? As far as I know, they aren’t employing some unknown technique that can't be done with manual D.I. So the only explination is that IOC Containers break OOP/SOLID Principles by using static objects private accessors.
Do IOC Containers break the following principles behind the scenes? This is the real question since I have a good understanding, but have a feeling somebody else has a better understanding:
- Scope control. Scope is the reason for nearly every decision I make on my code design. Block, Local, Module, Static/Global. Scope should be very explicit, as much at block-level and as few at Static as possible. You should see declarations, instantiations, and lifecycle endings. I trust the language and GC to manage scope as long as I’m explicit with it. In my research I’ve found that IOC Containers set up most or all dependencies as Static and control them through some AOP implementation behind the scenes. So nothing is transparent.
- Encapsulation. What is the purpose of encapsulation? Why should we keep private members so? For practical reasons it is so implementors of our API can’t break the implementation by changing the state (which should be managed by the owner class). But also, for security reasons, it’s so injections can’t occur that overtake our member states and bypass validation and class control. So anything (Mocking frameworks or IOC frameworks) that somehow injects code before compile time to allow external access to private members is pretty huge.
- Single Responsibility Principle. On the surface, IOC Containers seem to make things cleaner. But imagine how you would accomplish the same thing without the helper frameworks. You would have constructors with a dozen or so dependencies being passed in. That doesn’t mean cover it up with IOC Containers, it is a good thing! It’s a sign to re-factor your code and follow SRP.
Open/Closed. Just like SRP isn’t Class-Only (I apply SRP down to single-responsibility lines, let alone methods). Open/Closed is not just a high level theory to not alter the code of a class. It’s a practice of understanding the configuration of your program and having control over what gets altered and what gets extended. IOC Containers can change the way your classes work altogether partially because:
a. The main code isn’t making the determination of switching out dependencies, the framework configuration is.
b. The scope could be altered at a time that isn’t controlled by the calling members, it is instead determined externally by a static framework.
So the configuration of the class isn’t really closed is it, it alters itself based on the configuration of a third party tool.
The reason this is a question is because I am not necessarily a master of all IOC Containers. And while the idea of an IOC Container is nice, they appear to just be a façade that covers up poor implementation. But in order to accomplish external Scope control, Private member access, and Lazy loading, a lot of non-trivial questionable things have to go on. AOP is great, but the way it is accomplished through IOC Containers is also questionable.
I can trust C# and the .NET GC to do what I expect it to. But I can’t put that same trust in a third party tool that is altering my compiled code to perform workarounds to things I wouldn’t be able to do manually.
E.G.: Entity Framework and other ORMs create strongly Typed objects and map them to database entities, as well as provide boiler-plate basic functionality to perform CRUD. Anybody could build their own ORM and continue to follow OOP/SOLID Principles manually. But those frameworks help us so we don’t have to re-invent the wheel every time. Whereas IOC Containers, it seems, help us purposely work around OOP/SOLID Principles and cover it up.
Explicitness of codeis exactly the thing I have problems with. Manual DI can easily become a chore, but it at least puts selfcontained explicit program flow in one place - "You get what you see, you see what you get". While IOC containers' bindings and declarations can easily become a hidden parallel program on its own.