我目前已经布置了以下类/接口。类型T
表示从DataProvider
实现返回的数据格式。我正在使用工厂,因此不需要将类型信息附加到MyStreamingOutput
。我正在使用HK2
将DataProviderFactory
注入MyStreamingOutput
。
public interface DataProvider<T> {
public T next() { ... }
...
}
public final class SQLDataProvider<T> {
public SQLDataProvider(final String query, final RowMapper<T> rowMapper) { ... }
}
public interface DataProviderFactory {
public <T> DataProvider<T> getDataProvider(final String query, final RowMapper<T> rowMapper);
...
}
public final class SQLDataProviderFactory {
public <T> DataProvider<T> getDataProvider(final String query, final RowMapper<T> rowMapper) {
return new SQLDataProvider<T>(query, rowMapper);
}
}
public final class MyStreamingOutput implements StreamingOutput {
public MyStreamingOutput(final DataProviderFactory dpFactory) { ... }
@Override public void write(final OutputStream outputStream) throws IOException { ... }
}
这一切都很好。现在,我正在尝试为
MyStreamingOutput
设置单元测试,但遇到了一些障碍。我编写了以下用于测试目的的其他类:public final class DataProviderFactoryStub implements DataProviderFactory {
private final DataProvider dataProvider;
public DataProviderFactoryStub() {
this.dataProvider = new DataProviderStub();
}
public DataProviderFactoryStub(final DataProvider dataProvider) {
this.dataProvider = dataProvider;
}
@Override
public <T> DataProvider<T> getDataProvider(final String query, final RowMapper<T> rowMapper) {
return this.dataProvider;
}
}
绑定发生在
final class QueryTestResourceConfig extends ResourceConfig {
public QueryTestResourceConfig() {
...
this.register(new AbstractBinder() {
@Override
protected void configure() {
bind(DataProviderFactoryStub.class).to(DataProviderFactory.class);
}
});
}
}
我可以成功地将此类注入
MyStreamingOutput
,但它会发出编译器警告,因为getDataProvider()
所使用的类型信息不会被传入工厂的实例共享。我不能将类型信息添加到DataProviderFactoryStub
类中,因为那样它将不再实现DataProviderFactory
接口。我不想在接口上输入类型信息,因为它是错误的-在Stub案例之外,工厂不应该关心DataProvider
实例返回的类型。我非常想避免对query
和rowMapper
参数使用设置器,因为在这种情况下,我认为这是不好的设计。我无法摆脱这样的感觉,我要么在泛型应用程序中缺少一些微妙的东西,要么在依赖项注入应用程序中缺少了一些明显的东西。解决此用例的正确方法是什么?看来这正是DI所要解决的问题,但我看不出如何解决。
最佳答案
使用DI时,我们通常会得到非常基础的工厂类(即,它们的创建方法通常足够简单以适合一行)。您的SQLDataProviderFactory
类是一个很好的例子。
这样做的原因是因为工厂对象只是用于创建对象的占位符。我们希望避免用new
关键字乱码,因为这样做会将代码紧密地耦合到特定类型。因此,我们最终得到了工厂,这些工厂的方法基本上只是美化的new
关键字。
我要指出的是,这里重要的是产品的类型。工厂只是一个管道。当您用测试倍数替换工厂时,您真正要做的就是用测试倍数替换产品。这意味着,每当我定义一个双重测试工厂时,我总是也必须定义一个双重测试产品。
例如,您的存根工厂只是试图返回存根产品。问题在于它返回的存根产品的类型与调用代码所期望的类型不匹配。如果您定义自己的存根产品,则代码会正确放置:
public final class DataProviderStub<T> implements DataProvider<T> {
private final T dummy;
public DataProviderStub() { }
public T next() { return this.dummy; } // Just for example
}
public final class DataProviderFactoryStub implements DataProviderFactory {
public DataProviderFactoryStub() { }
@Override
public <T> DataProvider<T> getDataProvider(final String query, final RowMapper<T> rowMapper) {
return new DataProviderStub<T>();
}
}
存根工厂仅存在,因此您可以将存根
DataProvider
注入到SUT中。