2015-02-03 10 views
16

Próbuję zastąpić mój kod przez rx-java. (Jest to bardzo mały kod.)Jak zamienić "if statement" na rx-java, aby uniknąć piekła wywołania zwrotnego?

Jest gotowy i działa.

Ale chcę wiedzieć ...

  1. Czy to dobry styl Rx?
  2. Jeśli nie dobry, proszę wskazać zły punkt

Poniżej jest mój kod, który jest obsługa api.

przed

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"); 
} 

po

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)); 

EDIT

prawie wymienić. thanks for good comment.
(ale mam kilka niezmienionych kodów, ma wiele callback i jeśli instrukcja.)

Chcę uniknąć "piekła oddzwonienia".

Kluczem jest inny typ wynik między 'callSuccessApi' i 'callFailureApi'

przed 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... 
       } 
      } 
     } 
    } 
} 

po Rx (unikać oddzwonienia piekła! Czy to dobry styl 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())); 

Przepraszam za moje słabe angielskie i długie pytanie.

Updated mój kod

mam 2 ważne informacje w tej sprawie. (Dziękuję @ Tomáš Dvořák, @Will

  1. czy jest to dobry sposób, aby przejść zależy od konkretnej sytuacji .
  2. Nie ma nic złego w używaniu if ramach mapie/flatmap/subskrybować.

zaktualizowany kod:

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

Konkretne użycie operatorów zależy od sytuacji. Idź z najprostszym rozwiązaniem, w tym przypadku 'apiResultStream.subscribe (aBoolean -> if (aBoolean) {doSomething} else {doSomethingElse}. Dzięki takiemu podejściu nie musisz nawet tego buforować (co i tak było nieparzyste). Trudno polecić więcej, gdy nie wiemy, co chcesz osiągnąć: –

+0

dzięki dobrej radzie zaktualizowałem pytanie "unikam piekła oddzwonienia" – kyanro

+1

Nawet po aktualizacji problem, który przedstawiłeś, polega na tym, że chcesz wykonać jedno z dwóch działań powodujących efekt uboczny w zależności od wartości obserwowanej wartości boolowskiej Moje proste rozwiązanie nadal obowiązuje i rozwiązuje twój przykład w prosty i czysty sposób Jeśli faktycznie masz inny problem do rozwiązania, opublikuj go Btw, oczywiście może użyć 'filtru', aby podzielić przepływ, ale znowu, czy jest to dobry sposób na odejście, zależy od konkretnej sytuacji. –

Odpowiedz

12

Istnieje wiele sposobów na zrobienie tego, a to naprawdę zależy od twojego przypadku użycia. Generalnie nie chciałbym dzielić się na 2 strumienie, ponieważ czyni to twój kod mniej czytelnym. Ponadto, nie jestem pewien, jakie korzyści uzyskasz z połączenia flatMap. Nie ma nic złego w robieniu, jeśli coś w ramach połączenia na mapie.

Oto kilka opcji:

1 - W przypadku dodawania zalogowaniu (trochę jak twój linii wydruku), używam doOnEach()

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

2 - Prace robisz jest częścią swój strumień, a ty będziesz chciał zrobić więcej pracy w strumieniu później - użyj map

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

3 - Jeśli jest to praca, którą chcesz zrobić na końcu rurociągu - i E po zakończeniu całego transformacyjnego lub innego strumienia, np. Praca, zrób to w wywołaniu subskrypcji.

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

Problemem jest to - z takim prostym przypadku zastosowania, trudno zaproponować najlepsze rozwiązanie, ale doceniam, że w nauce Rx, wypracowanie jak zrobić instrukcji warunkowych może wydawać się mylące. Ogólnie używam po prostu map lub flatMap, gdy wywołuję inną metodę, która zwraca Observable i robię tam moją logikę.

Aktualizacja

Nadal nie wiem, dlaczego jesteś dzieląc swoje transmisje. Dopóki nie zaczniesz robić sprytnie z różnymi wątkami, pierwsze wywołanie subskrypcji będzie blokowało drugie, co prawdopodobnie nie jest tym, czego potrzebujesz. Jeśli nie zadzwonisz do subskrybenta więcej niż raz, to nie potrzebujesz połączenia cache().

Nie ma nic złego w korzystaniu z if statement w obrębie map//subscribe. Zwłaszcza, jeśli czyni twój kod bardziej czytelnym.

chciałbym wykonać następujące czynności:

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

więc znacznie czystsze.

Jestem nieco zdezorientowany przez twoje subskrypcje System.out.println. Czy jest to do celów debugowania lub rejestrowania? Jeśli tak, po prostu zrób to w powyższej płaskiej mapie w instrukcji if.

Nadzieja to pomaga,

Will

+0

dzięki dobry komentarz. Zastosowałem ten komentarz do mojego kodu. ale nie mogłem zastąpić "piekielnego oddzwaniania". więc zaktualizowałem moje pytanie. – kyanro

+0

@kyanro Zaktualizowana odpowiedź odzwierciedlająca aktualizację. – Will

+0

Przepraszam za pomyłkę. println reprezentował "doSomething". Mój kod ma inny typ zwrotu betwen callSuccessApi i callFailureApi. W takim przypadku, czy mogę zastosować to rozwiązanie? – kyanro

0

Aby uniknąć if/else i nie przerwać łańcucha ™, lubię używać publikowania i scalić rozdzielić i ponownie połączyć strumienia:

apiResultStream 
    .publish(results -> 
    Observable.merge(
     results.filter(result -> result.response == true) 
       .flatmap(result -> callSuccessApiObservable()), 
     results.filter(result -> result.response == false) 
       .flatmap(result -> callFailureApiObservable()) 
    ) 
) 
    .subscribe(... 
Powiązane problemy