Мутация состояния реактивным путем

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

все это поток

однако мое текущее понимание (или ментальный барьер) говорит мне, что любая операция, которая изменяет состояние (например, удаление данных из репозитория), не должна быть/возвращать поток/наблюдаемый.

Небольшие сведения о моем домене: у меня есть пример использования для регистрации геозон. Поскольку геозоны не выдерживают перезагрузки, я отслеживать активные геозоны в репозитории. Иногда приложению необходимо удалить геозону, поэтому основные шаги для этого:

  • получить геозону из репозитория
  • удалить геозону с устройства
  • удалить геозону из репозитория

мое текущее решение следующее:

geofenceRepository.get(id)
            .map(new Func1<Geofence, String>() {
                @Override
                public String call(Geofence geofence) {
                    geofenceRepository.delete(geofence.getId()); // synchronous call here
                    return geofence.getRequestId();
                }
            })
            .toList()
            .flatMap(new Func1<List<String>, Observable<Status>>() {
                @Override
                public Observable<Status> call(List<String> ids) {
                    return locationProvider.removeGeofences(ids);
                }
            });

где Geofence — это моя пользовательская структура данных, а locationProvider — из этой замечательной библиотеки.

Вы заметите, что извлечение данных реализовано как потоковое/наблюдаемое, в отличие от удаления.

Что мне не нравится в приведенном выше примере: оператор карты с побочным эффектом

Вопросы

  • Что было бы лучшим решением, чтобы быть более «реактивным», чего мне здесь не хватает?
  • Есть ли смысл вообще использовать реактивный подход?

реактивное программирование I иметь в виду:

программирование с асинхронными потоками данных


person tsobe    schedule 11.09.2015    source источник


Ответы (2)


Я не вижу никаких проблем с вашим подходом, и более реактивный подход будет означать, что больше API использует/возвращает Observables. У вас могут быть побочные эффекты в любой из лямбда-выражений, но будьте осторожны при изменении значения, поскольку, если задействована асинхронность, один и тот же объект может быть изменен одновременно на разных этапах конвейера. Обычно мы используем неизменяемые или эффективно неизменяемые значения, чтобы избежать этой проблемы. Нет реальной необходимости разделять ваши действия, поэтому предлагаемое doOnNext разделение является предпочтением конкретного разработчика.

Если у вашего geofenceRepository.delete была версия, которая возвращает какое-то Observable, вы могли бы действовать более реактивно, используя flatMapping поверх него:

get(id)
.flatMap(f -> geoFence.deleteAsync(f.getId()).map(v -> f.getRequestId()))
.toList()
.flatMap(...)
.subscribe(...)

Здесь deleteAsync вернет Observable<Void>, который после завершения возобновит основную последовательность с идентификатором запроса.

person akarnokd    schedule 12.09.2015

Реактив великолепен, и я думаю, что эта ситуация идеальна.

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

Попробуйте использовать оператор onNext в своей цепочке для удаления. Что вы хотите сделать, это получить его, который выглядит как geofenceRepository.get(id), удалить его с помощью оператора, а затем удалить его из locationProvider. Может быть, что-то вроде:

geofenceRepository.get(id)
        .map(new Func1<Geofence, String>() {
            @Override
            public String call(Geofence geofence) {
                return geofence.getRequestId();
            }
        })
        .doOnNext(new Action1<String>){
          @Override
          public void call(final String geoFenceId) {
            geofenceRepository.delete(geofence.getId());
          }
        })
        .doOnNext(new Action1<String>() {
          @Override
          public void call(final String geoFenceId) {
                return locationProvider.removeGeofences(ids);
          }
        });

Что вы, вероятно, действительно хотите сделать, так это создать двух подписчиков. Таким образом, если вы хотите посмотреть статус одного или обоих, вы можете это сделать. Вы можете комбинировать статус каждого. Это немного зависит от того, являются ли удаление из репозитория и удаление из провайдера независимыми.

Observable<String> getFence = geofenceRepository.get(id)
        .map(new Func1<Geofence, String>() {
            @Override
            public String call(Geofence geofence) {
                return geofence.getRequestId();
            }
        });

        getFence.subscribe(new Action1<String>){
          @Override
          public void call(final String geoFenceId) {
            geofenceRepository.delete(geofence.getId());
          }
        });

        getFence.map(new Func1<String, Status>() {
          @Override
          public Status call(final String geoFenceId) {
                return locationProvider.removeGeofences(ids);
          }
        }).subscribe(new Action1<Status>(){
           @Override
           public void call(final Status status(){
              //Handle your status for each removal
           }
        });
person Vinny K    schedule 11.09.2015