Розбираємось з фреймворком ReactiveX і пишемо у реактивному стилі для Android. Реактивне програмування в Objective-C Зворотний бік реактивності




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

Якщо ви нічого не чули про реактивне програмування, все гаразд. Це дисципліна, що стрімко розвивається, в якій скомбіновані паралелізм (concurrency) з подієвою орієнтованістю та асинхронністю. Реактивність притаманна будь-якому веб-сервісу та розподіленій системі, і служить ядром багатьох вископродуктивних систем з великим ступенем паралелізму. Якщо коротко, то автори курсу пропонують розглядати реактивне програмування як природне розширення функціонального програмування (з функціями вищих порядків) на паралельні системи з розподіленим станом, координовані та оркестровані асинхронними потоками даних, якими обмінюються активні суб'єкти, або актори.

Більш зрозумілими словами це описується в Реактивному маніфесті, нижче я перекажу основні положення з нього, а повний переклад опубліковано на хабрі. Як розповідає вікіпедія, термін реактивне програмуванняіснує досить давно і має практичні застосування різного ступеня екзотичності, але новий поштовх до розвитку та поширення він отримав зовсім недавно завдяки зусиллям авторів Реактивного маніфесту — ініціативній групі з Typesafe Inc. Typesafe відома серед функціонального програмування як компанія, заснована авторами прекрасної мови Scala і революційної паралельної платформи Akka. Наразі вони позиціонують свою компанію як творця першої у світі реактивної платформи, призначеної для розробки нового покоління. Їхня платформа дозволяє швидко розробляти складні інтерфейси користувачата надає новий рівеньабстракції над паралельними обчисленнями та багатопоточністю, зменшуючи властиві їм ризики завдяки гарантовано передбачуваному масштабуванню. Вона реалізує практично ідеї Реактивного маніфесту і дозволяє розробнику осмислювати і створювати додатки, відповідальні сучасним запитам.

Ви можете познайомитися з цією платформою та реактивним програмуванням, взявши участь у масовому відкритому онлайн-курсі «Принципи реактивного програмування». Цей курс є продовженням курсу Мартіна Одерські «Принципи функціонального програмування на Скеля», який набрав понад 100 000 учасників та продемонстрував один із найвищих у світі ступінь успішного проходження масового відкритого онлайн-курсу його учасниками. Разом із творцем мови Скеля новий курсчитають Ерік Мейєр, який розробляв середовище Rx для реактивного програмування під.NET, і Роланд Кун, який веде команду розробки Akka в Typesafe в даний час. Курс розкриває ключові елементи реактивного програмування і показує, як вони застосовуються для конструювання подієво-орієнтованих систем, що мають масштабованість і стійкість до відмови. Навчальний матеріал ілюструється короткими програмами та супроводжується набором завдань, кожне з яких це програмний проект. У разі успішного виконання всіх завдань учасники отримують сертифікати (зрозуміло, що участь та сертифікати безкоштовні). Курс триває 7 тижнів і розпочинається цього понеділка, 4 листопада. Детальний план, а також вступне відео доступні на сторінці курсу: https://www.coursera.org/course/reactive.

Тим хто зацікавився або сумнівається, пропоную стислий виклад базових концепцій Реактивного маніфесту. Його автори відзначають значні зміни у вимогах до додатків за останні роки. Сьогодні програми розгортаються в будь-якому оточенні від мобільних пристроївдо хмарних кластерів із тисячами багатоядерних процесорів. Ці оточення висувають нові вимоги до програмного забезпечення та технологій. В архітектурах попереднього покоління акцент робився на керовані сервери та контейнери, а масштабування досягалося за рахунок додаткового дорогого обладнання, пропрієтарних рішень та паралельних обчислень через багатопоточність. Зараз розвивається нова архітектура, в якій можна виділити чотири найважливіші риси, які все більш переважають як у користувальницьких, так і в корпоративних промислових оточеннях. Системи з такою архітектурою: подієво-орієнтовані (Event-driven), масштабовані (Scalable), відмовостійкі (Resilient) і мають швидкий відгук, тобто. чуйні (Responsive). Це забезпечує комфортну взаємодію з користувачем, що дає відчуття реального часу, і підтримується масштабованим прикладним стеком, що самовідновлюється, готовим до розгортання в багатоядерних і хмарних оточеннях. Кожна з чотирьох характеристик реактивної архітектури застосовується до всього технологічного стеку, що відрізняє їх від ланок багаторівневих архітектурах. Розглянемо їх трохи докладніше.


Подієво-орієнтованіДодатки припускають асинхронні комунікації компонент і реалізують їх слабку пов'язаність (loosely coupled design): відправник і одержувач повідомлення не потребують відомостей ні один про одного, ні про спосіб передачі повідомлення, що дозволяє сконцентруватися на змісті комунікацій. Крім того, що слабопов'язані компоненти значно покращують супроводжуваність, розширюваність та еволюціонування системи, асинхронність та неблокуючий характер їх взаємодії дозволяють також звільнити значну частину ресурсів, знизити час оклику та забезпечити б обільшу пропускну спроможністьпроти традиційними додатками. Саме завдяки подієво-орієнтованій природі можливі решта рис реактивної архітектури.

Масштабованістьу тих реактивного програмування — це реакція системи зміну навантаження, тобто. еластичність, що досягається можливістю додавання або звільнення обчислювальних вузлів у міру потреби. Завдяки низькій зв'язаності, асинхронному обміну повідомленнями та незалежності від розміщення компонентів (location transparency), спосіб розгортання та топологія програми стають вирішенням часу розгортання та предметом конфігурації та адаптивних алгоритмів, що реагують на навантаження. Таким чином, обчислювальна мережастає частиною програми, що спочатку має явну розподілену природу.

ВідмовостійкістьРеактивна архітектура також стає частиною дизайну, і це значно відрізняє її від традиційних підходів до забезпечення безперервної доступності системи шляхом резервування серверів і перехоплення управління при відмові. Стійкість такої системи досягається її здатністю коректно реагувати на збої окремих компонент, ізолювати ці збої, зберігаючи їх контекст у вигляді повідомлень, що викликали їх, і передавати ці повідомлення іншому компоненту, здатному прийняти рішення про те, як слід обробляти помилку. Такий підхід дозволяє зберегти чисту бізнес-логіку програми, відокремивши від неї логіку обробки збоїв, яка формулюється в явному декларативному вигляді для реєстрації, ізолювання та обробки збоїв засобами самої системи. Для побудови таких систем, що самовідновлюються, упорядковуються ієрархічно, і проблема ескалується до того рівня, який здатний її вирішити.

