Рубрики
Без рубрики

Правильный способ ведения дел

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

Автор оригинала: Abhishek Bansal.

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

Постановка проблемы

Рассмотрим следующую постановку задачи:

  1. Вы должны выполнить вызов API, который возвращает сопоставление идентификатора объекта объекту.
  2. Вам нужно преобразовать эту карту в список объектов, содержащих идентификатор объекта.
  3. Теперь вам нужно отфильтровать некоторые объекты из этого списка.
  4. Для каждого из этих отфильтрованных объектов вам необходимо выполнить еще один вызов API.
  5. Вышеуказанные вызовы API должны выполняться последовательно. Каждый последующий вызов API должен выполняться с задержкой в 100 миллисекунд.

Это реальный жизненный сценарий. Я написал реализацию для этого в одном из наших приложений для Android. Мы используем Дооснащение для создания сетей, RxJava-2 для реактивного программирования и |/Retrolambda для Java 8, как синтаксис и сокращение шаблонов.

Теперь подумайте о том, как вы будете реализовывать это в императивном мире.

Императивный Путь

Позволь мне помочь тебе. В императивном мире:

  1. Вероятно, вы выполните вызов API с помощью дооснащения (если вы используете дооснащение) или в асинхронной задаче.
  2. Когда этот API вернется, вы повторите карту и преобразуете ее в список в основном потоке (конечно, вы можете сделать это в другом потоке, но я хочу, чтобы все было просто).
  3. Затем вы должны выполнить вторую итерацию для фильтрации нужных объектов.
  4. Теперь все становится интересным, потому что вам нужно выполнить вызовы API для каждого из этих оставшихся объектов в списке. Вероятно, вам потребуется написать службу или AsyncTask, которая теперь выполняет синхронные вызовы API после времени ожидания 100 мс.

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

Что ж, Rx делает всю эту тяжелую работу за вас. Давайте посмотрим, как это сделать.

Способ Rx

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

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

Вот пример из производственного кода:

APIProvider.getInstance()
.getService()
.getMyObjects(userName)
.subscribeOn(Schedules.io())
.map(MyObject::toList)
.flatMap(rx.Observable::from)
.filter(myObject->myObject.getName().equals(Constants.SOME_NAME)) 
// adding a 10ms delay in subsequent api calls 
.flatMap(myObject -> Observable.timer(100, TimeUnit.MILLISECONDS).map(y -> myObject))
.doOnNext(rule -> { 
    // deleting my object one by one 
    APIProvider.getInstance()
    .getService()
    .deleteMyObject(userName, rule.getId()) .subscribe(); 
    })
.subscribe();

Давайте разберемся в этом коде строка за строкой:

APIProvider.getInstance().getService().getMyObjects(userName)

Это Модифицируйте способ выполнения вызова API. Этот API возвращает карту MyObjectId к Моему объекту . Пример JSON выглядит так:

{"1":{"name":"object1"},"2":{"name":"object2"}}
.subscribeOn(Schedules.io())

subscribeOn – это оператор Rx, который направляет RxJava для запуска обработки в потоке планировщика (фоновом потоке). Только эта строка позволяет переключить обработку в фоновый поток. Как это здорово!

Map – это оператор преобразования, который позволяет нам преобразовывать объекты. Здесь мы преобразуем Карта в Список . тоЛист – это статический метод, написанный в классе MyObject . Этот метод преобразует Map<Идентификатор объекта, MyObject> в Список . Версия списка содержит идентификаторы объектов в самом объекте. Мой объект::перечислить – это способ вызова этого метода на Java 8 с именем Ссылка на метод . Преобразованная структура данных теперь выглядит следующим образом:

[{"id":"1","name":"object1"},{"id":"2","name":"object2"}]
.flatMap(rx.Observable::from)

Это утверждение позволяет нам получать каждый MyObject как отдельные события в наблюдаемом потоке. До этого у нас был целый список или Карта как одно событие в наблюдаемом потоке. Чтобы выполнить фильтрацию и вызовы API для каждого из них отдельно, нам нужно, чтобы они были независимыми событиями в потоке.

.filter(myObject->myObject.getName().equals(Constants.SOME_NAME))

Это довольно очевидно. Оператор Filter позволяет нам отфильтровывать события, которые не соответствуют критериям фильтрации. Любой объект, имя которого не совпадает с константами . SOME_NAME не разрешается распространяться дальше в потоке.

.flatMap(myObject -> Observable.timer(100, TimeUnit.MILLISECONDS).map(y -> myObject))

Этот интересный фрагмент кода позволяет нам добавить задержку в 100 мс между последующими результатами. Внутри flatmap мы создаем другой поток , используя Таймер наблюдаемый. Согласно документации, Таймер возвращает наблюдаемое, которое выдает одно число ноль после указанного вами периода задержки.

Поскольку мы не хотим потерять наши MyObjects , нам нужно сопоставить этот ноль обратно с Моим объектом . Внешняя плоская карта следит за тем, чтобы события принимались в текущем потоке.

doOnNext(myObject -> { // deleting my object one by one 
APIProvider.getInstance().getService()
.deleteMyObject(userName, myObject.getId()) 
.subscribe();
})

Наконец, поскольку у нас есть хороший поток Мои объекты , включая задержку в 100 мс, мы можем выполнять наши вызовы API. Это то, что происходит внутри оператора doOnNext . Оператор doOnNext позволяет нам добавлять зацепку для каждого события в потоке.

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

Если вы заметили, вся эта работа происходит в фоновом режиме, и нам не нужно ни о чем беспокоиться. В моем случае мне не нужно было обрабатывать результаты API в главном потоке Android. Но, в случае необходимости, вы можете просто использовать оператор observeOn для переключения потоков в любое время.

Итак, что вы об этом думаете? Дайте мне знать в комментариях.

Счастливого кодирования!!

Оригинал: “https://www.codementor.io/@abhishekbansal813/the-rx-way-of-doing-things-fgox8sp1n”