Java 我怎么能';提供';匕首中的虚法

Java 我怎么能';提供';匕首中的虚法,java,dependency-injection,dagger-2,Java,Dependency Injection,Dagger 2,我正在尝试从Spring迁移一个项目,并开始使用Dagger2,方法是遵循一个示例GitHub repo 在Spring中,我可以在类级别使用注释来公开类的所有方法,包括void方法。我想在Dagger 2中这样做,即,我想“提供”一个void方法 在下面的代码示例中,我可能应该将printRandomUUID方法移动到Printer界面。然而,我做这个小练习的目的是迁移一个经典的Spring@组件或@服务 正确的方法是什么?是否可以在组件或模块级别提供void方法 public class M

我正在尝试从Spring迁移一个项目,并开始使用Dagger2,方法是遵循一个示例GitHub repo

在Spring中,我可以在类级别使用注释来公开类的所有方法,包括
void
方法。我想在Dagger 2中这样做,即,我想“提供”一个
void
方法

在下面的代码示例中,我可能应该将
printRandomUUID
方法移动到
Printer
界面。然而,我做这个小练习的目的是迁移一个经典的Spring
@组件
@服务

正确的方法是什么?是否可以在组件或模块级别提供void方法

public class Main {

 interface Printer {

    void printMsg(String msg);
 }

 static class ConsolePrinter implements Printer {

    @Override
    public void printMsg(String msg) {
        System.out.println(msg);
    }
 }

 @Singleton
 @Component(modules = ConsoleModule.class)
 interface HelloWorldApp {

    Printer getPrinter();

    //this doesn't compile -> java.lang.IllegalArgumentException: not a valid component method:
    void printRandomUUID();
 }

 @Module
 static class ConsoleModule {

    @Provides
    Printer providePrinter() {
        return new ConsolePrinter();
    }

    //this doesn't compile -> @Provides methods must return a value (not void)
    @Provides
    void printRandomUUID() {
        System.out.println(UUID.randomUUID().toString());
    }

 }

 public static void main(String[] args) {
    HelloWorldApp app = DaggerMain_HelloWorldApp.create();
    app.getPrinter().printMsg("Hello");
    System.out.println("-");
    //app.printRandomUUID();//here i want a void method to be consumed, from the component.
 }
}
这是不可能的。与Spring不同,Dagger仅通过检查组件接口和模块进行配置。这意味着Dagger
@Component
方法必须这样做,目前无法提供任意代码或委托给其他实例的方法

这并不是说组件不能有void方法:它们可以,但它们必须是在外部创建的实例中注入带注释的方法和字段的单参数方法。它们被称为,它们也可以返回它们接受的类型,以便于链接,而不是
void

从另一个角度来看,我认为将任意业务逻辑与Dagger创建的组件相结合是一个坏主意,原因是简单和正确:

  • 这样做可能违反SRP或关注点分离:依赖项注入的一个已知优势是将对象创建逻辑与其他业务逻辑分离。允许在对象创建组件上添加业务方法应该和在业务组件中使用
    new
    一样不合适。(是否每一个对象都应该通过DI图提供是另一天有争议的话题。)
  • 如果您坚持最佳实践,避免在构造函数/工厂/提供者中产生副作用和其他“繁重的工作”,那么您应该能够清楚地说明组件方法中可以和不可以发生什么。允许在组件上使用任意方法——特别是无效方法——与这种做法背道而驰
  • 如果您的应用程序使用单独的粒度库而不是单一的编译步骤,那么从其自身的对象图中使用组件可能会导致在不引入依赖循环的情况下很难构建。当然,Dagger允许在其自己的图形中注入组件,但这样做可能会导致以后出现循环问题
  • 类似的用例很容易用现有的结构来表示——如Louis Wasserman所评论的那样,通过图形使Runnable可用,或者注入一个类似的单一用途对象来保存该方法——因此将任意实现与组件隔离似乎不会导致功能或可读性的大损失。在最坏的情况下,您需要一个额外的方法调用来访问您定义的类

如果我像您一样进行迁移,我会在HelloWorldApp旁边创建一个名为HelloWorldMethods的类,并将我在HelloWorldApp上放置的所有方法转移到该类上。如果这是Spring迁移中的常见模式,您甚至可以为其定义一个本地约定(例如,FooComponent附带FooMethods或FooUtil)。最后,如果您想隐藏Dagger实现细节(如在外部API中),您还可以编写自己的类来包装和使用您的组件,将重要方法委托给内部组件,并提供您需要的任意实现。

这似乎是一件非常奇怪的事情。你能提供一个
Runnable
回调吗?FooMethods aproach就是我想要的。当我使用Dagger进行小步操作时,我意识到“思维方式”应该有所不同,我的意思是,带有spring的经典服务层不能直接转换为DI的Dagger方式。@没错,Dagger组件是提供服务或实例化服务的一种很好的方式,但它们本身并不是很好的服务。从好的方面来看,您可以更清楚地控制服务接口和实现的外观,这必然与组件接口分离。