原文链接:Keeping the Daggers Sharp ⚔️
原文作者:Py ⚔
译文出自:Dimon’s Program Basement
译者:Dimon
Dagger2是一个非常好的依赖注入库,但是其锋利的边缘处理起来也是比较棘手的。这就让我们来看看Square公司通过遵循哪些最佳事件来防止工程师们伤害自己!

与直接注入成员变量相比推荐通过构造函数注入
- 直接注入成员变量要求为非
final字段且非private字段。 
1 2 3 4 5
   |  class CardConverter {   @Inject PublicKeyManager publicKeyManager;   @Inject public CardConverter() {} }
 
  | 
 
- 忘记加上
@Inject会导致NullPointerException 
1 2 3 4 5 6
   |  class CardConverter {   @Inject PublicKeyManager publicKeyManager;   Analytics analytics;    @Inject public CardConverter() {} }
 
  | 
 
- 构造函数注入是更好的方式,因为它允许
不可变并且保证线程安全的对象没有部分构造的状态。 
1 2 3 4 5 6 7
   |  class CardConverter {   private final PublicKeyManager publicKeyManager;   @Inject public CardConverter(PublicKeyManager publicKeyManager) {     this.publicKeyManager = publicKeyManager;   } }
 
  | 
 
1 2
   | class CardConverter @Inject constructor(   private val publicKeyManager: PublicKeyManager)
   | 
 
- 我们依旧对系统构造的对象使用成员变量注入,比如Android Activities:
 
1 2 3 4 5 6 7 8 9 10 11
   | public class MainActivity extends Activity {   public interface Component {     void inject(MainActivity activity);   }   @Inject ToastFactory toastFactory;   @Override protected void onCreate(Bundle savedInstanceState) {     super.onCreate(savedInstanceState);     Component component = SquareApplication.component(this);     component.inject(this);   } }
  | 
 
单例应该非常罕见
1 2 3 4 5 6 7 8
   |  @Singleton public class BadgeCounter {   public final Observable<Integer> badgeCount;   @Inject public BadgeCounter(...) {      badgeCount = ...   } }
 
  | 
 
1 2 3 4 5 6 7 8 9 10 11
   |  @Singleton class RealToastFactory implements ToastFactory {   private final Application context;   @Inject public RealToastFactory(Application context) {     this.context = context;   }   @Override public Toast makeText(int resId, int duration) {     return Toast.makeText(context, resId, duration);   } }
 
  | 
 
- 在极少数情况下,我们使用作用域来
缓存创建成本高昂的实例或者重复创建和丢弃的实例。(译者注:这也是为了让你更少的通过Dagger实现单例,因为实现太简单,可能导致滥用单例。) 
与@Provides相比更推荐@Inject
@Provides方法不应该复制构造函数样板。 
- 当所有耦合问题都集中在一个地方的时候,代码更具可读性。
 
1 2 3 4 5 6 7
   | @Module class ToastModule {      @Provides RealToastFactory realToastFactory(Application context) {     return new RealToastFactory(context);   } }
   | 
 
- 这对于
单例尤为重要,这是你在阅读这个类的时候需要了解的关键实现细节(implementation detail)。 
1 2 3 4 5
   |  @Singleton public class BadgeCounter {   @Inject public BadgeCounter(...) {} }
 
  | 
 
推荐static修饰@Provides方法
- Dagger
@Provides方法能够static修饰。 
1 2 3 4 5 6 7
   | @Module class ToastModule {   @Provides   static ToastFactory toastFactory(RealToastFactory factory) {     return factory;   } }
   | 
 
- 生成的代码可以直接调用该方法,而不必创建Module实例。该方法调用可以由编译器内联。
 
1 2 3 4 5 6 7
   | @Generated public final class DaggerAppComponent extends AppComponent {      @Override public ToastFactory toastFactory() {     return ToastModule.toastFactory(realToastFactoryProvider.get())   } }
   | 
 
- 一个静态方法不会有太大变化,但所有绑定都是静态的,会导致相当大的性能提升。
 
- 试你的模块变得抽象并且如果其中一个
@Provides方法不是静态的,那么在编译时Dagger会失败。 
1 2 3 4 5 6 7
   | @Module abstract class ToastModule {   @Provides   static ToastFactory toastFactory(RealToastFactory factory) {     return factory;   } }
   | 
 
与@Provides相比更推荐@Binds
- 当您将一种类型映射到另一种类型时,
@Binds会替换@Provides。 
1 2 3 4 5
   | @Module abstract class ToastModule {   @Binds   abstract ToastFactory toastFactory(RealToastFactory factory); }
   | 
 
- 该方法必须是抽象的。它永远不会被调用;生成的代码将知道直接使用该实现。
 
1 2 3 4 5 6 7 8 9 10 11
   | @Generated public final class DaggerAppComponent extends AppComponent {      private DaggerAppComponent() {          this.toastFactoryProvider = (Provider) realToastFactoryProvider;   }   @Override public ToastFactory toastFactory() {     return toastFactoryProvider.get();   } }
   | 
 
避免在接口绑定上使用@Singleton
Statefulness is an implementation detail
- 只有实现才知道他们是否需要确保集中访问的可变状态。
 
- 将实现绑定到接口时,不应该有任何作用域注释。
 
1 2 3 4 5 6
   | @Module abstract class ToastModule {      @Binds @Singleton   abstract ToastFactory toastFactory(RealToastFactory factory); }
   | 
 
启用error-prone
几个Square团队正在使用它将常见的Dagger错误检查出来。
结论
这些指导原则适用于我们:小型异构团队在大型共享Android代码库上工作。由于您的背景可能不同,因此您应该应用对您的团队最有意义的内容。
轮到你了!您遵循哪些良好做法来保持Dagger代码的清晰度?