Java 我应该注入执行算法所需的对象吗?我应该注射所有的东西吗?

Java 我应该注入执行算法所需的对象吗?我应该注射所有的东西吗?,java,dependency-injection,guice,Java,Dependency Injection,Guice,也许我在文档中遗漏了它,但我想知道我应该如何处理“助手对象” 代码示例: public Path dijkstra(Node startNode, Node endNode) { Set<Node> nodesToInspect = new HashSet<Node>(); // should this Object be injected? Path path = new Path(); // and this one? while (!n

也许我在文档中遗漏了它,但我想知道我应该如何处理“助手对象”

代码示例:

public Path dijkstra(Node startNode, Node endNode) {
    Set<Node> nodesToInspect = new HashSet<Node>();  // should this Object be injected?
    Path path = new Path();  // and this one?

    while (!nodesToInspect.isEmpty()) {
        // some logic like:
        path.add(currentNode);

    }

    return path;
}
公共路径dijkstra(节点开始节点,节点结束节点){
Set nodesToInspect=new HashSet();//是否应注入此对象?
Path Path=newpath();//这一条呢?
而(!nodesToInspect.isEmpty()){
//一些逻辑如下:
add(currentNode);
}
返回路径;
}
我应该注入所有东西,还是应该在某个时刻说算法“知道”它最需要什么?
我应该试着消除每一个“新的”吗?或者一些对象创建是否很好,例如HashSet、ArrayList等API类。

在用依赖项注入替换简单的
新的
之前,您需要问自己“我为什么要这样做?”。。。“它有什么真正的好处?”。如果答案是“我不知道”或“什么都不知道”,那么你不应该

在本例中,我看不到在示例代码的第一个案例中使用DI的真正好处。除了该方法之外,没有必要知道内部集合是如何表示的。。。甚至知道它的存在

你应该问的另一个问题是,是否有更简单、更明显的方法来实现目标。例如,使用DI作为
path
变量的(最可能)目的是允许应用程序使用不同的
path
类。但简单的方法是将
Path
实例作为显式参数传递给
dijkstra
方法。您甚至可以使用重载使其更容易接受;e、 g

public Path dijkstra(Node startNode, Node endNode) {
    return dijkstra(startNode, endNode, new Path());
}

public Path dijkstra(Node startNode, Node endNode, Path path) {
    ...
}

最后要考虑的是,DI(爪哇)在某种程度上涉及反射,并且不可避免地比使用<代码>新< /代码>或工厂对象/方法的经典方法更昂贵。如果你不需要DI的额外灵活性,你不应该为此付费



我刚刚注意到你提到的两个变量是局部变量。我不知道有哪种DI框架允许您注入局部变量…

记住设计原则:“封装经常更改的内容”或“封装变化的内容”。作为工程师,你最清楚可能发生的变化。你不想把2012年硬编码为下一个十年的代码,但你也不想把“Math.PI”作为一个配置设置——这将是额外的开销和配置,你永远不需要接触

依赖注入不会改变这一原则

您是否正在编写一个算法,并且您知道您需要的是集合的哪一个实现,就像在您的Dijkstra示例中一样?自己创建对象。但是,如果您的同事很快就会为您的用例提供一个出色的Set优化新实现,或者如果您正在试验不同的集合实现以进行基准测试,该怎么办?也许你会想注入一个提供者,直到尘埃落定。这对于集合来说不太可能,但对于类似的一次性对象来说可能更可能,您可能会认为它们是“辅助对象”

假设您在不同类型的CreditCardAuthService之间进行选择,这些服务在运行时可能会有所不同。注射的好理由。但是,如果您已经签署了一份为期五年的合同,并且您知道您的代码很快就会被替换,那该怎么办呢?也许对一个服务进行硬编码更有意义。但是,您必须编写一些单元测试或集成测试,并且您真的不想使用真正的信用卡后端。回到依赖注入


记住:代码是可延展的。有一天,您可能会决定需要删除硬编码的哈希集,并用其他内容替换它,这很好。或者,您可能会发现,您需要您的服务经常变化,以使其受到Guice的控制,然后添加一个构造函数参数和一个绑定,并在一天内调用它。不要太担心。请记住,仅仅因为您有一个锤子,在使用DI时,并不是每个问题都是
提供者

,我宁愿尽可能避免使用“新的”。您在容器外部创建的每个实例(例如guice注入器)都不能重构为使用注入(如果您的“Path”实例应该通过配置注入字符串“root”……并且您也不能在这些对象上使用拦截器,该怎么办。 因此,除非它是纯实体Pojo,否则我一直使用提供/注入。
这也是控制反转(好莱坞)模式和可测试性的一部分……如果您需要模拟路径或在Junit中设置呢?如果您已经注入了它们(在本例中是提供者),您可以在之后轻松切换具体实现。

Google Guice允许我将一些提供程序对象传递给我的类,我可以使用这些对象轻松生成集合。但我不确定这是否是一种好方法。@荒谬的想法-传递提供程序并不是经典的DI(提供程序实际上是一个工厂对象)。此外,你需要问问自己,你到底实现了什么(价值)。好的观点,但这种方法会造成“建造商污染”。在这个简单的例子中,我的构造函数需要两个提供程序,我甚至没有将完整的算法或依赖项发布到其他类。此外,用户应该如何确定哪一个集合是算法的正确/最佳集合?如果使用构造函数注入,实例也将由guice创建,因此用户不会决定哪一个im但在这种情况下,我还是更喜欢字段注入。字段注入迫使我在测试时使用依赖项注入,这是我不想和/或不能使用的。是的,实例是由提供者创建的,但用户仍然必须选择(或有可能更改)正确/最佳的提供者。