28 votes

Modèle Java pour les rappels imbriqués?

Je suis à la recherche d'un Java motif pour rendre une séquence imbriquée de non-blocage des appels de méthode. Dans mon cas, certains code client doit asynchrone invoquer un service pour effectuer certains cas d'utilisation, et chaque étape de ce cas d'utilisation se doit d'être exécutée de manière asynchrone (pour des raisons en dehors de la portée de cette question). Imaginez, j'ai des interfaces existantes comme suit:

public interface Request {} 

public interface Response {} 

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

Il existe différents jumelé implémentations de l' Request et Response interfaces, à savoir l' RequestA + ResponseA (fourni par le client), RequestB + ResponseB (utilisé en interne par le service), etc.

Le flux de traitement ressemble à ceci:

Sequence diagram showing nested callbacks.

Entre la réception de chaque réponse et l'envoi de la demande suivante, un traitement supplémentaire qui doit arriver (par exemple, basé sur des valeurs dans l'une quelconque des précédentes requêtes ou réponses).

Jusqu'à présent, j'ai essayé deux approches de codage en Java:

  • anonyme classes: devient laid rapidement en raison de la nécessaire imbrication
  • les classes internes: plus soignée que la précédente, mais encore difficile pour un développeur de comprendre le flux d'exécution

Est-il un motif pour rendre ce code plus lisible? Par exemple, pourrais-je exprimer la méthode de service comme une liste d'opérations unitaires qui sont exécutés dans l'ordre par certains cadres de la classe qui prend soin de l'imbrication?

6voto

Daniel Pryden Points 22167

À mon avis, le moyen le plus naturel pour modéliser ce type de problème est avec Future<V>.

Ainsi, au lieu d'utiliser un rappel, il suffit de retourner un "thunk": un Future<Response> qui représente la réponse qui sera disponible à un certain moment dans l'avenir.

Vous pouvez ensuite soit le modèle étapes ultérieures que des choses comme Future<ResponseB> step2(Future<ResponseA>), ou utiliser ListenableFuture<V> de la Goyave. Ensuite, vous pouvez utiliser Futures.transform() ou l'un de ses surcharges de la chaîne de vos fonctions de façon naturelle, mais tout en préservant la nature asynchrone.

Si elle est utilisée de cette façon, Future<V> se comporte comme une monade (en fait, je pense qu'elle peut être considérée comme un seul, bien que je ne suis pas sûr que sur le dessus de ma tête), et donc l'ensemble du processus se sent un peu comme IO en Haskell comme effectuées à l'aide du IO monade.

6voto

Rob I Points 2750

Depuis la mise en œuvre (pas seulement l'interface) ne doit pas bloquer, j'aime votre liste d'idée.

Mettre en place une liste des "opérations" (peut - Futures?), pour lequel le programme d'installation devrait être assez clair et lisible. Ensuite, lors de la réception de chaque réponse, l'opération suivante doit être invoquée.

Avec un peu d'imagination, cela ressemble à la chaîne de responsabilité. Voici un pseudo-code pour ce que j'imagine:

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

2voto

Alexei Kaigorodov Points 5841

Vous pouvez utiliser acteur modèle de l'informatique. Dans votre cas, le client, les services et les rappels [B-D] tout peut être représenté en tant qu'acteurs.

Il existe de nombreuses acteur bibliothèques java. La plupart d'entre eux, cependant, sont lourdes, j'ai donc écrit un compact et extensible une: df4j. Il estime acteur modèle comme un cas particulier de la plus générale des flux de données, un modèle de calcul et, par conséquent, permet à l'utilisateur de créer de nouveaux types d'acteurs, à adapter de manière optimale les exigences de l'utilisateur.

1voto

ejb_guy Points 1094

Je ne sais pas si je vous pose la question correctement. Si vous souhaitez appeler un service et que son achèvement doit être transmis à un autre objet qui peut continuer le traitement à l'aide de result. Vous pouvez envisager d'utiliser Composite et Observer pour y parvenir.

0voto

Andrew Swan Points 5118

Finagle par Twitter semble pouvoir faire ce dont j'ai besoin. Les commentaires de tous ceux qui l'ont utilisé dans la vraie vie sont les bienvenus.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X