10 Minute Vim!

Modern Dependency Injection

· Read in about 2 min · (371 Words)

Dependency Injection can be greatly simplified while retaining all of its power.

The Unnecessary Boilerplate

Injection of an interface through the constructor is a common way to replace a dependency for unit-test mocking. Unfortunately, it is an extremely verbose pattern.

Look how much boilerplate is needed just to mock out a call to DateTime.Now:

 1 public interface ICurrentTime {
 2     DateTime GetCurrentTime();
 3 }
 4 
 5 public class CurrentTime : ICurrentTime {
 6     public DateTime GetCurrentTime() {
 7         return DateTime.Now;
 8     }
 9 }
10 
11 public class Formatter {
12     private readonly ICurrentTime currentTime;
13 
14     public Formatter() : this(new CurrentTime()) {}
15 
16     public Formatter(ICurrentTime currentTime) {
17         this.currentTime = currentTime;
18     }
19 
20     public string Format(string input) {
21         return string.Format("{0}: {1}", currentTime.GetCurrentTime().ToString(), input);
22     }
23 }

We learned to mock this way because Java did not have lambdas at the time the pattern was invented! Now that both Java and C# have lambdas, the existing DI pattern can be improved.

For Unit Test Mocking

Thanks to the power of lambdas, the previously highlighted lines (1-19) can be removed!

1 public class Formatter {
2     internal Func<DateTime> currentTime = () => DateTime.Now;
3 
4     public string Format(string input) {
5         return string.Format("{0}: {1}", currentTime().ToString(), input);
6     }
7 }

Much better! The code is easier to read, understand, and mock in a unit test.

For Polymorphism

If you still need to replace the function with another for polymorphic dispatch, use constructor injection of just the lambda. Be forewarned, you probably need this much less than you think!

 1 public class Formatter {
 2     internal Func<DateTime> currentTime;
 3 
 4     public Formatter() : this(() => DateTime.Now) {}
 5 
 6     public Formatter(Func<DateTime> currentTime) {
 7         this.currentTime = currentTime;
 8     }
 9 
10     public string Format(string input) {
11         return string.Format("{0}: {1}", currentTime().ToString(), input);
12     }
13 }

If you are heavily invested in unit-testing, you might find you need very little actual interface polymorphism. To see where you really use interface polymorphism, find the interfaces in your codebase only have a single concrete class in production. Each interface with a single concrete class is test-only boilerplate that can safely replaced with a lambda!

For more examples, check out SimpleMock!

STEVE SHOGREN

software developer, manager, author, speaker

BOOKS:

POSTS FOR:

CATEGORIES