使用guice进行客户端sdk /库设计模式的依赖注入

jon*_*lee 6 java design-patterns dependency-injection guice

我正在为Web API构建客户端SDK并尝试通过guice应用依赖注入.第三方将使用此Java客户端作为访问API的方式.

我希望能够注入我的外部依赖项(使用的HTTP客户端等),并为开发人员提供一种方法,如果他们想要或者我自己想要更改实现,则可以注入不同版本的这些依赖项(依赖项的一个很好的例子)注射对吗?).

但是为了连接依赖项,我必须让我的库的用户创建一个注入器等,如下所示:

Injector injector = Guice.createInjector(new MyAPIClientModule(url, username, password));
        this.service = injector.getInstance(MyAPIService.class);
Run Code Online (Sandbox Code Playgroud)

我不想把它推到我的库的用户,但我仍然希望让用户能够选择不同的实现或底层HTTP库等.

我在某种程度上错过了guice或DI的观点吗?这是使用guice时的标准做法吗?

或者我应该将它包装在另一个执行注入的类中,并向第三方用户提供一个示例Java对象?

Vla*_*eev 8

我希望能够注入我的外部依赖项(使用的http客户端等),并为开发人员提供一种方法,如果他们想要或者如果我想自己更改实现,则可以注入这些依赖项的不同版本(依赖项的一个好例子)注射对吗?).

对于DI而言,这是一个很好的理由.像HTTP客户端这样的外部依赖关系通常具有具体的接口,除了完全依赖之外,没有人实现.我个人无法想象你的程序是如何编写的,因为交换底层HTTP客户端不会影响它的架构,也就是说,除非你为它提供自己的外观,比如

public interface HttpClient {
    HttpResponse send(HttpRequest request);
}
Run Code Online (Sandbox Code Playgroud)

where HttpRequestHttpResponse也是自定义类/接口.但是向最终用户提供这种扩展点很少是合适的,特别是如果你没有一些参考实现(这意味着用户必须为他/她想要的依赖创建这个外观).在极少数情况下这合适的,但可能这不是你的情况.

对于DI,通常也不是相同依赖的不同版本,因为交换版本可以在构建/组装时完成.


如果您希望为用户提供一些能力来提供他们自己的库"移动部件"的实现,那么首先必须为所有这些移动部件定义严格的接口.换句话说,提供一组interface用户必须扩展并在类中注入的s.

然后创建由Guice模块组成的"绑定空间",在这些模块中,您可以声明这些接口的要求:

public class SomeModule extends AbstractModule {
    @Override
    protected void configure() {
        requireBinding(SomeUserAPI.class);
        // Other bindings which probably use SomeUserAPI in implementations
    }
}
Run Code Online (Sandbox Code Playgroud)

通过声明所需的绑定,您确保没有人能够在您的模块中混合,除非它们提供给定类的某些实现.当然,如果Guice无法找到绑定,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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后用户像这样使用它:

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();
Run Code Online (Sandbox Code Playgroud)

在这种方法中,您允许用户以整洁和受控的方式使用Guice DI扩展您的库.

但是,您仍然需要向用户公开Guice(因为用户必须实现Module接口).除非你做一些奇怪的事情,否则我认为你不能完全避免这种情况

Library.create(SomeUserAPIImpl.class, SomeUserAPI2Impl.class, ...)
Run Code Online (Sandbox Code Playgroud)

也就是说,接受表示扩展点实现的类对象(然后在一些内部模块中绑定它们).但我不认为从库界面中消除Guice真的很值得.