我目前已经安排了以下类/接口。类型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
参数使用setter,因为我认为在这种情况下设计不好。
我无法摆脱这种感觉,即我在遗传学的应用中缺少一些微妙的东西,或者在我的依赖注入应用中显而易见的东西。解决此用例的正确方法是什么?看来这是DI要解决的问题,但我看不出如何解决它。
答案 0 :(得分:1)
当使用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。
答案 1 :(得分:1)
不幸的是,由于类型擦除,我不可能做我想要的。我将不得不考虑重构现有代码。