І наостанок чуйність— це здатність системи реагувати на вплив користувача незалежно від навантаження і збоїв, такі додатки залучають користувача у взаємодію, створюють відчуття тісного зв'язку з системою і достатньої оснащеності для виконання поточних завдань. Чуйність актуальна у системах реального часу, а й необхідна широкого кола додатків. Більше того, система, нездатна до швидкого відгуку навіть у момент збою, не може вважатися стійкою до відмови. Чуйність досягається застосуванням моделей (observable models), потоків подій (event streams) і клієнтів зі станом (stateful clients). Спостережувані моделі генерують події при зміні свого стану та забезпечують взаємодію реального часу між користувачами та системами, а потоки подій надають абстракцію, на якій побудовано цю взаємодію шляхом неблокуючих асинхронних трансформацій та комунікацій.

Таким чином, реактивні додатки є збалансованим підходом до вирішення широкого спектра завдань сучасної розробки ПЗ. Побудовані на подієво-орієнтованій підставі, вони надають кошти, необхідні для гарантій масштабованості та відмовостійкості і підтримують повнофункціональну чуйну користувальницьку взаємодію. Автори очікують, що все більше систем буде дотримуватися принципів реактивного маніфесту.

На додачу наводжу план курсу без перекладу. Просто на випадок, якщо ви дочитали до цього місця і вам все ще цікаво.

Week 1: Review of Principles Functional Programming: substitution model, for-expressions and how they relate to monads. Впроваджується до нової implementation of for-expressions: random value generators. Покази як це може бути використане в швидкоплинних випробуваннях і роках на перегляді ScalaCheck, інструмент, який здійснює цю думку.

Week 2: Functional programming and mutable state What makes an object mutable? Наскільки це впливає на substitution model. Extended example: Digital circuit simulation

Week 3: Futures. Впроваджуються майбутні як інший monad, з for-expressions як конкретні syntax. Shows how futures can composed to avoid thread blocking. Discusses cross-thread error handling.

Week 4: Reactive stream processing Generalizing futures to reactive computations over streams. Stream operators.

Week 5: Actors. Introduces the Actor Model, actors є capsulated units of consistency, asynchronous message passing, дискуси different message delivery semantics (at most once, at least once, exactly once) and eventual consistency.

Week 6: Supervision. Встановлюють поправку про помилку, hierarchical failure handling, Error Kernel pattern, lifecycle monitoring, discusses transient and persistent state.

Week 7: Conversation Patterns Відповідь на management conversational state між actors and patterns for flow control, routing messages to pools of actors for resilience or load balancing, acknowledgement of reception to achieve reliable delivery.

Перевірити інформацію. Необхідно перевірити точність фактів та достовірність відомостей, викладених у цій статті. На сторінці обговорення мають бути пояснення … Вікіпедія

Інтерактивність поняття, що розкриває характер та ступінь взаємодії між об'єктами. Використовується в областях: теорія інформації, інформатика та програмування, системи телекомунікацій, соціологія, промисловий дизайн та інші. В… … Вікіпедія

Цю статтю слід вікіфікувати. Будь ласка, оформіть її згідно з правилами оформлення статей. У цього терміна існують інші значення, див. Електромаш (значення) … Вікіпедія

закордонні психотерапевтичні техніки- ГЛУБИНІ ТЕХНІКИ Активна психотерапія (Фром Райхманн). Аналіз буття (Бінсвангер). Аналіз долі (Сонді). Аналіз характеру (В. Райх). Аналіз Я (Х. Кохут, Е. Еріксон). Аналітична ігротерапія (М. Кляйн). Аналітична терапія сім'ї (Richter). Велика психологічна енциклопедія

Книги

  • Реактивне програмування С++. Проектування паралельних та асинхронних додатків з використанням , Пай Прасід, Абрахам Пітер. Проектування паралельних та асинхронних додатків з використанням бібліотеки RxCpp та сучасного C++17 Засоби паралельного програмування, що підтримуються стандартом мовиСпільне…
  • , Нуркевич Т., Крістенсен Б.. У наші дні, коли програми асинхронні, а швидка реакція - найважливіша властивість, реактивне програмування допоможе писати надійніший, краще масштабований і працюючий код.
  • Реактивне програмування з використанням RxJava, Нуркевич Томаш, Крістенсен Бен. У наші дні, коли програми асинхронні, а швидка реакція - найважливіша властивість, реактивне програмування допоможе писати більш надійний, код, що краще масштабується і швидше працює.

Принципи реактивного програмування не є новими, і їх можна простежити з 70-х і 80-х років в основоположних роботах Джима Грея і Пета Хелланда за тандемною системою.

Ці люди набагато випередили свій час. Тільки останні 5-10 років технологічна індустрія була змушена переглянути існуючі «найкращі практики» у розвиток корпоративної системи. Вона навчилася застосовувати знання про реактивні принципи сьогоднішнього світу багатоядерних та хмарних обчислень.

Основою для реактивної системи є передача повідомлень, що створює тимчасову межу між компонентами, дозволяє їх розв'язувати у часі, використовуючи паралельність та простір, що розподіляє навантаження та забезпечує мобільність. Ця розв'язка є вимогою повної ізоляції між компонентами і становить основу як стійкості, так еластичності систем.

Основи реактивного програмування

Це програмування спрямоване на потоки інформації та поширення змін даних. При використанні мов програмування легко виділити статичні та динамічні потоки, при цьому базова модель автоматично поширить зміни через усі потоки даних. Говорячи простими словами, програмування Rx, що випускаються одним компонентом, і базова структура, що надається бібліотеками Rx, поширюватиме ці зміни на інший компонент, зареєстрований для отримання цих змін. Реактивне програмування Rx складається із трьох ключових моментів.

Основні функції компонентів:

  1. Спостерігаються - нічим іншим, як потоки даних. Спостерігається, що упаковує дані, які можуть передаватися з одного потоку в інший. Вони переважно випускають дані періодично або лише один раз у своєму життєвому циклі на основі конфігурацій. Існують різні оператори, які можуть допомогти спостерігачеві надіслати деякі конкретні дані на основі певних подій.
  2. Спостерігачі споживають потік даних, що випромінюється спостерігається. Спостерігачі підписуються за допомогою методу реактивного програмування subscribeOn() для отримання даних, що передають спостережуваним. Щоразу, коли спостерігається передасть дані, всі зареєстровані спостерігачі отримують дані у зворотному виклику onNext (). Тут вони можуть виконувати різні операції, такі як розбір відповіді JSON або оновлення інтерфейсу користувача. Якщо є помилка, викликана спостережуваним, спостерігач отримає її в Error ().
  3. Планувальники (розклад) - це компонент Rx, який повідомляє спостерігачам і спостерігачам, за яким потоком вони повинні працювати. Можна використовувати метод observOn(), щоб повідомляти спостерігачам, на якому потоці вони повинні спостерігати. Крім того, можна використовувати schedOn(), щоб повідомити спостерігачеві, в якому потоці вони повинні запускатися.

