Search code examples
c#dependency-injectionsingletondependency-managementservice-locator

Dependency Injection vs Service Location


I am currently weighing up the advantages and disadvantages between DI and SL. However, I have found myself in the following catch 22 which implies that I should just use SL for everything, and only inject an IoC container into each class.

DI Catch 22:

Some dependencies, like Log4Net, simply do not suit DI. I call these meta-dependencies and feel they should be opaque to calling code. My justification being that if a simple class 'D' was originally implemented without logging, and then grows to require logging, then dependent classes 'A', 'B', and 'C' must now somehow obtain this dependency and pass it down from 'A' to 'D' (assuming 'A' composes 'B', 'B' composes 'C', and so on). We have now made significant code changes just because we require logging in one class.

We therefore require an opaque mechanism for obtaining meta-dependencies. Two come to mind: Singleton and SL. The former has known limitations, primarily with regards to rigid scoping capabilities: at best a Singleton will use an Abstract Factory which is stored at application scope (ie. in a static variable). This allows some flexibility, but is not perfect.

A better solution would be to inject an IoC container into such classes, and then use SL from within that class to resolve these meta-dependencies from the container.

Hence catch 22: because the class is now being injected with an IoC container, then why not use it to resolve all other dependencies too?

I would greatly appreciate your thoughts :)


Solution

  • Because the class is now being injected with an IoC container, then why not use it to resolve all other dependencies too?

    Using the service locator pattern completely defeats one of the main points of dependency injection. The point of dependency injection is to make dependencies explicit. Once you hide those dependencies by not making them explicit parameters in a constructor, you're no longer doing full-fledged dependency injection.

    These are all constructors for a class named Foo (set to the theme of the Johnny Cash song):

    Wrong:

    public Foo() {
        this.bar = new Bar();
    }
    

    Wrong:

    public Foo() {
        this.bar = ServiceLocator.Resolve<Bar>();
    }
    

    Wrong:

    public Foo(ServiceLocator locator) {
        this.bar = locator.Resolve<Bar>();
    }
    

    Right:

    public Foo(Bar bar) {
        this.bar = bar;
    }
    

    Only the latter makes the dependency on Bar explicit.

    As for logging, there's a right way to do it without it permeating into your domain code (it shouldn't but if it does then you use dependency injection period). Amazingly, IoC containers can help with this issue. Start here.