How not to use dependency injection frameworks

Just like any tool, a DI framework can be misused. Here’s a quick guide to understand why you might want such a tool and how to avoid shooting yourself in the foot with it.

The goal

Inversion of Control (IoC) is a very general concept which I’d describe as ‘shifting responsibility’. The goal is to make the code reusable without having to think of every possible use up front. Essentially, the Open Closed Principle.

For example, using a test framework like NUnit means that the framework (rather than your code) has control over when your code is called. By keeping the mechanism of running the test separate from the test itself, your code will be reusable with a different test framework.

Another example (more relevant to the main topic) is making a class’s dependency configurable, so rather than a class Logger calling File.WriteAllText, it might call WriteText on a member of type IPersistentStore. If the behavior of the IPersistentStore is controlled by something outside the class, then we’ve inverted control of what the Logger will achieve, or shifted the responsibility of where the logger persists the given data. Here are two examples of Dependency Injection in action:

public interface IPersistentStore
{
    void WriteText(string text);
}

public class Logger
{
    public IPersistentStore PersistentStore { private get; set; }

    public Logger(IPersistentStore persistentStore)
    {
        PersistentStore = persistentStore;
    }

    public void LogError(Exception exception)
    {
        PersistentStore.WriteText(exception.Message);
    }
}

Obviously, only one of the solutions (public setter, or constructor) need be used. The danger of the public setter is that the caller may forget to call it – therefore you may want to set a sensible default if you use this style. I’m personally of the mind that a constructor should take everything required for the object to work correctly.

Another method which I should mention is the Service Locator Pattern (SLP). Long story short, you introduce another layer of indirection by injecting an object that knows how to locate/create a concrete instance of an IPersistentStore. It’s considered an anti-pattern by many so beware of hiding dependencies and turning what would otherwise be compile-time errors into runtime failures using it. The factory pattern will probably serve you better.

Side effects of using constructor injection and the factory pattern may include:

  • Writing code against contracts of interfaces rather than implementations that are subject to change. i.e. Loose coupling.
  • Fewer ‘just in case’ configuration parameters added for possible future functionality.
  • More testable code – as different varieties of test doubles can be used.
  • Confusion when this concept is over-applied, as you trawl through 20 classes and interfaces just to print Hello World.

Using frameworks

Dependency Injection frameworks are tools that can cut down on some of the boilerplate code that you might end up writing to achieve the above goal.

Some pretty well-respected people dislike DI frameworks. That should be a good hint that there are at least some pitfalls to avoid. Since this is an area where people disagree the most important piece of advice I can give is to leave yourself open to changing your mind later. You should always consider the cost of switching when starting to use a new technology.

One of the main objections I’ve heard is from people who have locked themselves into their DI framework by scattering references to it all over their code. This is not necessary to get them to work but is easily done, and is a slippery slope once you’ve started.

If you are doing DI, and you’re hitting problems like:

  • Too many very similar looking factories.
  • Implementing singleton patterns all over the place.

Then a good DI framework may be able to help you.

Many frameworks have various “automagic” binding options which match things up based on name. Switch them off, do not be tempted. You will be scattering an important responsibility all over your codebase and putting constraints on choosing appropriate names for the context.

Initialization is a responsibility in its own right, so it should have its own classes. Ideally you should write down these “bindings” of concretions to abstractions as “high up” as possible. I’d also encourage you to put all the bindings in a separate initialization phase of your application which runs before any normal application actions occur. This way you can test that phase independently.

In summary:

  • Use constructor injection
  • Segregate interfaces
  • Write and test your application initialization as its own responsibility
  • If you use a DI framework:
    • Switch off the automagic features
    • Minimize your dependency on it (especially static classes)
Advertisements

About GrahamTheCoder

I'm Graham (the coder). A C# developer based in Cambridge hoping blogging will achieve some or all of the following. Help me organise my thoughts. Practice writing things other than code. Give me a place to refer people to when I'm trying to make a long winded point. I welcome comments and constructive criticism, and hope to look back at my written opinions in the future and laugh at my own naivety.
This entry was posted in Principles and tagged , , , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s