web-dev-qa-db-fra.com

Comment remplacer 'if statement' par rx-Java pour éviter l'enfer de rappel?

J'essaie de remplacer mon code avec rx-Java. (C'est un très petit code.)

C'est fini et ça marche.

Mais je veux savoir...

  1. Est-ce un bon style Rx?
  2. Si ce n'est pas bon, veuillez indiquer le mauvais point

Ci-dessous est mon code qui est api manipulation.

avant

Random r = new Random();
boolean apiResult = r.nextBoolean(); // it represents api result. ex. {"result": true} or {"result": false}

if (apiResult == true) {
    // do something

    System.out.println("result:" + "success");
} else {
    // do something

    System.out.println("result:" + "failure");
}

après

Random r = new Random();
Observable<Boolean> apiResultStream = Observable.create(new OnSubscribe<Boolean>() {
    @Override
    public void call(Subscriber<? super Boolean> subscriber) {
        // emit true or false
         subscriber.onNext(r.nextBoolean());
    }
}).cache(1);


// I used filter for split. Is it Rx style?
// success if true emitted.
Observable<Boolean> successStream = apiResultStream
        .filter(aBoolean -> aBoolean == true); // here

// failure if false emitted.
Observable<Boolean> failureStream = apiResultStream
        .filter(aBoolean -> aBoolean == false); // here


// success flow
successStream
        .flatMap(aBoolean -> Observable.just("success"))
        // and do something
        .subscribe(aString -> System.out.println("result:" + aString));

// failure flow
failureStream
        .flatMap(aBoolean -> Observable.just("failure"))
        // and do something.
        // I want to keep subscriber.
        .subscribe(aString -> System.out.println("result:" + aString));

MODIFIER 