У реактивному програмуванні з використанням RxJava передбачені основні стандартні потоки, такі як Schedulers.newThread() створять новий фон. Schedulers.io () виконає код у потоці введення-виведення.

Основними перевагами Rx є збільшення використання обчислювальних ресурсів на багатоядерному та багатопроцесорному устаткуванні, підвищення продуктивності за рахунок скорочення точок та підвищення продуктивності за рахунок скорочення точок серіалізації, згідно із Законом Амдаля та Універсального закону про масштабованість Гюнтера.

Друга перевага - висока продуктивність для розробників, оскільки традиційні парадигми програмування щосили намагалися забезпечити простий і підтримуваний підхід до роботи з асинхронними та неблокуючими обчисленнями та IO. З цими завданнями справляється функціональне реактивне програмування, оскільки зазвичай усуває необхідність у явної координації між активними компонентами.

Там, де виникає Rx, створюється процес створення компонентів та склад робочих процесів. Щоб повністю використовувати асинхронне виконання, включення протитиску має вирішальне значення, щоб уникнути надмірного використання чи, швидше, необмеженого споживання ресурсів. Щоб забезпечити стійкий стан з точки зору потоку даних, зворотний тиск на основі навантаження відсилає попит, що протікає вгору потоком, і приймає повідомлення.

Таким чином, основними перевагами системи є:

  1. Підвищена продуктивність- завдяки можливості швидко та стабільно обробляти величезні обсяги даних.
  2. Покращений UX - через те, що програма більше реагує на користувача.
  3. Спрощені модифікації та оновлення – завдяки більш читальному та легшому прогнозуванню коду.

Але незважаючи на те, що Reactive Programming – дуже корисна штука при створенні сучасного програмного забезпеченняЩоб міркувати про систему на вищому рівні, потрібно використовувати інший інструмент - Reactive Architecture для процесу проектування реактивних систем. Крім того, важливо пам'ятати, що існує багато парадигм програмування, і Rx - це лише один із них, як і будь-який інструмент, він не призначений для всіх випадків використання.

Стійкість реактивних систем

Стійкість - це чуйність при невдачі і є невід'ємною функціональною властивістю системи. Для неї потрібні розробки, а не просто додавання до системи у ретроактивному вигляді. Стійкість реактивного програмування JavaScript виходить за межі відмовостійкості і це відбувається не через деградацію, а в разі невдачі вона може повністю сама виправитися.

Для цього потрібна ізоляція компонентів та стримування відмов, щоб уникнути збоїв, що поширюються на сусідні компоненти, що може призвести до катастрофічних сценаріїв із каскадними збоями. Таким чином, ключем до створення систем Resilient - самовідновлення - є те, що вони можуть бути охарактеризованими як повідомлення, надіслані на інші компоненти, які діють як супервізори і управляються з безпечного контексту поза компонентом, що відмовив.

Тут, будучи керованими повідомленнями, ці кошти відходять від сильно пов'язаних, тендітних, глибоко вкладених синхронних ланцюжків викликів, які здебільшого ігноруються. Ідея полягає в тому, щоб відокремити управління відмовими від ланцюжка викликів, наприклад, звільняючи клієнта від відповідальності за обробку збоїв сервера.

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

Реактивні системи є найбільш продуктивною системною архітектурою, в контексті багатоядерних, хмарних та мобільних архітектур:

  1. Ізоляція відмов пропонує перебирання між компонентами, запобігаючи відмовим від каскадування та обмежуючи обсяг і ступінь відмов.
  2. Ієрархії супервайзерів пропонують кілька рівнів захисту у поєднанні з можливостями самовідновлення, що усуває безліч тимчасових відмов від будь-яких експлуатаційних витрат для розслідування.
  3. Пропуск передачі повідомлень і прозорість розташування дозволяють вимикати та замінювати компоненти, не торкаючись роботи кінцевого користувача. Це знижує вартість збоїв, їхню відносну актуальність, а також ресурси, необхідні для діагностики та виправлення.
  4. Реплікація знижує ризик втрати даних та зменшує вплив збою на доступність пошуку та зберігання інформації.
  5. Еластичність дозволяє зберігати ресурси в міру того, як використання коливається, що мінімізує експлуатаційні витрати при низькому завантаженні та ризик збоїв або термінових інвестицій у масштабованість у міру збільшення навантаження.

Веб-програми можуть значною мірою виграти від стилю розробки з Rx, що дозволяє скласти робочі процеси запиту-відповіді, що включають розгалуження на виклики служб, асинхронне отримання ресурсів і складання відповідей, і подальше сортування для клієнта. Нещодавно push-а-серверні події та веб-сокети стали все частіше використовуватися на практиці, і для виконання цього в масштабі потрібно ефективний спосібзберігання множини відкритих підключень і де IO не блокує.

Для цього є інструменти, наприклад, Streams and Futures, які роблять простими неблокуючі та асинхронні перетворення, і підштовхує їх до клієнтів. Реактивне програмування з рівнем доступу до даних - оновлює та запитує їх у ефективному ресурсі, переважно з використанням баз даних SQLабо NoSQL із асинхронними драйверами.

Веб-програми також виграють від розробки реактивної системи для таких речей, як розподілене кешування, узгодженість даних та сповіщення з кількома вузлами. Традиційні веб-програми зазвичай використовують вузли, що стоять. Але як тільки програмісти починають використовувати Server-Sent-Events (SSE) і WebSockets - ці вузли стають працездатними, оскільки, як мінімум, вони підтримують стан клієнтського з'єднання, а push-повідомлення надсилаються до них відповідним чином. І тому потрібна розробка реактивної системи, оскільки це область, де важлива адресація одержувачів у вигляді обміну повідомленнями.

Суть реактивне програмування Java

Не потрібно обов'язково використовувати Rx у реактивних системах. Тому що Rx програмування та реактивні системи – не одне й теж. Хоча часто використовуються взаємозамінні терміни, але є точними синонімами і відбивають різні речі. Системи є наступним рівнем «реактивності». Цей рівень має на увазі конкретні проектні та архітектурні рішення, які дозволяють створювати стійкі та гнучкі додатки.

Проте дуже хороша ідея — комбінація методів — приносить ще більше переваг додаткам, оскільки робить їх ще більш пов'язаними, дозволяє ефективніше використовувати ресурси та забезпечує нижчу затримку. Коли йдеться про величезні обсяги даних або багатозадачність, часто потрібна асинхронна обробка, щоб системи були швидкими та чуйними.

