43

我正在寻找一种 Java 模式来进行嵌套的非阻塞方法调用序列。就我而言,某些客户端代码需要异步调用服务来执行某些用例,并且该用例的每个步骤本身都必须异步执行(出于此问题范围之外的原因)。想象一下,我有如下现有接口:

public interface Request {} 

public interface Response {} 

public interface Callback<R extends Response> {
    void onSuccess(R response);
    void onError(Exception e);
}

Request和接口有多种成对的实现Response,即RequestA+ ResponseA(由客户端提供)、RequestB+ ResponseB(由服务内部使用)等。

处理流程如下所示:

显示嵌套回调的序列图。

在收到每个响应和发送下一个请求之间,需要进行一些额外的处理(例如,基于任何先前请求或响应中的值)。

到目前为止,我已经尝试了两种用 Java 编写代码的方法:

  • 匿名类:由于需要嵌套而很快变得丑陋
  • 内部类:比上面的更整洁,但其他开发人员仍然难以理解执行流程

是否有一些模式可以使这段代码更具可读性?例如,我可以将服务方法表示为一个自包含操作的列表,这些操作由一些负责嵌套的框架类按顺序执行吗?

4

4 回答 4

13

由于实现(不仅是接口)不能阻塞,我喜欢你的列表想法。

设置一个“操作”列表(可能是Futures?),其设置应该非常清晰易读。然后在收到每个响应后,应该调用下一个操作。

稍微想象一下,这听起来像是责任链。这是我想象的一些伪代码:

public void setup() {
    this.operations.add(new Operation(new RequestA(), new CallbackA()));
    this.operations.add(new Operation(new RequestB(), new CallbackB()));
    this.operations.add(new Operation(new RequestC(), new CallbackC()));
    this.operations.add(new Operation(new RequestD(), new CallbackD()));
    startNextOperation();
}
private void startNextOperation() {
    if ( this.operations.isEmpty() ) { reportAllOperationsComplete(); }
    Operation op = this.operations.remove(0);
    op.request.go( op.callback );
}
private class CallbackA implements Callback<Boolean> {
    public void onSuccess(Boolean response) {
        // store response? etc?
        startNextOperation();
    }
}
...
于 2012-05-24T05:56:37.420 回答
7

在我看来,对这类问题建模最自然的方法是使用Future<V>.

因此,不要使用回调,而是返回一个“thunk”:a Future<Response>,它表示将来某个时候可用的响应。

然后,您可以将后续步骤建模为类似的东西Future<ResponseB> step2(Future<ResponseA>),或者使用ListenableFuture<V>Guava。然后,您可以使用Futures.transform()它的重载之一以自然的方式链接您的函数,但同时仍保留异步性质。

如果以这种方式使用,Future<V>其行为就像一个 monad(事实上,我认为它可能有资格作为一个,虽然我不确定我的头顶),所以整个过程感觉有点像 Haskell 中执行的 IO通过 IO 单子。

于 2012-05-22T06:56:15.190 回答
2

您可以使用参与者计算模型。在您的情况下,客户端、服务和回调 [BD] 都可以表示为参与者。

java有很多actor库。然而,它们中的大多数都是重量级的,所以我写了一个紧凑且可扩展的:df4j。它将actor模型视为更通用的数据流计算模型的一个特定案例,因此允许用户创建新类型的actor,以最佳地满足用户的需求。

于 2012-05-22T05:00:03.467 回答
1

我不确定我是否正确地回答了你的问题。如果你想调用一个服务并且它的完成结果需要传递给其他可以使用结果继续处理的对象。您可以考虑使用 Composite 和 Observer 来实现这一点。

于 2012-05-22T07:32:32.207 回答