Вивчення реактивного підходу в програмуванні — досить непроста річ. Цей підхід використовують для підвищення рівня абстракції коду. Замість постійної підтримки коду з високою деталізацією ви можете сконцентруватися на взаємозв'язку подій, які визначають бізнес-логіку. Реактивне програмування може вдвічі скоротити вихідний код, написаний в імперативному стилі.
Реактивне програмування — програмування з асинхронними потоками даних. Це поняття також називають подієво-орієнтованим програмуванням. Кеш, користувацький ввід, змінні, потоки даних — це все є прикладами потоків. Наприклад, уявіть, що ваша стрічка новин у Фейсбуці — потік подій. Ви можете спостерігати за ним і відповідно реагувати на події. Окрім цього ви отримуєте набір функцій для комбінації, фільтрування і створення потоку. Фільтруючи потік ви обираєте лише цікаві для вас події. Ви можете об’єднувати два потоки. Також один або декілька потоків можуть служити вхідними даними для іншого.
Так як основою реактивного програмування є потоки, розглянемо їх на прикладі кліка на кнопку:
Потік – це послідовність подій, упорядкована за часом. Він може видавати три типи даних:
Ми перехоплюємо ці події асинхронно, вказуючи одну функцію, яка буде викликатися, коли викинуто значення, іншу – для помилок і третю для обробки сигналу завершення. У деяких випадках можна опустити останні дві і сфокусуватися на оголошенні функції для перехоплення значень. Прослуховування потоку називається підпискою (subscribing). Функції, які ми оголошуємо, називаються спостерігачами (observer). Потік – це об'єкт наших спостережень (observable, спостережуваний об'єкт). Це в точності патерн проектування, званий «Спостерігач».
Використаємо альтернативний спосіб представлення за допомогою ASCII символів:
Розглянемо приклад: створимо потік подій, перетворивши початковий потік подій натискань.
Спершу додамо лічильник, який буде індикатором натискань кнопки. У більшості реактивних бібліотек кожен потік має багато вбудованих функцій, таких як об'єднання, фільтр, сканер тощо. Коли ви викликаєте одну з цих функцій, таких як clickStream.map(f), вона повертає новий потік, який базується на батьківському (на clickStream). Дочірній потік ніяким чином не зачіпає і не модифікує батьківський. Ця властивість називається постійністю (immutability) і є невід'ємною частиною реактивних потоків. Це дозволяє нам об'єднувати функції, наприклад
Функція map (f) створює новий потік, в якому за допомогою функції f замінюватиметься кожна нова подія. У нашому випадку ми прив'язуємо одиницю до кожного натискання на кнопку. Функція scan(g) агрегує всі попередні значення в потоці, повертаючи значення x = g (accumulated, current). Після цього counterStream посилає загальну кількість натискань.
Для зображення реальної сили реактивного підходу, припустимо, що ви хочете отримати потік, який буде викликати подвійне натискання. Щоб зробити це ще більш цікавим, давайте скажемо, що ми хочемо щоб новий потік розглядав потрійні натискання як подвійні, або, в загальному, як мультинатискання (два або більше). Уявіть, що ви могли б зробити те ж саме в традиційному імперативному програмуванні. Це звучить досить складно і включає в себе змінні для збереження деяких станів і тимчасових інтервалів.
Реалізація ідеї за реактивним програмуванням простіша. Уся логіка зосереджена в 4 рядках коду. Але давайте розглянемо діаграму для кращого розуміння і побудови потоків.
Сірі прямокутники є функціями трансформації одного потоку в інший. Перше, що ми зробили – акумулювали кліки в список. Кожний раз, коли 250 мілісекунд затримки події проходять (ось чому buffer(stream.throttle(250ms)), генерується подія. Результатом є потік списку, де до кожного елементу була застосована функція map(), щоб приєднати до кожного списку його довжину. І нарешті ми ігноруємо число 1, використовуючи функцію filter(x >= 2). Це все – всього 3 операції для створення цільового потоку. Ми можемо підписати на нього лістенер, який буде реагувати так, як ми захочемо.
Перевага стилю більш помітна в сучасних веб- і мобільних додатках, які працюють з великою кількістю різноманітних UI-подій. Десять років тому вся взаємодія з веб-сторінкою зводилася до відправки великих форм на сервер і виконанні простого рендеринга клієнтської частини. Зараз додатки складніші: зміна одного поля може спричинити за собою автоматичне збереження даних на сервері. Наприклад інформація про новий «лайк» повинна відправитися іншим підключеним користувачам.
Реактивне програмування дуже добре підходить для обробки великої кількості різноманітних подій, багатопотоковості.
Студенти нашої кафедри також надають перевагу реактивному стилю програмування. Починаючи з третього курсу вони вивчають ReactNative – фреймворк для створення кроссплатормних додатків мовою JavaScript. Проекти та курсові роботи включають в себе створення програмних додатків у реактивному стилі. Прикладом є створення додатку – аналогу Telegram з використанням технології Redux. Redux – відкрита JavaScript бібліотека призначена для управління станом програми, яка найчастіше використовується з ReactNative.
Анна Шевчук, студент кафедри АПЕПС, ТЕФ