У Java - представнику старого об'єктно-орієнтованого програмування, асинхронність може стати справді складною і зробити код важко зрозумілим та підтримуваним. Таким чином, Rx особливо корисно для цього «чисто» об'єктно-орієнтованого середовища, оскільки воно спрощує роботу з асинхронними потоками.

З його останніми випусками, починаючи з Java 8, сама Java зробила деякі спроби впровадити вбудовану реактивність, але ці спроби сьогодні не дуже популярні розробники. Тим не менш, є деякі живі і регулярно оновлювані сторонні реалізації для реактивного програмування Java, які допомагають заощадити день і тому особливо цінуються розробниками Java.

У звичайному додатку зазвичай неодноразово виконують деякі операції реактивного програмування з використанням RxJava, тому потрібно порівняти швидкість, використання процесора і пам'яті з тими ж операціями, які були реалізовані як з співпрограмами Kotlin, так і з RxJava. Це початковий тест продуктивності.

Щоразу, коли застосовується новий інструмент, який широко використовуватиметься у всьому кодексі, важливо зрозуміти, чи вплине воно на загальну продуктивність додатка, перш ніж приймати рішення про те, наскільки доцільно використовувати його. Практика використання дає коротку відповідь: в більшості випадків користувачам варто подумати про заміну RxJava на програми Kotlin, особливо в Android.

Реактивне програмування із застосуванням RxJava може, як і раніше, використовуватися в обмеженій кількостівипадків, і в цих випадках можна змішувати як RxJava, так і співпрограми.

Прості причини:

  1. Забезпечують набагато більшу гнучкість, ніж звичайне Rx.
  2. Надає багатий набір операторів у колекціях, які виглядатимуть так само, як із операторами RxJava.
  3. Реактивне програмування Kotlin можуть взаємодіяти за потреби з використанням rxjava.
  4. Вони дуже легкі та ефективні, враховуючи більше високий рівеньвикористання ЦП для збирання сміття з усіх об'єктів, створених RxJava.

Реактивні розширення

Reactive Extensions (ReactiveX або RX) - це бібліотека, яка слідує за принципами Rx, тобто складання асинхронних і заснованих на подіях програм з використанням послідовності, що спостерігається. Ці бібліотеки надають безліч інтерфейсів та методів, які допомагають розробникам писати чистий та простий код.

Реактивні розширення доступні кількома мовами. Програмісти особливо зацікавлені в RxJava і RxAndroid, тому що андроїд - це сфокусована область.

Реактивне програмування за допомогою RxJava - це реалізація Java Reactive Extension з Netflix. В основному це бібліотека, яка складає асинхронні події, дотримуючись шаблону спостерігача.

Можна створювати асинхронний трафік, перетворювати та споживати їх спостерігачем у різних потоках даних. Бібліотека пропонує широкий спектр дивовижних операторів, таких як карта, об'єднання та фільтр, які можуть бути застосовані до потоку даних. Коли програміст почне використовувати фактичні приклади коду, він дізнається більше про операторів та перетворення.

Багатопоточність у додатках Android

Android" class="if uuid-2938324" src="/misc/i/gallery/73564/2938324.jpg" />

Android реактивне програмування (RxAndroid) специфічний для платформи Android з кількома доданими класами поверх RxJava. Більш конкретно - планувальники представлені в RxAndroid (AndroidSchedulers.mainThread()), який відіграє важливу роль у підтримці концепції багатопоточності у додатках для Android.

Крім того, фахівці радять використовувати тільки бібліотеку RxJava. Навіть завдяки великій кількості планувальників, які використовуються у програмуванні для Android.

Нижче наведено список планувальників та їх короткий зміст:

  1. Schedulers.io () - використовується для виконання неінтенсивних операцій, таких як мережеві дзвінки, читання дисків / файлів, операцій з базами даних та який підтримує пул потоків.
  2. AndroidSchedulers.mainThread() - забезпечує доступ до основної теми Thread/UI. Зазвичай у цьому потоці відбуваються операції, такі як оновлення інтерфейсу користувача, взаємодія з користувачем. Фахівці радять користувачам, що вони не повинні виконувати будь-які інтенсивні операції над цим потоком, оскільки це може спричинити кидок програми або діалог ANR.
  3. Schedulers.newThread() - використовуючи це, новий потік буде створено щоразу, коли заплановано завдання. Зазвичай пропонується не використовувати розклад для тривалих робіт. Нитки, створені за допомогою newThread(), не будуть повторно застосовуватись.
  4. Schedulers.computation() - цей графік може застосовуватися для виконання інтенсивних операцій з процесором, по обробці величезних даних центру реактивного програмування, обробка растрових зображень. Кількість потоків, створених з використанням цього планувальника, повністю залежить кількості доступних ядер ЦП.
  5. Schedulers.single () - цей планувальник виконає всі завдання в наступному порядку, що можна використовувати, коли потрібна необхідність послідовного виконання.
  6. Schedulers.immediate() - цей планувальник виконує завдання негайно, синхронно блокуючи основний потік.
  7. Schedulers.trampoline () - виконує завдання у режимі First In-First Out. Усі заплановані завдання виконуватимуться одна одною, обмежуючи кількість потоків фону одним.
  8. Schedulers.from() - дозволяє створювати планувальник від виконавця, обмежуючи кількість створюваних потоків. Коли пул потоків зайнятий, завдання буде поставлено в чергу.

Тепер, коли є хороші теоретичні знання про RxJava та RxAndroid, можна перейти до деяких прикладів коду, щоб краще зрозуміти концепцію. Для початку роботи потрібно додати залежності RxJava та RxAndroid до проектів build.gradle та синхронізувати проект.

Програмування.

Observer підписують на Observable, щоб він міг почати отримувати дані, використовуючи два методи:

  1. SubscribeOn (Schedulers.io ()) - говорить Observable, щоб запустити завдання у фоновому потоці.
  2. ObservOn (AndroidSchedulers.mainThread()) - вказує Observer отримувати дані в потоці інтерфейсу користувача Android.

Ось і все таким чином програміст зможе написати свою першу програму реактивного програмування з RxJava.

Підприємства та постачальники проміжного програмного забезпечення почали використовувати Reactive, а у 2016 -2018 роках спостерігалося величезне зростання корпоративної зацікавленості у прийнятті цієї парадигми.

Rx пропонує продуктивність для розробників завдяки ресурсоефективності на рівні компонентів для внутрішньої логіки та перетворення потоку даних, в той час як реактивні системи пропонують продуктивність для архітекторів та DevOps завдяки стійкості та еластичності на рівні системи. Вони застосовуються для створення «Cloud Native» та інших широкомасштабних розподілених систем. На практиці також широко використовують книги про реактивне програмування Java з методами, що дозволяють комбінувати принципи проектування реактивних систем.

