2015-02-03 3 views
16

Ich versuche, meinen Code durch RX-Java zu ersetzen. (Es ist sehr kleiner Code.)Wie 'If-Anweisung' mit RX-Java zu ersetzen, um Rückruf-Hölle zu vermeiden?

Es ist fertig und es funktioniert.

Aber ich möchte wissen, ...

  1. Ist es ein guter Stil Rx?
  2. Wenn nicht gut, geben Sie bitte schlechten Punkt

Unten ist mein Code, die api Handhabung ist.

vor

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

nach

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

ich fast ersetzt. thanks for good comment.
(aber ich habe ein paar nicht-Code ersetzt. Es hat viele Rückruf und wenn Äußerung.)

Ich will ‚Hölle Rückruf‘ vermeiden.

Der Schlüssel ist, unterschiedlichen Ergebnistyp zwischen 'callSuccessApi' und 'callFailureApi'

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

nach mit rx (Rückruf Hölle vermeiden! Ist es ein guter Rx Stil?)

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

Entschuldigung für meine schlechte Englisch und lange Frage.

Mein Code aktualisiert

Ich habe 2 wichtige Informationen in dieser Frage. (Danke @ Tomáš Dvořák, @Will

  1. , ob es ein guter Weg, um auf die besondere Situation zu gehen depends
  2. .
  3. Es ist nichts falsch mit der Verwendung einer if-Anweisung innerhalb einer Karte/flatmap/abonnieren.

Aktualisierter Code

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

Konkrete Verwendung der Operatoren hängt von der Situation ab. Gehen Sie mit der einfachsten Lösung, in diesem Fall 'apiResultStream.subscribe (aBoolean -> if (aBoolean) {doSomething} else {doSomethingElse}'. Mit diesem Ansatz müssen Sie nicht einmal im Cache (was sowieso ungerade war). Es ist schwer, mehr zu empfehlen, wenn wir nicht wissen, was Sie erreichen möchten. –

+0

danke guten Rat. Ich aktualisierte Frage zu "Callback-Hölle vermeiden." – kyanro

+1

Auch nach Ihrem Update ist das Problem, das Sie vorgestellt haben, dass Sie durchführen möchten Eine der beiden Nebeneffekte hängt vom Wert der booleschen Observablen ab.Meine einfache Lösung gilt und löst Ihr Beispiel einfach und sauber.Wenn Sie tatsächlich ein anderes Problem zu lösen haben, bitte posten Sie das. Übrigens natürlich Sie kann 'filter' verwenden, um den Fluss zu teilen, aber wiederum, ob es ein guter Weg ist, hängt von der jeweiligen Situation ab. –

Antwort

12

Es gibt viele Möglichkeiten, dies zu tun, und es hängt wirklich von Ihrem Anwendungsfall ab. Im Allgemeinen möchte ich nicht in 2 Streams aufgeteilt werden, da dies Ihren Code weniger lesbar macht. Ich bin mir auch nicht sicher, welchen Nutzen Sie aus dem flatMap-Anruf ziehen. Es ist nichts falsch daran zu tun, wenn Sachen innerhalb einer Karte anrufen.

Hier sind ein paar Optionen:

1 - Zum (ein bisschen wie Ihre Druckzeilen) Protokollierung hinzugefügt, ich doOnEach()

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

2 verwenden - Die Arbeiten Sie Ihren Teil tun von Strom, und Sie gehen zu wollen später mehr Arbeit auf dem Stream zu tun - verwenden map

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

3 - Wenn diese Arbeit ist, dass Sie am Ende der Pipeline tun wollen - ich E nachdem alle transformierenden oder anderen streamähnlichen Arbeiten abgeschlossen sind, dann mache es im Subskriptionsaufruf.

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

Das Problem ist - mit einem solch einfachen Anwendungsfall, ist es schwierig, die beste Option vorschlagen, aber ich schätze, dass Rx in Lernen, Arbeit, wie bedingte Anweisungen tun verwirrend erscheinen kann. Im Allgemeinen verwende ich einfach map oder flatMap, wenn ich eine andere Methode anrufe, die eine Observable zurückgibt und meine Logik dort tue.

aktualisieren

Immer noch nicht sicher, warum Sie Ihre Streams sind zu splitten. Wenn Sie nicht mit verschiedenen Threads clever werden, blockiert der erste Subskriptionsaufruf die zweite, was wahrscheinlich nicht das ist, was Sie wollen. Wenn Sie das Abonnement nicht mehr als einmal anrufen, benötigen Sie auch keinen cache() Anruf.

Es ist nichts falsch daran, einen if statement innerhalb eines map/flatmap/subscribe zu verwenden. Vor allem, wenn es Ihren Code lesbarer macht.

Ich würde folgendes tun:

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

So viel sauberer.

Ich bin ein bisschen verwirrt durch Ihre System.out.println Anrufe in abonnieren. Gibt es das für Debug- oder Protokollierungszwecke? Wenn ja, dann tue das in der obigen flatMap in der if-Anweisung.

hoffte, das hilft,

Will

+0

danke guten Kommentar. Ich habe diesen Kommentar auf meinen Code angewendet. aber ich konnte die "Callback-Hölle" nicht ersetzen. Also habe ich meine Frage aktualisiert. – kyanro

+0

@kyanro Aktualisierte Antwort, um Ihr Update zu reflektieren. – Will

+0

Entschuldigung für Verwirrung. println repräsentiert 'doSomething'. Mein Code hat einen anderen Rückgabetyp zwischen callSuccessApi und callFailureApi. Kann ich in diesem Fall diese Lösung anwenden? – kyanro

0

das zu vermeiden, wenn/sonst und nicht die Kette ™ zu brechen, Ich mag den Strom verwenden, veröffentlichen und zu verschmelzen teilen und wieder zusammenführen:

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