java—如何为多接口继承启用类似生成器的模式?

n9vozmp4  于 2021-06-26  发布在  Java
关注(0)|答案(2)|浏览(324)

首先,让我们定义接口:

public interface Transformable<T> {
    <R extends Transformable<T>> R registerTransformation(Function<T,T> transformation);
    T process(T entity);
}
public interface HttpService<T> {
    T execute(URL url);
}

如界面所示 Transformable<T> ,我们可以创建一个简单的生成器模式来注册转换并在一行中执行所有转换,例如:

new DefaultTransformableImpl<Integer>(){}
    .registerTransformation(number -> number * 2)
    .registerTransformation(number -> number - 1)
    .registerTransformation(number -> number * number)
    .process(109845)

好了,现在我们准备创建 HttpService . 这样做的目的是提供修改标头、http方法。。。在实际将请求发送到服务器之前,不需要为连接示例指定方法适配器 org.jsoup.Connection 接口:

import java.util.function.Function;
import java.util.List;
import java.net.URL;
import org.jsoup.Connection;
import org.jsoup.helper.HttpConnection;
import org.jsoup.nodes.Document;
import lombok.SneakyThrows;

public class JsoupHttpService implements HttpService<Document>, Transformable<Connection> {
    private final List<Function<Connection, Connection>> fns = new LinkedList<>();

    @Override public <R extends Transformable<Connection>> R registerTransformation(Function<Connection, Connection> transformation) {
        this.fns.add(transformation);
        return (R) this;
    }

    @Override public Connection process(Connection connection) {
        return fns.stream().reduce(Function.identity(), Function::andThen).apply(connection);
    }

    @SneakyThrows
    @Override public Document execute(URL url) {
        return this.process(HttpConnection.connect(url)).execute().parse();
    }
}

这只是实际实现的简化,只关注问题的必要部分,这可以通过尝试使用 JsoupHttpService 使用方法链接:

public Document doSomething() {
    return new JsoupHttpService(){}
        .registerTransformation(connection -> connection.method(Connection.Method.GET))
        .registerTransformation(connection -> connection.userAgent("fake user-agent"))
        .execute("https://www.google.com")
        .parse();
}

如果您在ide中尝试这个方法,它会说一些接近于无法解析“transformable”中的方法“execute”。。。所以我想知道你们是否知道我如何在不修改两个接口的情况下解决这个问题。是我创建另一个接口的唯一选择 HttpService 并尝试调整中定义的方法 Transformable 在里面?

ss2ws0br

ss2ws0br1#

我建议几乎所有使用多重继承的方法,甚至是接口,都是一个坏主意。而且,将泛型和类型继承混合在一起会导致噩梦般的混乱——看看这些奇怪的结构 Comparable 以及 Enum .
显然这并不能阻止我修改代码。
如果方法的类型参数仅用于返回类型,则该方法的选项为:return null ,抛出异常,执行堆类型污染,不必费心返回或更糟。
对于构建器的这类问题,解决方法是使用递归定义的类型参数混乱地参数化构建器类型,并可能添加一个抽象的“get this”方法(如果 Transformable 是抽象的,所以可以有字段。

public interface Transformable<THIS extends Transformable<THIS, T>, T> {
    THIS registerTransformation(Function<T, T> transformation);
    T process(T entity);
}

public class JsoupHttpService implements HttpService<Document>, Transformable<Connection> {
    private final List<Function<Connection, Connection>> fns = new LinkedList<>();

    @Override public JsoupHttpService registerTransformation(Function<Connection, Connection> transformation) {
        this.fns.add(transformation);
        return this;
    }

为了“得到这个”假设 Transformable 有一个通用的默认实现方法

// Silly example.
    default THIS setSomethingWhichUsuallyHasNoEffect() {
        // In an abstract class, could assign fields here.
        return getThis(); // Cannot inline within source.
    }
    abstract THIS getThis(); // Would be protects in abstract class.

然后在 JsoupHttpService :

@Override public JsoupHttpService getThis() {
        return this;
    }
ffvjumwh

ffvjumwh2#

问题是 registerTransformation 返回一个 Transformable . 一 Transformable 不是一个 HttpService .
您可以通过以下方式实现您想要的:

JsoupHttpService service = new JsoupHttpService();
service.registerTransformation(connection -> connection.method(Connection.Method.GET));
service.registerTransformation(connection -> connection.userAgent("fake user-agent"));
service.execute();

似乎可转换接口的作者没有正确理解何时使用它是合适的 extends . 未经检查的演员 (R) 应该是个线索。

相关问题