Inversion Of Control, Single Responsibility Principle and Nikola’s laws of dependency injection
Today I stumbled upon the stack overflow question regarding using DI frameworks for classes with many dependencies where the example given and couple of answers have reminded me about a subject I want to post for a long time.
So the example from question basically goes like this:
MyClass(ILog log, IAudit audit, IPermissions permissions, IApplicationSettings settings)
// … versus …
ILog log = DIContainer.Get<ILog>();
And the 3 questions related to this example are:
- How to avoid passing those common but uninteresting dependencies interfaces to every class?
- How do you approach dependencies that might be used, but may be expensive to create?
- How to build object graph of MyClass considering the fact that any of those dependencies can have their own dependencies etc..
So, let’s start and keep it short…
What is wrong fundamentally with his example?
Being a big proponent of DDD principles as the one leading to clean and maintainable design, I found the design of the question example is wrong in a sense that an entity (which MyClass is) should NOT have dependencies on infrastructure (or any other) services.
Related to that is Nikola’s 1st law of IoC
Store in IoC container only services. Do not store any entities.
I strongly believe that if the author of example was following that principle he wouldn’t be in the position to ask the question he did ask.
Second thing I learned in past years to detect as wrong in code like the one in example is the high number of dependencies in a constructor which usually in my experience points to SRP (Single Responsibility Principle) violations and low SOC (Separation of Concerns) in code. The example is not showing the code of the MyClass but (based on my experience) I am pretty sure it can be broken to couple of coherent SRP separate classes where each one of them would have very few (if any) number of dependencies
Related to that is Nikola’s 2nd law of IoC
"Any class having more then 3 dependencies should be questioned for SRP violation"
Basically all answers on the question are agreeing that second approach is worst (together with me) because burring up the dependencies a class has prevents effective black box unit testing and makes harder refactoring. I already blogged about transparent vs. opaque DI, so I’ll skip beating the dead horse here.
The thing I do want here to discuss are the answers which are recommending in general replacing the first case with the single dependency on IServiceLocatorIContainer.
IServiceLocatorIContainer injection doesn’t make a lot of sense in general
There is no real advantage of using the IServiceLocator vs the DIContainer from the second solution. I mean we would be decoupled from the specific IoC container but the problems of low testability and hard refactoring would stay due to the same opaque nature of dependencies class has. In other words, from my point of view using singleton service locator in this sense is even better then the IServiceLocator being injected (same set of problems, one parameter less in constructor).
The only exception from this rule is the case when we have multiple components mapped to a service with a different key (IoC version of Strategy pattern implementation) there is no way to inject the one with a given key (as long they share the same interface) so injecting IContainer in that case is acceptable.
Setter type of dependency injection shouldn’t be used
Second type of answer is not to use constructor dependency injection but instead setter type of DI (I’ve blogged about the differences long time ago). Couple a years ago I was fan of the setter type from the same reasons (removes the constructor noise) but the set of problems I was facing in real world related to it convince me that it is much worse choice then the constructor type. The main reasons behind that opinion are primarily due to the facts that setter type of DI makes dependencies again opaque and (in this case) even more important result with creation of public API members which only purpose is to satisfy infrastructure needs which (I believe) is deeply wrong. No API members should be created just in order to satisfy the infrastructure andor unit testing needs.
Nikola’s 3rd law of IoC
Every dependency of the class has to be presented in a transparent manner in a class constructor.
Factories and IoC
The question contains a thought of injecting the factory interface which reminded me also on a discussion I had with one of my colleagues regarding usage of factories in certain scenarios (as proposed in Art Of Unit Testing – go buy that book in case you haven’t done that already).
IMO, using factories together with IoC doesn’t make a lot of sense because IoC container is in a sense “universal abstract factory”.
In other words, in my experience any time I thought about adding a factory I ended with much simpler IoC based solution so that’s why I would dare to say that “IoC kill the Factory star"
Q2: How do you approach dependencies that might be used, but may be expensive to create?
Very simple, don’t create them to be like that. A constructor of a class being resolved from a IoC should be as light as possible just defining (if any) its own dependencies. Any class initialization or implementation shouldn’t be implicitly triggered from constructor but instead explicitly by invoking a specific member on instance resolved from a container. That’s how resolving all of them could be done without any significant performance issues.
Nikola’s 4th law of IoC
Every constructor of a class being resolved should not have any implementation other then accepting a set of its own dependencies.
Q3: How to build object graph of MyClass?
Without going into the details of how to do this with given framework (which other did in the SO answers and I covered it for Unity here) I would like to emphasize again the need for moving the mapping definition and resolution from UI elements (as proposed as option question) to dedicated application bootstrapper classcomponent which (implementing the Builder design pattern) sole responsibility would be to define the mappings in a single place or orchestrating other component bootstrappers.
On the beginning of the application life cycle, bootstrapper would build up the dependencies removing (ideally) the need for other parts of code base to be aware of the IoC container awareness.
(More about Builder pattern in dusty but still good Jeremy’s blog post)
Nikola’s 5th law of IoC
IoC container should be explicitly used only in Bootstrapper. Any other “IoC enabled” code (including the unit tests) should be completely agnostic about the existence of IoC container."
So here we go, my dear reader – my first blog post in a while. I decided to fight with my Twitter addiction which sucked up all of my blogging energy and to start writing down (in my awful English) the thoughts and experiences I collected in last year so stay tuned for the bunch of very diverse and (hopefully) amusing blog posts