好的实践
依赖导致(DI)并不是火箭科技。我们只是需要尽力避免创建对象和使用单例,除非有使用它们的有足够说服力的理由,比如工具类方法没有外部依赖、或者一个工具类不可能被框架外部所使用(互操作性包装和字典键的这种常见的例子)。
IoC 框架的许多问题都是在开发者第一次学习如何使用它们时出现的,他们不但没有真正改变处理依赖项以及抽象的方式来符合 IoC 模型,反而尝试操作 IoC 容器来迎合他们固有的编码风格,这经常导致高耦合和低内聚。
使用复合根避免服务定位器的反模式
我们的应用依赖树应该只有一个根元素(即应用复合根),它是唯一的我们应该调用解决方法的组件。
每次调用解决方法时,我们都需要注入些什么,如同它是一个服务定位器一样的反模式。如果我们使用 MVC 框架,那么复合根应该位于应用类的路由逻辑附近或者在控制器工厂类里。
避免构造器的过度注入
构造器的过度注入违反了单一职责原则。太多的构造器参数表明了太多的依赖;太多的依赖表明了这个类尝试做得太多了。通常这个错误伴随着其他的代码坏味道,比如通常有着很长而且模糊的类名称(xxxManager之类)。
避免注入数据,而不是行为
注入数据,而不是行为,是政治家反模式的一个子类型,只是在这里容器是那个没用的类。如果一个类需要知道当前日期和时间,那么没有必要注入一个日期和时间,即数据;相反的,你应该注入一个系统时钟的上层抽象。这不仅仅在 DI 时是正确的做法;这对可测性至关重要,因为你可以不必等待就能够测试不同的时间下的行为。
避免将所有的生命周期声明为单例
将所有的生命周期声明为单例,对我来说,这就是货物崇拜编程的完美例子,简直是俗称的“对象污水池”。我看过多到我记不住的单例滥用案例,其中很少和 DI 相关。
避免特定于实现的接口类型
另一常见的错误是实现特定于实现的接口类型,只是为了能够在容器中注册。这本身就违背了依赖倒置原则(仅仅因为它是一个接口并不能说明它真的抽象)并且经常包括接口膨胀从而违反了接口隔离原则。
避免可选依赖
换句话说,有个构造器接受依赖注入,但是也存在另一个构造器使用了“默认”实现。这也违反了依赖倒置原则并且容易引发违背里斯科夫替代原则,作为开发者,随着时间推移,开始假设有默认实现,并且开始使用默认构造器来创建实例。