1

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

例如,A 需要 B,需要 C,需要 Z,需要从命令行输入。最后,我觉得一切都会被辅助注射。因此,鉴于我坚持使用它,我想确保我正确使用它。

我个人觉得写自己的工厂也一样好。此外,除了相同的优势之外,我还可以进一步将我的相关对象的创建限制在这些工厂中。

因此,我的问题是,使用辅助注入到底有多大用处,仅仅是同时辅助和非辅助的想法吗?如果在我的情况下您只有辅助参数怎么办?

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

如果有人能在这里启发我,我将不胜感激,

非常感谢

PS:

我的依赖是这样的:

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

这里的重点是下图: InfrastructureService -> KnowledgeBaseService -> ConFigDataObject -> InputData

因此,InfrastructureService只能使用以正确的 inputFile、URL、工作文件夹等启动的知识库服务,它通过 configDataObject 提供给它,从程序的输入接收它们并存储处理后的版本他们。

因此,到目前为止,我所做的是有一个 assistedFactory 来创建KnowledgeBaseService。它将 ConfigDataObject 作为参数。configDataObject是使用factoryMethod (Scala Companion Object)创建的。最后,还为InfrastructureService创建了一个 assistedFactory ,其创建方法KnowledgeBaseService

正如您所猜想的那样,几乎所有内容都是预先创建的,并且以某种方式手动创建。我觉得很奇怪。

4

2 回答 2

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扩展。用户输入将在第一次访问提供者时触发,然后由于其范围,结果将被缓存。

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

于 2013-12-26T04:44:38.533 回答
1

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

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

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)

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

于 2013-12-26T17:18:38.717 回答