什么时候辅助 - 注射有用吗?

时间:2013-12-26 01:08:07

标签: java guice assisted-inject

我目前在我的应用程序中使用Guice。然而,我发现自己大多使用辅助注入,因为有一系列注入的对象都取决于程序的输入。 因此几乎所有东西都是辅助注射

例如A需要B需要需要Z的命令,需要从命令行输入。最后我觉得一切都会被辅助注入。所以考虑到我坚持下去,我想确保我正确使用它。

我个人觉得写自己的工厂会很好。此外,除了相同的优点,我还可以进一步限制我的对象的创建到这些工厂。

因此,我的问题是,使用辅助注射真的有用,它是否只是同时辅助和非辅助的想法?如果在我的情况下你只有辅助参数怎么办?

他们必须通过辅助注射来组织它。我只是没有看到它。

如果有人能在这里启发我,我会非常感激,

非常感谢

PS:

我的依赖是这样的:

我有 InfrastructureService ,这需要 KnowledgeBaseService ,而知识库服务又需要 ConFigDataObject 。我的configDataObject包含来自程序输入的信息。 configDataObject在对这些输入进行一些验证和处理后存储这些信息。例如,可以向其提供表示文件路径的字符串,并且它将验证它是否是存在的文件,并且具有将该File返回给其使用者的getter方法。其他东西可能是URLNname到真实URL对象,依此类推。

重要的一点是以下图表: InfrastructureService - > KnowledgeBaseService - > ConFigDataObject - >的 InputData

因此 InfrastructureService 只能使用以右输入文件,URL,工作文件夹等启动的 knowledgeBaseService .... configDataObject,从程序的输入接收它们并存储它们的处理版本。

因此,截至目前,我所做的是使用assistFactory来创建 knowledgeBaseService 。它将ConfigDataObject作为参数。 configDataObject 是使用 factoryMethod(Scala Companion对象)创建的。最后,还为 InfrastructureService 创建了辅助工厂,该工具作为创建方法 KnowledgeBaseService 的参数。

正如你可以猜到的一切都是在前面创建的,并且不知何故是手动创建的。我发现它很奇怪。

2 个答案:

答案 0 :(得分:4)

看起来你过度使用了“依赖”的概念。您应该首先将包含用户输入(实际上是任何数据)的类与包含业务逻辑的类分开,然后您应该通过方法而不是通过注入传递这些数据,因为用户输入不是依赖项。

这样你几乎不需要辅助注入,因为你可以用new直接创建“数据”类(没关系,因为它们没有依赖关系),并且“行为”类可以互相注入通过构造函数以标准方式。然后“行为”类将工作,传递“数据”类的对象,而不是将它们作为依赖项。您将看到辅助注射的需求消失,您的程序变得更加简单易懂。

例如,不要使用以下内容:

public class OtherDependency {
    private final int s;

    @Inject
    OtherDependency(@Assisted int s, ...) {
        this.s = s;
        ...
    }

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

public class SumService {
    private final int x1;
    private final int x2;
    private final OtherDependencyFactory depFactory;

    @Inject
    SumService(@Assisted int x1, @Assisted int x2, OtherDependencyFactory depFactory) {
        this.x1 = x1;
        this.x2 = x2;
        this.depFactory = depFactory;
    }

    public void doWork() {
        int s = x1 + x2;

        OtherDependency dep = depFactory.create(s);
        dep.doWork();
    }
}

public class EntryPoint {
    private final SumServiceFactory sumServiceFactory;

    @Inject
    EntryPoint(SumServiceFactory sumServiceFactory) {
        this.sumServiceFactory = sumServiceFactory;
    }

    public void start() {
        Scanner sc = new Scanner(System.in);
        int x1 = sc.nextInt();
        int x2 = sc.nextInt();

        SumService sumService = sumServiceFactory.create(x1, x2);
        sumService.doWork();
    }
}

(显然,我对你的程序了解不多,但当我看到“首先需要用户输入的依赖链”时,这就是我所想到的)

你应该做类似

的事情
public class OtherDependency {
    @Inject
    OtherDependency(...) {
        ...
    }

    public void doWork(int s) { /* use s */ ... }
}

public class SumService {
    private final OtherDependency dep;

    @Inject
    SumService(OtherDependency dep) {
        this.dep = dep;
    }

    public void doWork(int x1, int x2) {
        int s = x1 + x2;
        dep.doWork(s);
    }
}

public class EntryPoint {
    private final SumService sumService;

    @Inject
    EntryPoint(SumService sumService) {
        this.sumService = sumService;
    }

    public void start() {
        Scanner sc = new Scanner(System.in);
        int x1 = sc.nextInt();
        int x2 = sc.nextInt();

        sumService.doWork(x1, x2);
    }
}

所有用户输入都通过方法参数从一个类转移到另一个类,这些类本身是无状态的,通常只是注入。根本不需要使用辅助注射。

<强>更新

我已阅读您的更新。有多种可能性可以做到你想要的,具有不同的复杂性。

首先,最简单的变体之一(我认为在这些情况下最好的变体)是通过方法调用传递输入数据。我不知道你的架构如何禁止这个。只需让KnowledgeBaseService上需要此数据的所有方法接受它,然后从InfrastructureService传递它。你不需要辅助注射。

其次,您可以在创建注入器之前获取用户输入,然后执行toInstance()绑定。这取决于实际的架构,因此可能无法正常工作。我相信这是最简单,最简单灵活的结构。

第三,您可以使用提供者。为ConfigDataObject创建一个提供程序,要求用户输入并创建相应的实例,然后在ConfigDataObject范围内将Singleton绑定到该提供程序。如果用户输入没有失败,这将起作用,因为您不应该从提供者抛出异常。如果您需要用户输入验证,请使用throwing providers扩展名。然后,在第一次访问提供者时将触发用户输入,然后由于其范围,结果将被缓存。

可能还有其他方法,但我能够提出这些方法。

答案 1 :(得分:1)

由于应用程序的参数,我采用的方式是以更优雅的方式解决问题,而不是使用不必要的辅助注入链。

那就是如果在我的情况下你的数据来自命令行那么我认为正确的appraoch是将输入数据结构的类型绑定到从命令行获得的实例输入结构:

object MyApp extends App {

  val config = ConfigData(args(0))

  val injector = Guice.createInjector(module(config))
  val service = injector.getInstance(classOf[InfrastructureService])

  println("The service name is:" + service.kbService.config.configName)

}


case class module(config: ConfigData) extends AbstractModule {
  def configure(): Unit = {
    bind(classOf[ConfigData]).toInstance(config)
  }
}

case class ConfigData(val configName: String)

class KbService @Inject() (val config: ConfigData)

class InfrastructureService @Inject() (val kbService: KbService)

我相信这里的关键是提醒自己模块可以用任何必要的输入数据进行参数化