На сьогоднішній день існує ціла низка методологій програмування складних систем реального часу. Одна з таких методологій називається ФРП (FRP). Вона ввібрала в себе шаблон проектування, званий Спостерігач (Observer) з одного боку, з другого, як не важко здогадатися, принципи функціонального програмування. У цій статті ми розглянемо функціональне реактивне програмування на прикладі його реалізації в бібліотеці Sodiumдля мови Haskell.

Теоретична частина

Традиційно програми прийнято поділяти на три класи:

  • Пакетні
  • Інтерактивні
  • Реактивні

Відмінності між цими трьома класами програм криються у типі їхньої взаємодії із зовнішнім світом.

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

Реактивна програма, на відміну від інтерактивної, жорстко синхронізована із зовнішнім середовищем: від неї потрібно реагувати на події зовнішнього світу з прийнятною затримкою в темпі виникнення цих подій. У той же час інтерактивній програмі можна змушувати зовнішнє середовище чекати. Наприклад, текстовий редакторє інтерактивною програмою: користувач, який запустив процес виправлення помилок у тексті, повинен дочекатися його завершення, щоб продовжити редагування. Однак, автопілот є реактивною програмою, оскільки при виникненні перешкоди вона повинна відразу ж скоригувати курс, щоб здійснити маневр обльоту, адже реальний світ не можна поставити на паузу як користувача текстового редактора.

Ці два класи програм з'явилися трохи пізніше, тоді, коли комп'ютери почали використовуватися в управлінні машинами, і стали розвиватися інтерфейси користувача. З того часу змінилося безліч методик реалізації, кожна наступна з яких покликана була виправити недоліки попередніх. Спочатку це були прості подієво-керовані програми: в системі виділяли кілька подій, на які необхідно було реагувати, і створювали обробники цих подій. Обробники також могли генерувати події, які йшли у зовнішній світ. Ця модель була простою і вирішувала деяке коло простих інтерактивних завдань.

Але згодом інтерактивні та реактивні програми ставали складнішими і подієво-орієнтоване програмування перетворилося на пекло. Виникла потреба у більш розвинених інструментах синтезу подієво-керованих систем. У цій методології було дві головні вади:

  • Неявний стан
  • Недетермінованість

Щоб виправити це, спочатку було створено шаблон спостерігач (observer), що трансформував події в значення, що змінюються в часі. Набір цих значень був явним станом, в якому знаходиться програма. Так, на зміну обробці подій прийшла звична для пакетних програм робота з даними. Розробник міг змінювати значення, що спостерігаються, і підписувати обробники на зміну цих значень. З'явилася можливість реалізовувати залежні значення, що змінювалися за заданим алгоритмом, при зміні значень, від яких вони залежали.

Хоч події в цьому підході і пішли, але потреба в них все ж таки залишилася. Не завжди подія передбачає зміну значення. Наприклад, подія реального часу має на увазі збільшення лічильника часу на секунду, проте подія спрацьовування будильника щодня в певний час зовсім не має ніякого значення. Звичайно, можна пов'язати і цю подію з певним значенням, але це буде штучним прийомом. Наприклад, ми можемо ввести значення: час_срабатывания_будильника == остаток_от_деления (поточний_час, 24*60*60). Але це буде не зовсім те, що нас цікавить, оскільки ця змінна прив'язана до секунди і насправді значення змінюється двічі. Щоб з'ясувати, що будильник спрацював, передплатник повинен визначати, що значення стало істинним, а чи не навпаки. Значення буде істинним рівно одну секунду, а якщо ми змінимо період тиків з секунди, скажімо, на 100 мілісекунд, то значення істина буде вже не секунду, а ці 100 мілісекунд.

Поява методології функціонального реактивного програмування – це своєрідна відповідь функціональників на шаблон спостерігач. У FRP були переосмислені вироблені підходи: події (Events) нікуди не поділися, проте значення, що спостерігаються, також з'явилися і були названі характеристиками (Behaviors). Подія в цій методології є деяким дискретно генерованим значенням, а характеристика --- генерується безперервно. Обидва вони можуть бути пов'язані між собою: характеристики можуть генерувати події, а події виступати як джерела для характеристик.

Проблема недетермінованості стану набагато складніша. Вона випливає з того факту, що подієво-керовані системи дефакто асинхронні. Це тягне за собою виникнення проміжних станів системи, які можуть бути з деяких причин неприпустимі. Для вирішення цієї проблеми виникло так зване синхронне програмування.

Практична частина

Бібліотека Sodiumз'явилася як проект реалізації FRPіз загальним інтерфейсом різними мовами програмування. У ній є всі елементи методології: примітиви (Event, Behavior) і шаблони їх використання.

Примітиви та взаємодія із зовнішнім світом

Два основних примітиви, з якими нам належить працювати, це:

  • Event a- подія зі значенням типу a
  • Behavior a- характеристика (або змінне значення) типу a

Ми можемо створювати нові події та значення функціями newEventі NewBehavior:

NewEvent:: Reactive (Event a, a -> Reactive ()) NewBehavior:: a -> Reactive (Behavior a, a -> Reactive ())

Як бачимо, обидві ці функції можуть бути викликані лише в монаді Reactive, в результаті повертається сам примітив, а також функція, яка повинна бути викликана для активації події або зміни значення. Функція створення характеристики приймає як перший аргумент початкове значення.

Щоб пов'язати реальний світ із реактивною програмою, існує функція sync, а щоб пов'язати програму із зовнішнім світом існує функція listen:

Sync:: Reactive a -> IO a listen:: Event a -> (a -> IO ()) -> Reactive (IO ())

Перша функція, як можна зрозуміти з назви, виконує певний реактивний код синхронно, вона дозволяє потрапити до контексту Reactiveз контексту IOа друга служить для додавання обробників подій, що виникають у контексті Reactive, що виконуються в контексті IO. Функція listenповертає функцію unlisten, що потрібно викликати, щоб від'єднати обробник.

У такий спосіб реалізується своєрідний механізм транзакцій. Коли ми робимо щось усередині монади Reactive, код виконується в межах однієї транзакції під час виклику функції sync. Стан детермінований лише поза контекстом транзакції.

Це базис реактивного функціонального програмування, достатній для написання програм. Може трохи збентежити той факт, що слухати можна лише події. Саме так і має бути, як ми побачимо далі, між подіями та характеристиками існують тісні взаємозв'язки.

Операції над основними примітивами

Для зручності до методології внесено додаткові функції, перетворюючі події та характеристики. Розглянемо деякі з них:

