Guice协助注入过度使用的替代方案,依赖注入与非DI构造方法

时间:2014-12-25 23:42:38

标签: java oop dependency-injection guice

我喜欢在构造函数中传递运行时依赖项的主要原因是:

  1. 它需要依赖
  2. 它提供了设置实例变量的中心位置
  3. 将依赖项设置为实例变量会阻止您 必须从类中的方法传递给它或者在类中传递两次或更多次 两种或更多种公共方法
  4. 这使我在使用Guice时使用了很多Assisted Injects。与不使用DI相比,这会创建额外的代码,因此请阅读以下内容: How exactly is Assisted-inject suppose to be use to be useful?

    似乎大多数人在使用辅助注入的构造函数中没有通过运行时(派生的,在启动时不可用)依赖项,而是在各个方法中传递它们。这对于上面的stackoverflow帖子中给出的简单类很好,其中只有一个依赖于依赖的方法:

    public class SomeClass {
        @Inject
        SomeClass(...) {
            ...
        }
    
        public void doWork(int s) { /* use s */ ... }
    }
    

    但是如果该类有很多使用依赖的方法呢?你是否将它从公共方法传递给私有方法并要求它传递给所有公共方法? 例如:

    public class SomeClass {
        @Inject
        SomeClass(...) {
            ...
        }
    
        public void doWork(int s) { 
         /*some code */
         someOtherMethod(s);
         anotherMethod(s);  
    
        }
        //any private method that needs it gets it passed in as a param
        private void someOtherMethod(int s)...
        private void anotherMethod(int s)...
    
        //require it passed in all public methods that need it
        public void anotherPublic(int s){
          someOtherMethod(s);
        }
    }
    

    与使用构造函数相反,这会增加一些额外的代码,如下所示:

    public class SomeClass {
        private int s;
        SomeClass(int s) {
            this.s = s;
        }
    
        public void doWork() { 
         someOtherMethod();
         anotherMethod();  
    
        }
        private void someOtherMethod()...
        private void anotherMethod()...
    
        public void anotherPublic(){}
    }
    

    或者你会从服务方法中设置实例var吗?

    public class SomeClass {
        Integer s;
        @Inject
        SomeClass(...) {
            ...
        }
    
        public void doWork(Integer s) { 
         /***set instance var this time***/
          this.s = s;
         someOtherMethod();
         anotherMethod();  
    
        }
        private void someOtherMethod()...
        private void anotherMethod()...
    
        public void anotherPublicMethod(){
         if(s==null){ //check if s was set already
             throw new IllegalStateException();
          }else{
          /* do something else */
          }
        }
    
    
    }
    

    或者你会将依赖项作为参数传递给其他公共方法并在那里设置实例变量吗?例如:

    public class SomeClass {
        @Inject
        SomeClass(...) {
            ...
        }
    
        public void doWork(Integer s) { 
         /***set instance var this time***/
          this.s = s;
         someOtherMethod();
         anotherMethod();  
    
        }
        private void someOtherMethod()...
        private void anotherMethod()...
    
        public void anotherPublicMethod(Integer s){
           this.s = s;
          /* do something else */
        }
    
    
    }
    

    所以我认为将param从方法传递给方法或抛出非法状态异常以检查它与使用普通构造函数相比并不理想,但显然任何框架/模式都有优点/缺点。

    如果我不是以理想的方式分离我的对象,请告诉我您使用的一些指导方针,即"我每个服务类只使用一种公共方法,请参阅本书或发布相关内容: " 。

    你们在上述情况下做了什么?

1 个答案:

答案 0 :(得分:3)

你确定了在你的问题中使用辅助注入的一些很好的理由:它确保对象实例只存在于完全初始化的状态,将你的依赖关系保持在一起,并释放对象的公共接口,使其不需要可预测的参数各种方法。

除了你提到的那些之外,我真的没有任何其他选择:

  • 为该依赖项添加setter方法,可能需要IllegalStateException检查或一个好的默认值
  • 使用相同的IllegalStateException检查创建initialize(int s)伪构造函数方法
  • 接受个别方法中的参数
  • 使用自定义工厂替换FactoryModuleBuilder样板文件,从而创建更多额外的样板文件,以避免

我的最爱是你似乎在两者之间决定的 - 辅助注射或在每种方法中采用参数 - 主要是因为它们都始终将对象保持在可预测的可用状态。我之间的决定取决于对象应该携带什么样的状态该状态是否可变,以及我想如何控制实例。对于Car.licensePlateNumber,车牌号可能因车辆实例而异;每辆车都有一个车牌号(在这个例子中)永远不会变化,如果没有它,汽车是无效的,所以它应该是一个构造函数参数。相反,Repository<T>可能经常在其所有方法中采用相同的T实例,但无论您传入哪个实例,存储库仍然是存储库,您可能希望自由地重用该实例不为每个T创建一个新的(因为您可能与辅助注射有关)。这两种设计都是有效的,每种设计都适用于某些情况。

请记住there shouldn't really be that much extra code required for assisted injection

/** In module: install(new FactoryModuleBuilder().build(SomeClass.Factory.class)); */
public class SomeClass {
  public interface Factory {
    SomeClass create(int s);
  }

  private final int s;

  @Inject
  SomeClass(/* ..., */ @Assisted int s) {
    this.s = s;
  }

  public void doWork() { /* ... */ }
}