我希望能够注入我的外部依赖项(http 客户端
使用等)并为开发人员提供注入不同版本的方法
如果他们想要或者我曾经想要更改这些依赖项
自己实现(依赖注入的一个很好的例子吗?)。
这对于 DI 来说是一个很好的案例,这一点值得商榷。像 HTTP 客户端这样的外部依赖项通常具有具体的接口,除了该依赖项之外没有人实现。我个人无法想象你的程序是如何编写的,因为交换底层 HTTP 客户端不会影响它的架构,也就是说,除非你为它提供你自己的外观,像
public interface HttpClient {
HttpResponse send(HttpRequest request);
}
其中HttpRequest 和HttpResponse 也是自定义类/接口。但是向最终用户提供这种扩展点很少合适,特别是如果您没有一些参考实现(这意味着用户必须为他/她的依赖项创建这个外观想要)。这在极少数情况下是合适的,但很可能这不是你的情况。
相同依赖项的不同版本通常也不适用 DI,因为交换版本可以在构建/组装时完成。
如果您想向用户公开一种能力,以提供他们自己的一些库“移动部件”的实现,那么首先您必须为所有这些移动部件定义严格的接口。换句话说,提供一组interfaces,您的用户必须对其进行扩展并注入到您的类中。
然后您创建由 Guice 模块组成的“绑定空间”,并在这些模块中声明对这些接口的要求:
public class SomeModule extends AbstractModule {
@Override
protected void configure() {
requireBinding(SomeUserAPI.class);
// Other bindings which probably use SomeUserAPI in implementations
}
}
通过声明所需的绑定,您可以确保没有人能够混入您的模块,除非它们提供给定类的某种实现。当然,如果 Guice 找不到绑定,它无论如何都会失败,但是当您明确要求它时,您会获得更具体的错误消息,以及清晰的模块接口。
然后您为您的库创建特殊的“入口点”,其唯一职责是创建注入器并为用户提供您的类的实例。这个类接受来自用户的 Guice 模块并将其集成到注入器中。
public class Library {
private final Injector injector;
private Library(Module userModule) {
// SomeModule and AnotherModule are modules defined in the library
// and they are not the part of public interface of your library
this.injector = Guice.createInjector(userModule, new SomeModule(), new AnotherModule());
}
public static Library create(Module userModule) {
return new Library(userModule);
}
public MyAPIService myAPIService() {
return injector.getInstance(MyAPIService.class);
}
}
然后用户这样使用它:
Library library = Library.create(new AbstractModule() {
@Override
protected void configure() {
// recall requireBinding(SomeUserAPI.class) statement we wrote previously,
// here we are "implementing" it
bind(SomeUserAPI.class).to(SomeUserAPIImpl.class);
// other bindings for your exposed interfaces
}
});
MyAPIService service = library.myAPIService();
在这种方法中,您允许用户使用 Guice DI 以一种简洁且可控的方式扩展您的库。
但是,您仍然必须向用户公开 Guice(因为用户必须实现 Module 接口)。我不认为你可以完全避免这种情况,除非你做一些奇怪的事情,比如
Library.create(SomeUserAPIImpl.class, SomeUserAPI2Impl.class, ...)
也就是说,接受表示扩展点实现的类对象(然后将它们绑定到某个内部模块中)。但我认为从库界面中删除 Guice 并不值得。