Подія, яка ніколи не відбудеться -- (може використовуватись як заглушка) never:: Event a -- Об'єднання двох подій однакового типу в одну -- (зручно для визначення одного обробника для класу подій) merge:: Event a -> Event a -> Event a -- Витягує значення з Maybe подій -- (відокремлюємо зерна від полови) filterJust:: Event (Maybe a) -> Event a -- Перетворює подію на характеристику з початковим значенням -- (міняємо значення при виникненні подій) hold :: a -> Event a -> Reactive (Behavior a) -- Перетворює характеристику на подію -- (генеруємо події при зміні значення) updates:: Behavior a -> Event a -- Перетворює характеристику на подію -- (також генеруємо подію для початкового значення) value:: Behavior a -> Event a - При виникненні події бере значення характеристики, - застосовує функцію і генерує подію snapshot:: (a -> b -> c) -> Event a -> Behavior b - > Event c -- Отримує поточне значення характеристики sample:: Behavior a -> Reactive a - Зводить повторювані події в одне coalesce:: (a -> a -> a) -> Event a -> Event a - Пригнічує всі події, крім першого once:: Event a -> Event a - Поділяє подію з списком на кілька подій split:: Event [a] -> Event a

Сферичні приклади у вакуумі

Давайте спробуємо щось написати:

Import FRP.Sodium main = do sync $ do -- створюємо подію (e1, triggerE1)<- newEvent -- создаём характеристику с начальным значением 0 (v1, changeV1) <- newBehavior 0 -- определяем обработчик для события listen e1 $ \_ ->putStrLn $ "e1 triggered" -- визначаємо обробник для зміни значення характеристики listen (value v1) $ \v -> putStrLn $ "v1 value: " ++ show v -- Генеруємо подію без значення triggerE1 () -- Змінюємо значення характеристики changeV1 13

Встановимо пакет Sodiumза допомогою Cabalі запустимо приклад в інтерпретаторі:

# якщо хочемо працювати в окремій пісочниці # створюємо її > cabal sandbox init # встановимо > cabal install sodium > cabal repl GHCi, version 7.6.3: http://www.haskell.org/ghc/:? for help Loading package ghc-prim... linking... done. Loading package integer-gmp... linking... done. Loading package base... linking... done. # завантажимо приклад Prelude> :l Compiling Main (Example.hs, interpreted) Ok, modules loaded: Main. # Виконаємо приклад *Main>

А тепер поекспериментуємо. Закоментуємо рядок, де змінюємо наше значення (changeV1 13) і перезапустимо приклад:

*Main> :l Example.hs Compiling Main (Example.hs, interpreted) Ok, modules loaded: Main. *Main> main e1 triggered v1 value: 0

Як бачимо, тепер виводиться початкове значення, тому відбувається функція valueгенерує першу подію з початковим значенням показника. Давайте замінимо функцію valueна updatesі подивимося, що вийшло:

*Main> :l Example.hs Compiling Main (Example.hs, interpreted) Ok, modules loaded: Main. *Main> main e1 triggered

Тепер початкове значення не виводиться, але якщо розкоментувати рядок, в якому ми змінили значення, то, як і раніше, буде виведено змінене значення. Повернімо все, як було, і згенеруємо подію e1двічі:

*Main> :l Example.hs Compiling Main (Example.hs, interpreted) Ok, modules loaded: Main. *Main> main e1 triggered e1 triggered v1 value: 13

Як бачимо, подія також спрацювала двічі. Спробуємо цього уникнути, навіщо у функції listenзамінимо аргумент e1на (once e1)тим самим створивши нову подію, яка спрацьовує один раз:

*Main> :l Example.hs Compiling Main (Example.hs, interpreted) Ok, modules loaded: Main. *Main> main e1 triggered v1 value: 13

Коли у події немає аргументу, нам важливий сам факт наявності чи відсутності події, тому функція onceдля об'єднання подій правильний вибір. Однак, коли аргумент є, це не завжди підходить. Перепишемо приклад так:

<- newEvent (v1, changeV1) <- newBehavior 0 listen e1 $ \v ->putStrLn $ "e1 triggered with: " ++ show v listen (value v1) $ \v -> putStrLn $ "v1 value: " ++ show v triggerE1 "a" triggerE1 "b" triggerE1 "c" changeV1 13

Отримаємо, як і очікували, всі події зі значеннями в тому самому порядку, в якому вони були згенеровані:

*Main> :l Example.hs Compiling Main (Example.hs, interpreted) Ok, modules loaded: Main. *Main> main e1 triggered with: "a" e1 triggered with: "b" e1 triggered with: "c" v1 value: 13

Якщо використовуємо функцію onceз e1, то отримаємо тільки першу подію, тому спробуємо використати функцію coalesce, навіщо замінимо аргумент e1в listenаргументом (coalesce (\_ a -> a) e1):

*Main> :l Example.hs Compiling Main (Example.hs, interpreted) Ok, modules loaded: Main. *Main> main e1 triggered with: "c" v1 value: 13

І справді, ми отримали лише останню подію.

Ще приклади

Давайте розглянемо приклади складніше:

Import FRP.Sodium main = do sync $ do (e1, triggerE1)<- newEvent -- создаём характеристику, изменяемую событием e1 v1 <- hold 0 e1 listen e1 $ \v ->putStrLn $ "e1 triggered with: " ++ show v listen (value v1) $ \v -> putStrLn $ "v1 value is: " ++ show v -- генеруємо події triggerE1 1 triggerE1 2 triggerE1 3

Ось що маємо на виході:

*Main> :l Example.hs Compiling Main (Example.hs, interpreted) Ok, modules loaded: Main. *Main> main e1 triggered with: 1 e1 triggered with: 2 e1 triggered with: 3 v1 value is: 3

Значення характеристики виводиться лише один раз, хоча подій було згенеровано декілька. У цьому полягає особливість синхронного програмування: характеристики синхронізовані із викликом sync. Щоб це продемонструвати, трохи переробимо наш приклад:

<- sync $ do (e1, triggerE1) <- newEvent v1 <- hold 0 e1 listen e1 $ \v ->putStrLn $ "e1 triggered with: " ++ show v listen (value v1) $ \v -> putStrLn $ "v1 value is: " ++ show v return

Ми лише винесли тригер події у зовнішній світ і викликаємо його в різних фазах синхронізації:

*Main> :l Example.hs Compiling Main (Example.hs, interpreted) Ok, modules loaded: Main. *Main> main v1 value is: 0 e1 triggered with: 1 v1 value is: 1 e1 triggered with: 2 v1 value is: 2 e1 triggered with: 3 v1 value is: 3

Тепер за кожної події демонструється нове значення.

Інші операції над примітивами

Розглянемо наступну групу корисних функцій:

