Как заменить "if statement" на rx-java, чтобы избежать обратного вызова ад?

Я пытаюсь заменить мой код на rx-java. (Это очень маленький код.)

Он закончен, и он работает.

Но я хочу знать...

  • Это хороший стиль Rx?
  • Если не хорошо, укажите плохую точку.

Ниже мой код, который обрабатывает api.

перед

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

после

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

Я почти заменил. спасибо за хороший комментарий.
(но у меня есть несколько не замененных кодов. У него много запросов обратного вызова и if).

Я хочу избежать "callback hell".

Ключ - это другой тип результата между 'callSuccessApi' и 'callFailureApi'

до 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...
                }
            }
        }
    }
}

после с rx (избегать обратного вызова ад! Это хороший стиль 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()));

Извините за мой бедный английский и длинный вопрос.

Обновлен мой код

У меня есть 2 важных информации в этом вопросе (спасибо @Tomáš Dvořák, @Will

  • будет ли это хороший способ, зависит от конкретной ситуации.
  • Нет ничего плохого в использовании инструкции if в map/flatmap/subscribe.

обновленный код

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

Ответы

Ответ 1

Существует множество способов сделать это, и это действительно зависит от вашего варианта использования. В общем, я бы не хотел разбивать на 2 потока, так как это делает ваш код менее удобочитаемым. Кроме того, я не уверен, какую выгоду вы получите от звонка flatMap. Нет ничего плохого в том, чтобы делать, если вещи в карточном вызове.

Вот несколько вариантов:

1 - для добавления журнала (немного похоже на ваши строки печати) я использую doOnEach()

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

2 - Работа, которую вы выполняете, является частью вашего потока, и вы захотите больше работать над потоком позже - используйте map

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

3 - Если это работа, которую вы хотите выполнить в конце конвейера - IE после того, как все трансформационные или другие потоки, например, работа завершена, сделайте это в вызове подписки.

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

Проблема - с таким простым прецедентом трудно предложить лучший вариант, но я понимаю, что в обучении Rx разработка способов выполнения условных операторов может показаться запутанной. В общем, я просто использую map или flatMap, когда я вызываю другой метод, который возвращает Observable и выполняет мою логику там.

Обновление

Не уверен, почему вы раскалываете свои потоки. Если вы не начнете разбираться в разных потоках, первый вызов подписки будет блокировать второй, который, вероятно, не тот, который вы хотите. Кроме того, если вы не вызываете подписку более одного раза, вам не нужен вызов cache().

Нет ничего плохого в использовании if statement в map/flatMap/subscribe. Особенно, если это делает ваш код более читаемым.

Я бы сделал следующее:

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

Так много чище.

Я немного смущен вашими вызовами System.out.println в подписке. Это для отладки или ведения журнала? Если это так, просто сделайте это в указанной выше flatMap в инструкции if.

Надеюсь, что это поможет,

Воля

Ответ 2

Чтобы избежать if/else и не разрывать цепочку ™, мне нравится использовать publish и merge для разделения и повторного объединения потока:

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