2

我目前已经布置了以下类/接口。类型T表示从DataProvider实现返回的数据格式。我正在使用工厂,所以我不需要将类型信息附加到MyStreamingOutput. 我HK2用来注入DataProviderFactoryMyStreamingOutput.

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实例返回的类型。我非常想避免对queryandrowMapper参数使用设置器,因为在这种情况下我认为这是不好的设计。

我无法摆脱这样一种感觉,即我在泛型应用程序中遗漏了一些微妙的东西,或者在我的依赖注入应用程序中遗漏了一些明显的东西。解决此用例的正确方法是什么?这似乎是 DI 旨在解决的问题,但我不知道如何解决它。

4

2 回答 2

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。

于 2014-04-28T18:53:38.293 回答
1

不幸的是,由于类型擦除,不可能做我想做的事。我将不得不考虑重构现有代码。

于 2014-04-30T16:36:25.423 回答