Об'єднує події з використанням функції mergeWith:: (a -> a -> a) -> Event a -> Event a -> Event a - Фільтрує події, залишаючи тільки ті, - для яких функція повертає істину filterE:: (a -> Bool) -> Event a -> Event a - Дозволяє "вимикати" події - коли характеристика дорівнює False gate:: Event a -> Behavior Bool -> Event a - Організує перетворювач подій - з внутрішнім станом collectE: : (a -> s -> (b, s)) -> s -> Event a -> Reactive (Event b) - Організує перетворювач характеристик - з внутрішнім станом collect:: (a -> s -> (b , s)) -> s -> Behavior a -> Reactive (Behavior b) -- Створює характеристику як результат накопичення подій accum:: a -> Event (a -> a) -> Reactive (Behavior a)

Звичайно, це ще далеко не всі функції, що надаються бібліотекою. Є й набагато екзотичніші речі, які виходять за рамки цієї статті.

Приклади

Давайте спробуємо ці функції у дії. Почнемо з останньої, організуємо щось на кшталт калькулятора. Нехай у нас буде певне значення, до якого можна застосовувати арифметичні функції та отримувати результат:

Import FRP.Sodium main = triggerE1<- sync $ do (e1, triggerE1) <- newEvent -- пусть начальное значение будет равно 1 v1 <- accum (1:: Int) e1 listen (value v1) $ \v ->putStrLn $ "v1 value is: " ++ show v return triggerE1 -- ​​додаємо 1 sync $ triggerE1 (+ 1) -- помножуємо на 2 sync $ triggerE1 (*2) -- віднімаємо 3 sync $ triggerE1 (+ (-3) ) - додаємо 5 sync $ triggerE1 (+ 5) - зводимо в ступінь 3 sync $ triggerE1 (^ 3)

Запустимо:

*Main> :l Example.hs Compiling Main (Example.hs, interpreted) Ok, modules loaded: Main. *Main> main v1 is: 1 v1 is: 2 v1 is: 4 v1 is: 1 v1 is: 6 v1 is: 216

Може здатися, що набір можливостей досить мізерний, але насправді це не так. Адже ми маємо справу з Хаскелем, аплікативні функтори та монади нікуди не поділися. Ми можемо виконувати над характеристиками та подіями будь-які операції, які ми звикли виконувати над чистими значеннями. Внаслідок чого виходять нові характеристики та події. Для характеристик реалізований клас функтор і аплікативний функтор, для подій з очевидних причин лише функтор.

Наприклад:

<$>), (<*>)) import FRP.Sodium main = do (setA, setB)<- sync $ do (a, setA) <- newBehavior 0 (b, setB) <- newBehavior 0 -- Новая характеристика a + b let a_add_b = (+) <$>a<*>b - Нова характеристика a * b let a_mul_b = (*)<$>a<*>b listen (value a) $ \v -> putStrLn $ "a = "++ show v listen (value b) $ \v -> putStrLn $ "b = "++ show v listen (value a_add_b) $ \v - > putStrLn $ "a + b = "++ show v listen (value a_mul_b) $ \v -> putStrLn $ "a * b = "++ show v return (setA, setB) sync $ do setA 2 setB 3 sync $ setA 3 sync $ setB 7

Ось що буде виведено в інтерпретаторі:

λ> main a = 0 b = 0 a + b = 0 a * b = 0 a = 2 b = 3 a + b = 5 a * b = 6 a = 3 a + b = 6 a * b = 9 b = 7 a + b = 10 a * b = 21

А тепер подивимося як щось подібне працює з подіями:

Import Control.Applicative ((<$>)) import FRP.Sodium main = do sigA<- sync $ do (a, sigA) <- newEvent let a_mul_2 = (* 2) <$>a let a_pow_2 = (^ 2)<$>a listen a $ \v -> putStrLn $ "a = "++ show v listen a_mul_2 $ \v -> putStrLn $ "a * 2 = "++ show v listen a_pow_2 $ \v -> putStrLn $ "a^2 = " ++ show v return sigA sync $ do sigA 2 sync $ sigA 3 sync $ sigA 7

Ось що буде виведено:

λ> main a = 2 a * 2 = 4 a ^ 2 = 4 a = 3 a * 2 = 6 a ^ 2 = 9 a = 7 a * 2 = 14 a ^ 2 = 49

У документації є перелік екземплярів класів, які реалізовані для Behaviorі Event, Але ніщо не заважає продати екземпляри відсутні класів.

Зворотний бік реактивності

p align="justify"> Функціональне реактивне програмування безсумнівно спрощує розробку складних систем реального часу, проте існує безліч аспектів, які необхідно враховувати при використанні цього підходу. Тому розглянемо тут проблеми, які найчастіше мають місце.

Неодночасність

Синхронне програмування передбачає певний механізм транзакцій, що забезпечує узгодженість станів системи, що послідовно змінюють один одного, і, отже, відсутність проміжних несподіваних станів. У Sodiumза транзакції відповідають виклики sync. Хоч стан усередині транзакції не визначено, проте не можна вважати, що все всередині відбувається одночасно. Значення змінюються у порядку, який впливає результат. Так, наприклад, спільне використання подій та характеристик може спричинити несподівані ефекти. Розглянемо приклад:

Import Control.Applicative ((<$>)) import FRP.Sodium main = do setVal<- sync $ do (val, setVal) <- newBehavior 0 -- создаём булеву характеристику val >2 let gt2 = (> 2)<$>val -- створюємо подію зі значеннями, які > 2 let evt = gate (value val) gt2 listen (value val) $\v -> putStrLn $ putStrLn $ "val > 2?" ++ show v listen evt $ \v -> putStrLn $ "val > 2: " ++ show v return $setVal 0

Очікується висновок на кшталт цього:

Val = 0 val > 2? False val = 1 val > 2? False val = 2 val > 2? False val = 3 val > 2? True val > 2: 3 val = 4 val > 2? True val > 2: 4 val = 0 val > 2? False

Однак насправді рядок val > 2: 3буде відсутня, а в кінці з'явиться рядок val > 2: 0. Так відбувається тому, що подія зміни значення (value val)генерується до того, як буде обчислено залежну характеристику gt2, і тому подія evtне виникає для встановленого значення 3. Наприкінці ж, коли ми знову встановили 0, обчислення характеристики gt2запізнюється.

Загалом, ефекти ті ж, що і в аналоговій та цифрової електроніки: гонки сигналів, для боротьби з якими використовують різні прийоми Зокрема синхронізацію. Так ми і вчинимо, щоб змусити цей код працювати належним чином:

