Где провести черту с реактивным программированием

Я использую RxJava в своем проекте уже около года. Со временем я полюбил его очень сильно - теперь я думаю, может быть, слишком много...

Большинство методов, которые я сейчас пишу, содержат в себе некоторую форму Rx, и это здорово! (пока нет). Теперь я замечаю, что некоторые методы требуют большой работы, чтобы объединить различные наблюдаемые методы производства. У меня такое чувство, что хотя я понимаю, что пишу сейчас, следующему программисту будет очень трудно понять мой код.

Прежде чем я перейду к сути, позвольте мне привести пример прямо из моего кода в Kotlin (не углубляйтесь в него):

private fun <T : Entity> getCachedEntities(
      getManyFunc: () -> Observable<Timestamped<List<T>>>,
      getFromNetwork: () -> Observable<ListResult<T>>,
      getFunc: (String) -> Observable<Timestamped<T>>,
      insertFunc: (T) -> Unit,
      updateFunc: (T) -> Unit,
      deleteFunc: (String) -> Unit)
      = concat(
      getManyFunc().filter { isNew(it.timestampMillis) }
          .map { ListResult(it.value, "") },
      getFromNetwork().doOnNext {
        syncWithStorage(it.entities, getFunc, insertFunc, updateFunc, deleteFunc)
      }).first()
      .onErrorResumeNext { e ->  // If a network error occurred, return the cached data and the error
        concat(getManyFunc().map { ListResult(it.value, "") }, error(e))
      }

Вкратце, что это делает:

  • Retrieve some timestamped data from storage
    • If data is not new, fetch data from network
      • Sync network data again with the storage (to update it)
    • Если произошла сетевая ошибка, снова извлеките более старые данные и ошибку.

И вот мой актуальный вопрос: реактивное программирование предлагает несколько действительно мощных концепций. Но как мы знаем with great power comes great responsibility.

Где мы проводим линию? Можно ли заполнить все наши программы потрясающими реактивными однострочниками или мы должны сохранить их только для действительно обыденных операций?

Очевидно, это очень субъективно, но я надеюсь, что кто-то с большим опытом может поделиться своими знаниями и подводными камнями. Позвольте мне сформулировать это лучше

Как сделать мой код реактивным, но легко читаемым?


person maxandron    schedule 13.03.2016    source источник


Ответы (2)


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

Лично я думаю, что самая большая подсказка кроется в названии реактивный фреймворк. Учитывая требование, вам нужно подумать о том, действительно ли реактивное решение имеет смысл.

В любом предложении Rx вы хотите ввести один или несколько потоков событий и выполнить какое-то действие в ответ на событие.

Я думаю, что есть два ключевых вопроса, которые нужно задать:

  • Вы контролируете поток событий?
  • В какой степени вы должны завершать ответы со скоростью потока событий?

Если вы не контролируете поток событий и вы должны отвечать со скоростью потока событий, тогда Rx - хороший кандидат.

В любых других обстоятельствах это, вероятно, плохой выбор.

Я видел много примеров, когда люди прыгали через обручи, чтобы создать иллюзию отсутствия контроля, чтобы оправдать Rx, что мне кажется безумием. Зачем отказываться от контроля, который у вас есть?

Некоторые примеры:

  1. Вы должны извлечь данные из фиксированного списка файлов и сохранить их в базе данных. Вы решаете поместить каждое имя файла в тему и создать реактивный конвейер, который открывает каждый файл и проецирует данные, затем каким-то образом обрабатывает данные и, наконец, записывает их в базу данных.

    Это не проходит контрольный тест и тест скорости. Было бы гораздо проще перебирать файлы, извлекать их и обрабатывать как можно быстрее. Фраза «решить нажать» здесь является раздачей.

  2. Вам нужно отобразить цены на акции с фондовой биржи.

    Очевидно, что это хороший выбор для Rx. Если вы не можете идти в ногу со скоростью цен в целом, вы облажались. Возможно, вы смешиваете цены (возможно, чтобы предоставлять обновление только раз в секунду), но это все равно считается отслеживанием. Чего вы не можете сделать, так это попросить фондовую биржу снизить скорость.

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

Иногда вы носите клиентскую шляпу в системе клиент/сервер, и легко попасть в ловушку, жертвуя контролем или размещая контроль не в том месте, что можно легко исправить с помощью правильного дизайна. Учти это:

  1. Клиентское приложение отображает обновления новостей с сервера.

    • News updates are submitted to the server at any time and are created in high volume.
    • Клиент должен обновляться с интервалом, установленным клиентом.
    • Интервал обновления можно изменить в любое время, и пользователь всегда может запросить немедленное обновление.
    • Клиент показывает только обновления, помеченные определенными ключевыми словами, указанными пользователем.
    • Обновления новостей иногда бывают длинными, и клиент не должен хранить полное содержание обновлений новостей, а должен отображать заголовок и сводку.
    • По запросу пользователя может быть показано полное содержание статьи.

Здесь частота обновлений новостей не зависит от клиента. Но нужная частота обновления и интересующие теги есть.

Для клиента, чтобы получать все обновления новостей по мере их поступления и фильтровать их на стороне клиента, это не сработает. Но вариантов масса:

  • Должен ли сервер отправлять поток данных обновлений с учетом частоты обновления клиента? Что делать, если клиент уходит в автономный режим?
  • А если клиентов тысячи? Что делать, если клиент хочет немедленного обновления?

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

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

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

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

Заворачивать

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

Поэтому, если вы задумаетесь об элементах управления и коэффициента отклика в своем сценарии, вы, вероятно, останетесь на правильном пути.

Проблема скорости отклика может быть тонкой, но важен аспект степени. Скорость прибытия может колебаться, и будет некоторая приемлемая степень колебаний скорости отклика - ясно, что если у вас в конечном итоге нет способа наверстать упущенное, то в какой-то момент клиент взорвется.

person James World    schedule 17.03.2016
comment
Удивительный ответ! Большое спасибо, Джеймс, это дало мне много пищи для размышлений. - person maxandron; 17.03.2016
comment
Отличный ответ Джеймс. Это больше похоже на раздел Почему Rx в IntroToRx introtorx.com/Content/v1.0.10621.0/01_WhyRx.html - person Lee Campbell; 21.03.2016
comment
Это отличный ответ, однако я не полностью его понял. В примере с ценами на акции, как Rx поможет вам не отставать от цен? - person Emilios1995; 23.03.2016
comment
Взгляните на zerobugbuild.com/?p=192 — это всего лишь один из подходов к смешение. По сути, вы создаете оператор, который каким-то образом объединяется, ожидая возврата подписчика из текущего вызова OnNext(). - person James World; 23.03.2016
comment
есть приложение, где оно буквально оборачивает все в наблюдаемое. совершенно ненужно. - person filthy_wizard; 05.01.2019

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

  1. Могу ли я проверить это?
  2. Могу ли я легко нанять кого-то, кто может поддерживать его. Не трудно поддерживать его, но будет ли хорошо, если его оставить в покое?

С этой целью я также считаю, что только потому, что вы можете, не всегда означает, что вы должны. В качестве руководства я стараюсь избегать создания запросов, которые превышают, скажем, 7 строк кода. Запросы большего размера я стараюсь разделить на подзапросы, которые составляю сам.

Если код, который вы предоставили, лежит в основе кодовой базы и находится на крайнем уровне сложности, то это может быть хорошо. Однако, если вы обнаружите, что весь ваш код Rx настолько сложен, возможно, вы создаете сложную для работы базу кода.

person Lee Campbell    schedule 14.03.2016