【问题标题】:Is Guice's @ImplementedBy evil? Is it appropriate in some cases?Guice 的@ImplementedBy 是邪恶的吗?在某些情况下合适吗?
【发布时间】:2011-09-06 00:46:56
【问题描述】:

我听说过“@ImplementedBy 是邪恶的”的说法,理由是它破坏了 DI 概念并使接口了解其实现者。

这在某些情况下可能是正确的,但我经常发现它只会带来更简洁的代码(无需维护模块),而不会真正损害过程中的任何东西。

作为实用主义者而非纯粹主义者,您认为什么时候值得使用@ImplementedBy?

【问题讨论】:

  • 这肯定与 Google 的“不作恶”格言不相容吧?

标签: java guice


【解决方案1】:

我对@ImplementedBy 有同样的呃、恶心、恶心的感觉,但同时它非常有用。 Spring 必须扫描你给它的包列表中的所有类。在 Guice 中,您不必配置要扫描的包列表,@ImplementedBy 是关键(如果不使用 Binder 来绑定)。当它在第一个 Injector.getInstance 上进入您的对象层次结构并命中一个接口时,它然后使用 @ImplementedBy 来查找默认实现(只要 Binder 中没有任何内容覆盖该默认值)。

我们也使用@ImplementedBy。我们发现它使用起来非常好,有点糟糕,但它工作得很好,而且因为它是 DI,它并不真正依赖于实现,因为无论如何你都可以用新的绑定覆盖。

同时接口在 DI 框架中的使用通常越来越少。我们项目中的所有 DAO 接口都消失了,我们仍然可以为 DAO 交换模拟对象。 java 类是隐式接口,可以在不需要接口的情况下进行模拟。我们现在保留主要 api 的接口使用非常清晰,而不是与实现代码混淆。对于 DAO,我们不再需要这个了。

【讨论】:

  • 通过类路径是一次性的启动成本。我不喜欢@ImplementedBy 的地方在于它创建了循环依赖。我会喜欢@ImplementorFor
  • true 但是 ImplementedFor 在启动时会非常昂贵,因为您必须“找到” ImplementedFor 类,这意味着扫描每个类。 Hibernate 有这个问题,然后他们提出了一个讨厌的解决方法,只扫描带有 meta.xml 文件的 jar,因为它很昂贵......扫描所有 jar 是不理想的。我同意这个理论,但我认为实用性胜出。 @MihaiDanila
  • 但是在启动时扫描整个类路径一次是否不切实际?我认为这取决于应用程序,并且系统的启动成本通常很大。当然,这应该由图书馆的用户决定,图书馆为我们提供了使用该功能的选项。
  • 是的,我想如果你不做这样的事情,那很好docs.google.com/document/d/…我们对每台服务器都这样做,所以对于我们所做的任何服务器来说,让测试套件永远花费是不切实际的。我们的服务器启动时间很快,并且我们的测试套件保持这种状态(我们可以像无人业务一样进行重构,而无需重写测试)。所以你是对的。我来自另一个世界。
  • @MihaiDanila ^^
【解决方案2】:

您通常应该更喜欢显式绑定而不是即时 (JIT) 绑定。显式绑定允许注入器在注入器创建时抓取依赖图。如果依赖项丢失或无效,这允许 Guice 快速失败。使用像 @ImplementedBy 这样的即时绑定,Guice 在绑定执行之前无法报告问题。

JIT 绑定与 PrivateModules/子注入器的交互也很差。虽然大多数应用程序不需要这些功能,但如果每个绑定都属于特定模块,那么当您这样做时,痛苦会减轻。

【讨论】:

    【解决方案3】:

    当一个接口不打算有多个实现但必须成为依赖注入过程的一部分时,它很有用,因为它具有必须由框架注入的依赖项。

    【讨论】:

      【解决方案4】:

      我可以理解为什么 Google 会这样做,但有一个首选的实现并不一定是邪恶的。请注意,文档说它是针对 default 实现的,而不是 only 一个。

      顺便说一句,我发现这个问题是因为我在互联网上搜索@ImplementedBy 概念的现有实现。

      我创建了一个名为 @ImplementedBy 的注解来放置在我的一个接口上。当使用纯的、未注入的反射时,这是告诉接口使用什么实现的最简单方法,尤其是在使用仅理解接口的现有 API 时。 (接口,不是实现)

      注释允许我用一行注释和装饰器内的一行来泛化一些非常粗糙的生成器。这么简单的操作我不用依赖框架。

      【讨论】:

        【解决方案5】:

        对我来说,是的,如果您将其用于硬线绑定并且永远不会重用绑定,因为您颠倒了接口的含义,这是邪恶的。我同意@thSoft,这是一个很常见的模式,但我什至不清楚,为什么我们没有@Implements 注释。 此外,给定的默认实现不是运行时使用的也可能令人恼火。

        只是为了弄清楚google sayed to that是什么

        Annotate types tell the injector what their default implementation type is. …
        

        !!它还消除了诸如

        之类的通用接口
        @ImplementedBy(MyImpl.class)
        public interface MyInterface<SIn,SOut> {}
        
        public class MyImpl implements MyInterface<String, Integer> {}
        

        通常不会只实施一次。 详情请见Inject Generic Implementation using Guice

        【讨论】:

        • 之所以没有@Implements是因为它没有意义: 1.可能有多个类用@Implements(MyInterface.class)注解,然后你就迷路了。 2. 更糟糕的是,为了找到实现,您必须先加载所有类。 +++ 关于泛型,我猜,@ImplementedBy 只是一个简单用法的捷径,所以它已经足够好了。 AFAIK,没有办法使用 TypeToken 作为注释参数。
        • 可能有多个实现者,但正确使用它仍然很有用。可以将其命名为@DefaultImplementor,以便更果断地决定是否可以使用它。 @ImplementedBy 在后台有构建系统时创建循环依赖。
        猜你喜欢
        • 2011-01-02
        • 1970-01-01
        • 2011-12-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-18
        • 1970-01-01
        • 2010-11-26
        相关资源
        最近更新 更多