Import Control.Applicative ((<$>)) import FRP.Sodium main = do (sigClk, setVal)<- sync $ do -- Мы ввели новое событие clk -- сигнал синхронизации -- прям как в цифровой электронике (clk, sigClk) <- newEvent (val, setVal) <- newBehavior 0 -- Также вы создали альтернативную функцию -- получения значения по сигналу синхронизации -- и заменили все вызовы value на value" let value" = snapshot (\_ v ->v) clk let gt2 = (> 2)<$>val let evt = gate (value" val) gt2 listen (value" val) $ \v -> putStrLn $ "val = "++ show v listen (value" gt2) $ \v -> putStrLn $ "val > 2 ? ++ show v listen evt $ \v -> putStrLn $ "val > 2: " ++ show v return (sigClk, setVal) - Ввели нову функцію sync" - яка викликає сигнал синхронізації - в кінці кожної транзакції - - І замінили їй усі виклики sync let sync" a = sync $a >> sigClk() sync" $setVal 1 sync" $setVal 2 sync" $setVal 3 sync" $setVal 4 sync" $setVal 0

Тепер наш висновок став таким, як і очікувалося:

λ> main val = 0 val > 2? False val = 1 val > 2? False val = 2 val > 2? False val = 3 val > 2? True val > 2: 3 val = 4 val > 2? True val > 2: 4 val = 0 val > 2? False

Лінивість

Проблеми іншого роду пов'язані з лінивою природою обчислень у Haskell. Це призводить до того, що при випробуванні коду в інтерпретаторі, деякий висновок наприкінці може бути просто відсутнім. Що можна запропонувати в цьому випадку, так виконати марний крок синхронізації в кінці, наприклад sync $ return ().

Висновок

На цьому поки що, гадаю, достатньо. У теперішній моментодин із авторів бібліотеки Sodiumпише книгу про ФРП. Сподіватимемося, це якось заповнить прогалини в даній галузі програмування і послужить популяризації прогресивних підходів у наших закостенілих умах.

Світ ООП-розробки взагалі та мова Javaзокрема, живуть дуже активним життям. Тут є свої модні тенденції, і сьогодні розберемо один із головних трендів сезону – фреймворк ReactiveX. Якщо ти ще осторонь цієї хвилі - обіцяю, вона тобі сподобається! Це точно краще, ніж джинси із завищеною талією:).

Реактивне програмування

Як тільки ООП-мови дорослі до масового застосування, розробники усвідомили, наскільки іноді не вистачає можливостей С-подібних мов. Оскільки написання коду в стилі функціонального програмування серйозно руйнує якість ООП-коду, а отже, і підтримуваність проекту, було придумано гібрид - реактивне програмування.

Парадигма реактивної розробки будується ідеї постійного відстеження змін стану об'єкта. Якщо такі зміни відбулися, то всі зацікавлені об'єкти мають отримати вже оновлені дані та працювати тільки з ними, забувши про старі.

Хорошим прикладом ідеї реактивного програмування може бути Excel-таблиця. Якщо зв'язати кілька осередків однією формулою, результат обчислення змінюватиметься щоразу, коли зміняться дані у цих осередках. Для бухгалтерії така динамічна зміна даних – звична справа, але для програмістів це скоріше виняток.

A=3; b = 4; c = a + b; F1(c); a=1; F2(c);

У цьому прикладі функції F1 і F2 будуть працювати з різними значеннями змінної C. Часто потрібно, щоб у обох функцій були лише найактуальніші дані, - реактивне програмування дозволить без зміни логіки самих функцій відразу викликати F1 з новими параметрами. Така побудова коду дає змогу моментально реагувати на будь-які зміни, що зробить його швидким, гнучким і чуйним.

ReactiveX

Втілювати з нуля ідеї реактивного програмування може бути досить клопітно - є підводне каміння, та й часу це займе пристойно. Тому для багатьох розробників ця парадигма залишалася лише теоретичним матеріалом, доки не з'явився ReactiveX.

Фреймворк ReactiveX - це інструмент для реактивного програмування, що працює з усіма популярними мовами ООП. Самі творці називають його мультиплатформним API для асинхронної розробки, що базується на патерні «Спостерігач» (Observer).

Якщо термін «реактивне програмування» – це своєрідна теоретична модель, то патерн «Спостерігач» – готовий механізм відстеження змін у програмі. А відстежувати їх доводиться досить часто: завантаження та оновлення даних, оповіщення про події тощо.

Паттерн «Спостерігач» існує приблизно стільки ж, скільки саме ООП. Об'єкт, стан якого можна змінити, називається видавцем (популярний переклад терміна Observable). Решта учасників, яким цікаві ці зміни, - передплатники (Observer, Subscriber). Для отримання повідомлень передплатники реєструються у видавця, явно вказуючи свій ідентифікатор. Видавець час від часу генерує повідомлення, які їм розсилаються за списком зареєстрованих передплатників.

Власне, творці ReactiveX не вигадали нічого революційного, вони просто зручно реалізували патерн. І хоча в багатьох ООП-мовах, і в Java зокрема, є готові реалізації патерну, у цьому фреймворку є додатковий «тюнінг», який перетворює «Спостерігач» на дуже потужний інструмент.

RxAndroid

Порт бібліотеки ReactiveX для світу Androidназивається rxAndroid і підключається, як завжди, через Gradle.

Compile "io.reactivex:rxandroid:1.1.0"

Видавець, що генерує сповіщення, тут задається за допомогою класу Observable. Видавець може мати кілька передплатників, для їх реалізації скористаємося класом Subscriber. Стандартна поведінка Observable - випустити одне або кілька повідомлень для передплатників, а потім завершити свою роботу або видати повідомлення про помилку. Як повідомлення можуть бути як змінні, і цілі об'єкти.

Rx.Observable myObserv = rx.Observable.create(new rx.Observable.OnSubscribe () ( @Override public void call(Subscribersubscriber) ( subscriber.onNext("Hello"); subscriber.onNext("world"); subscriber.onCompleted(); ) ));

У цьому випадку видавець myObserv спочатку надішле рядки hello та message, а потім повідомлення про успішне завершення роботи. Видавець може викликати методи onNext() , onCompleted() і onEror() , тому у передплатників вони мають бути визначені.

Subscriber mySub = New Subscriber () (... @Override public void onNext(String value) (Log.e("got data", " " + value);) );

Все готове до роботи. Залишилось зв'язати об'єкти між собою – і «Hello, world!» у реактивному програмуванні готовий!

MyObserv.subscribe(mySub);

Слід сказати, що це був дуже простий приклад. У ReactiveX є безліч варіантів поведінки всіх учасників патерну: фільтрація, групування, обробка помилок. Користу від реактивного програмування можна відчути, лише спробувавши його у справі. Приступимо до завдання серйозніше.

Продовження доступне лише учасникам

Варіант 1. Приєднайтесь до спільноти «сайт», щоб читати всі матеріали на сайті

Членство у спільноті протягом зазначеного терміну відкриє тобі доступ до ВСІХ матеріалів «Хакера», збільшить особисту накопичувальну знижку та дозволить накопичувати професійний рейтинг Xakep Score!