嵌套回调的 Java 模式?

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

public interface Request {} 

public interface Response {} 

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

和 接口有各种配对的实现,即 + (由客户端给出)、+ (由服务内部使用)等。RequestResponseRequestAResponseARequestBResponseB

处理流程如下所示:

Sequence diagram showing nested callbacks.

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

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

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

是否有某种模式可以使此代码更具可读性?例如,我是否可以将服务方法表示为由某个负责嵌套的框架类按顺序执行的独立操作的列表?


答案 1

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

设置一个“操作”列表(也许是s?),设置应该非常清晰和可读。然后在收到每个响应后,应调用下一个操作。Future

只要有一点想象力,这听起来像是责任链。以下是我想象的一些伪代码:

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();
    }
}
...

答案 2

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

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

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

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


推荐