Modern Dependency Injection

综合编程 2016-06-11

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, andmock 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!

您可能感兴趣的

GraphQL with ASP.NET Core (Part- III : Dependency ... Read the previous part - GraphQL with ASP.NET Core Part- II : Middleware The letter 'D' in SOLID stands for Dependency inversion princ...
为什么多线程、junit 中无法使用spring 依赖注入?... 为什么多线程、junit 中无法使用spring 依赖注入? 这个问题,其实体现了,我们对spring已依赖太深,以至于不想自己写实例了。 那么到底是为什么在多线程和junit单元测试中不能使用依赖注入呢? 一、为什么多线程下spring的依赖注入失效了呢? 答:因为spring为了考...
Road to dependency injection – Matthias Noba... Statically fetching dependencies I've worked with several code bases that were littered with calls to Zend_Registry::get() , sfContext::ge...
Dependency Injection in ASP.NET Core – Demys... .NET Core and ASP.NET Core have been around for almost 2 years since its 1.0 release. With it came a new modern and multi-platform framework built fr...
Spring源码系列:依赖注入-引言 在上面四篇文章中大概分析了一下Bean的载入,其实这个过程就是在Ioc容器中建立BeanDefinition的数据映射。但是对于Bean的实例化并未涉及,在之前的分析中也提到,bean的实例化是在依赖注入是才具体完成。 关于依赖注入 关于Spring,我们最先想到的就两个Ioc和Aop;然后...