J'ai presque remplacé. merci pour bon commentaire.
(mais j'ai quelques codes non remplacés. Ils ont beaucoup de callback et if statement.)

Je veux éviter «l'enfer de rappel».

La clé est un type de résultat différent entre 'callSuccessApi' et 'callFailureApi'

avant rx

// callback hell!
callApi(new Callback<Result>(){
    @Override
    public void success(Result result) {
        if (result.Response == true) {
            callSuccessApi(new Callback<ResultSuccess>(){
                @Override
                public void success(ResultSuccess result) {
                    // and more callbacks...
                }
            }
        } else { // result.Response == false
            callFailureApi(new Callback<ResultFailure>(){
                @Override
                public void success(ResultFailure result) {
                    // and more callbacks...
                }
            }
        }
    }
}

apres avec rx (eviter le callback! Est-ce un bon style de Rx?)

// change 1st api to observable.(I changed other api to observable)
Observable<Result> apiResultStream = Observable.create(new OnSubscribe<Boolean>() {
    @Override
    public void call(Subscriber<? super Boolean> subscriber) {
        callApi(new Callback<Result>(){
            @Override
            public void success(Result result) {
                subscriber.onNext(result);
            }
        });
    }
}).cache(1); // ensure same Observable<Result> for success and failure.


// I used filter for split. Is it Rx style?
// success if result.response == true.
Observable<ResultSuccess> successStream = apiResultStream
        .filter(result -> result.response == true); // here

// failure if result.response == false.
Observable<ResultFailure> failureStream = apiResultStream
        .filter(result -> result.response == false); // here


// success flow. callSuccessApi return Observable<ResultSuccess>
successStream
        .flatMap(result -> callSuccessApi(result))
        // and more api call with flatMap...
        .subscribe(resultSuccessN -> System.out.println("result:" + resultSuccessN.toString()));

// failure flow. callFailureApi return Observable<ResultFailure>
failureStream
.flatMap(resultFailure -> callFailureApi(result))
        // and more api call with flatMap...
        .subscribe(resultFailureN -> System.out.println("result:" + resultFailureN.toString()));

désolé pour mon pauvre anglais et longue question.

Mise à jour de mon code

J'ai eu 2 informations importantes dans cette question. (Merci @ Tomáš Dvořák, @Will

  1. que ce soit un bon chemin à suivre dépend de la situation. 
  2. Il n'y a rien de mal à utiliser une instruction if dans une carte/une flatmap/subscribe.

code mis à jour

Observable<Result> apiResultStream = Observable.create(new OnSubscribe<Boolean>() {
        @Override
        public void call(Subscriber<? super Boolean> subscriber) {
            callApi(new Callback<Result>() {
                @Override
                public void success(Result result) {
                    subscriber.onNext(result);
                }
            });
        }
    });

    // In this case,   I used 'if' for simply and cleanly.
    apiResultStream
            .subscribe(result -> {
                if (result.response == true) {
                    callSuccessApi(); // this line looks like 'callback'. but I used this for simply and cleanly.
                } else {
                    callFailureApi();
                }
            });
17
kyanro

Il existe de nombreuses façons de le faire et cela dépend vraiment de votre cas d'utilisation. En général, je ne voudrais pas diviser en 2 flux, cela rendrait votre code moins lisible. De plus, je ne suis pas sûr des avantages de l'appel flatMap. Il n'y a rien de mal à faire si des éléments dans un appel de carte. 

Voici quelques options:

1 - Pour ajouter la journalisation (un peu comme vos lignes d'impression), j'utilise doOnEach()

apiResultStream
  .doOnEach(next -> {
    if (next) logger.info("Logging true " + next);
    else  logger.info(Logging false " + next);
  })
  .subscribe(....

2 - Le travail que vous effectuez fait partie de votre flux, et vous allez vouloir travailler plus tard sur le flux - utilisez map

apiResultStream
  .map(next -> {
        if (next) doSomeCallWithNextWhenTrue(next);
        else doSomeCallwithNextWhenFalse(next);
      })
  .subscribe(...

3 - S'il s'agit d'un travail que vous souhaitez effectuer à la fin du pipeline - IE après que tous les flux de transformation ou autres, comme celui-ci est terminé, effectuez l'opération dans l'appel d'abonnement.

apiResultStream
  .subscribe(next -> {
            if (next) doSomeCallWithNextWhenTrue(next);
            else doSomeCallwithNextWhenFalse(next);
          });

Le problème est qu’avec un cas d’utilisation aussi simple, il est difficile de suggérer la meilleure option, mais j’apprécie qu’en apprenant à Rx, déterminer comment faire des déclarations conditionnelles peut paraître déroutant. En général, j'utilise simplement map ou flatMap lorsque j'appelle une autre méthode qui retourne une Observable et effectue ma logique. 

Mettre à jour

Vous ne savez toujours pas pourquoi vous séparez vos flux. À moins que vous ne deveniez intelligent avec différents threads, le premier appel d'abonnement bloquera le second, ce qui n'est probablement pas ce que vous voulez. En outre, si vous n'appelez pas abonnement plus d'une fois, vous n'avez pas besoin de l'appel cache().

Il n'y a rien de mal à utiliser un if statement dans une map/flatmap/subscribe. Surtout si cela rend votre code plus lisible.

Je voudrais faire ce qui suit:

apiResultStream
  .flatMap(result -> {
    if (result.response == true) {
      return callSuccessApi(result)
    }
    else {
      return callFailureApi(result)
  })
  //Do any more calls you need
  .subscribe(...

Tellement plus propre. 

Je suis un peu confus par vos appels System.out.println dans les abonnements. Est-ce là à des fins de débogage ou de journalisation? Si c'est le cas, faites simplement cela dans la flatMap ci-dessus dans l'instruction if. 

J'espère que cela t'aides, 

Volonté

13
Will

Pour éviter le if/else et ne pas rompre la chaîne ™, j'aime utiliser la méthode publier et fusionner pour scinder et fusionner de nouveau le flux:

apiResultStream
  .publish(results -> 
    Observable.merge(
        results.filter(result -> result.response == true)
               .flatmap(result -> callSuccessApiObservable()),
        results.filter(result -> result.response == false)
               .flatmap(result -> callFailureApiObservable())
    )
  )
  .subscribe(...
1
Till