.NET FrameworkDependency Injection


Problems Solved By Dependency Injection

If we didn't use dependency injection, the Greeter class might look more like this:

public class ControlFreakGreeter
    public void Greet()
        var greetingProvider = new SqlGreetingProvider(
        var greeting = greetingProvider.GetGreeting();

It's a "control freak" because it controls creating the class that provides the greeting, it controls where the SQL connection string comes from, and it controls the output.

Using dependency injection, the Greeter class relinquishes those responsibilities in favor of a single responsibility, writing a greeting provided to it.

The Dependency Inversion Principle suggests that classes should depend on abstractions (like interfaces) rather than on other concrete classes. Direct dependencies (coupling) between classes can make maintenance progressively difficult. Depending on abstractions can reduce that coupling.

Dependency injection helps us to achieve that dependency inversion because it leads to writing classes that depend on abstractions. The Greeter class "knows" nothing at all of the implementation details of IGreetingProvider and IGreetingWriter. It only knows that the injected dependencies implement those interfaces. That means that changes to the concrete classes that implement IGreetingProvider and IGreetingWriter will not affect Greeter. Neither will replacing them with entirely different implementations. Only changes to the interfaces will. Greeter is decoupled.

ControlFreakGreeter is impossible to properly unit test. We want to test one small unit of code, but instead our test would include connecting to SQL and executing a stored procedure. It would also include testing the console output. Because ControlFreakGreeter does so much it's impossible to test in isolation from other classes.

Greeter is easy to unit test because we can inject mocked implementations of its dependencies that are easier to execute and verify than calling a stored procedure or reading the output of the console. It doesn't require a connection string in app.config.

The concrete implementations of IGreetingProvider and IGreetingWriter might become more complex. They, in turn might have their own dependencies which are injected into them. (For example, we'd inject the SQL connection string into SqlGreetingProvider.) But that complexity is "hidden" from other classes which only depend on the interfaces. That makes it easier to modify one class without a "ripple effect" that requires us to make corresponding changes to other classes.