Guice中@ImplementedBy批注背后的动机是什么?


10

我最近阅读了有关Google Guice中@ImplementedBy可用的注释的信息。它允许程序员指定接口与其实现之间的绑定,以供将来在依赖关系注入中使用。这是即时绑定的示例。

我已经习惯于使用以下语法在模块中定义显式绑定:

bind(SomeInterface.class).to(SomeInterfaceImplementation.class);

根据文档,这等效于@ImplementedBy注释的以下用法:

@ImplementedBy(SomeInterfaceImplementation.class)
public interface SomeInterface {
    //method declarations
}

我在这里看到的唯一好处是代码短了一点。同时,这种方法也有一个缺点,由相同的文档正确指出:

@ImplementedBy小心使用;它从接口到其实现添加了编译时依赖性。

在许多情况下,这种依赖关系可能不是问题,但我个人认为这是代码的味道。

什么用例使@ImplementedBy注释值得使用?

一种可能的方法似乎是在库或框架的代码中使用它。如文档中所述,注释可以提供默认绑定,很容易被显式绑定覆盖。

如果一个类型同时在一个bind()语句(作为第一个参数)中并且具有@ImplementedBy注释,bind()则使用该语句。批注建议可使用绑定覆盖的默认实现

这样,作为库的开发人员,我可以为用户提供现成的绑定,可以在客户端代码中的某个位置进行自定义。

这是注释存在的唯一原因吗?还是我想念的东西?我可以通过在只使用某些业务逻辑的应用程序而不是要扩展的库/框架的代码中使用它来获得任何收益吗?


2
相关的,可能重复的问题(尽管标题更清楚):Guice的@ImplementedBy邪恶吗?
杰夫·鲍曼

不是严格的重复,但是这里有一些有趣的讨论:stackoverflow.com/questions/6197178/…–
理查德·沃登

Answers:


8

我认为这里的危险是使用@ImplementedBy注释。适当地使用,与bind()模块中的语句等结合使用,就可以了。

拥有默认实现非常适合测试。您不必在每次测试具有大量依赖关系的类时,或者如果您有一个依赖于许多事物的类时,都不必显式定义模拟注入(因此,您必须每次都定义一个模拟)。

例如,您可能有一个类:

@ImplementedBy(NoOpDataService.class)
interface DataService {
    Map<String, MyPOJO> getData();
}

然后NoOpDataService是:

class NoOpDataService implements DataService {
    @Override
    public Map<String, MyPOJO> getData() {
        return Collections.emptyMap();
    }
}

显然,您永远不会在实际代码中使用它;在您的Guice模块中,您将绑定实际上执行某项操作的实现。但是对注入的类的所有测试DataService都不再需要模拟绑定。

tl;博士,我同意您的意见,即让您的接口依赖于您的实现可能是一种代码味道。但它也可以删除样板代码,从而使测试更加容易。实现这一功能并不难;尽管滥用的可能性很小,但最终的后果不会太糟(服务意外启动),即使确实发生,也不会很难解决。


3
将测试代码添加到生产中?
Basilevs 2015年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.