.NET and me Coding dreams since 1998!

16Oct/0912

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:

  1. How to avoid passing those common but uninteresting dependencies interfaces to every class?
  2. How do you approach dependencies that might be used, but may be expensive to create?
  3. 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."

Appendix

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

Comments (12) Trackbacks (0)
  1. Nice article, I only would not agree with complete getting rid of factories. Imo, even having IoC actively used in the project factory itself should not become completely transparent as it's logic can be requested to become more complicated and within the factory/factory method it would be much easier to retreive something from container based on domain logic rules.

  2. And thank you for posting this blog instead of twitter!

  3. Great post. Regarding 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."

    what do you mean by "Including unit tests"?

    You mean that in unit tests is better to call bootstraper class before test to register dependencies rather than use IoC container explicitly in unit test ?

  4. Hi there, thought I'd comment since I asked the original SO question.

    Good post, you make some really good points.

    The reason that my example is like it is, is that I was looking to introduce an IoC container to begin to bring dependencies under control in a solution that has approaching 1 million lines of code. You are absolutely right that SRP is being violated, but there is no way that I can fix that overnight. Have to start somewhere right?

    Anyway, we're using Unity now, and things are slowly moving in the right direction.

  5. @Radenko,

    what I learned in last 3 years of unit testing is that the easiest unit test to be written is the one which doesn’t need any extra bootstrappers calls andor ServiceLocator initializations etc…

    In general way to do that is by following the SRP in a sense that you don’t allow your dependencies to go to deep and following some of the rules I stated in this blog post.

    It’s hard to explain it like this, so I would add that subject to the pipeline of my subjects I need to blog post in next weeks (right now blogging about the way I ‘enhanced’ Prism looks more fun to blog about)

    As a rule of thumb, I have recently started looking at all test  fixtures code as a sign of potential test smell – but that is just my preference which (again) I would try to defend in next couple of weeks through my blog posts :)

  6. @Nikola

    But if I want to test if some method from my dependency class is called and that class is registered in Bootstrapper and resolved in my concrete method i need to mock that class and register mocked instance of that class in my Ioc container.

    To do this I need to register mocked class in unit test and use Ioc container in unit test.

    Do you know better way?

    Thanks

    Radenko

  7. I hear you what are you saying :)

    If you have ILoggingService IoC mapped in bootstrapper and used in SomeClass.SomeMethod – how come I clame that you don’t need to init IoC because the container wouldn’t be able to resolve it and SomeMethod would fail

    In short, to do what I have in mind one would have to:

    -  follow my rule #3 -> the ILoggingService would have to be injected in constructor.

    - follow my rule #5 -> Use BuildUp to perform auto object graph build up.

    That’s how your bootstrapper  mappings would be active in your code (during the runtime) without SomeClass knowing about IoC and allow you (while writing tests) to simply inject the IloggingService mock or use AutoMockingContainer to do that for you

    My next post would provide some small example illustrating in more details how I do that now – it is very simple once you see it

    I hope this makes some sense to you  :)

    Nikola

  8. I am not sure that we thinking on same thing.

    If I have :

    Public static Class Bootstraper

    {

    public static void SetUp()

    {

    IContainer cont=UnityContainer.Instance();

    cont.Register<ISomething,A>();

    }

    }

    Public Class A: ISomething

    {

    public void Pero ()

    {}

    }

    Public Class B

    {

    public void Djuro {

    IContainer cont=UnityContainer.Instance();

    ISomething classA = cont.Resolve<ISomething >();

    classA.Pero();

    }

    }

    I want to test that method Pero is called. I need to register mock of Class A in unit test  and that breaks your fifth law?

  9. I believe we are talking about the same :)

    Let me use tommorow your sample for my blog post to illustrate what is my point, today is release day so I don’t have time to do it right now

  10. VERY interesting post. Eagerly awaiting your example for tomorrow :-)

  11. I have a very similar question up on StackOverflow stackoverflow.com/…/is-it-bad-to-use-servicelocation-instead-of-constructor-injection-to-avoid-writin

    The situation in particular that I am interested in is when you have to pass parameters AND dependencies to a class. I don't particularly like writing loads of factory classes just to wrap constructors.

  12. Wow. I really wish I had found this post when I started out with DI. I've found that my own code has violated these laws and my code just didn't "feel right."

    Thank you so much for this.


Leave a comment

No trackbacks yet.