我目前已经布置了以下类/接口。类型T表示从DataProvider实现返回的数据格式。我正在使用工厂,因此不需要将类型信息附加到MyStreamingOutput。我正在使用HK2DataProviderFactory注入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实例返回的类型。我非常想避免对queryrowMapper参数使用设置器,因为在这种情况下,我认为这是不好的设计。

我无法摆脱这样的感觉,我要么在泛型应用程序中缺少一些微妙的东西,要么在依赖项注入应用程序中缺少了一些明显的东西。解决此用例的正确方法是什么?看来这正是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中。

07-27 18:31