Функциональный стиль для ваших Android приложений
Сегодня я вам расскажу, как можно использовать функциональное программирование, чтобы сделать разработку Android приложений приятнее, предсказуемее и проще, чем это принято в индустрии. Мы обсудим различные важные концепции, такие как
- что такое функциональный стиль
- какие мы можем выделить основополагающие черты
- как это может помочь в проектировании приложения
Также я дам вам рецепт того, как внедрить TEA в ваше приложение
Стиль построения программ, который рассматривает программу как вычисление математической функции и избегает побочных эффектов и изменения состояния
-- Wikipedia
Когда начинаешь программировать в функциональном стиле и на себе ощущать простоту идей, невольно задумываешься,
А потом я открыл как-то страничку на википедии и до меня дошло, в чём дело - если прочесть определение функционального стиля - то всё становится понятно - вот тебе
Наши программы сделаны для того чтобы иметь эффект на окружающий мир. Какой стартап не возьми - у всех в целях "мы хотим изменить мир к лучшему!" Но как вы собираетесь изменять мир к лучшему с функциональном программированием, если у него в определении указано, что фишка в том, чтобы ничего не менять. Сразу перед глазами всплывает программист - дзен - буддист, которые все таски закрывает как "не будет исправлено", потому что код прекрасен сам по себе и делать ничего полезного ему не нужно.
И тем не менее, функциональное программирование всё же на слуху, посмотрите только на количество книг, рассказывающих о приёмах функционального программирования на разных языках
И если на JavaScript, Python, Scala ещё можно представить функциональщину, то тут есть ещё Java, Objective-C и C++.
И если посмотреть например, на Manning, они то начиная с 2016 года они выпускают по нескольку книг по фп в год!
На самом деле, фп уже здесь
Всё таки в нём есть множество идей, которые уже давно вошли в мейнстрим языки. Сейчас жизнь мобильного дева сложно представить без любой из этих вещей.
Но мы по прежнему используем эти идеи только на маленьких масштабах Когда же возникает мысль "хм, это фп вроде неплохо работает - а почему бы не написать всё приложение с его использованием?" Мы сразу её отметаем, вспоминая об определении из википедии. Мы ведь не можем делать программу на основе концепции, вся суть которой в том чтобы избегать возможности что-либо делать?
Стиль построения программ, в котором код разделяют на три категории
Всем, кто раньше сталкивался с фп и у кого сформировалось мнение, что оно того не стоит, предлагаю перефразировать определение функционального стиля
В ФП можно прийти разными путями, и среди фп сообщества есть свои холивары по поводу того, каких программистов можно называть функциональщиками, каких - нет. И вообще, что есть Торт, а что - нет это вечный вопрос.
Но по моим ощущениям в чём то практики фп согласны - это в том, что функциональный стиль - это про то, как смотреть на код, который мы пишем
Давайте посмотрим на эти три категории внимательнее
Факты о моделируемой системе
Нам, программистам, не особо стоит объяснять что такое данные, но повторюсь:
Зачастую данные можно представить в виде key-value или списков. Мы привыкли, что "данные" обычно пользовательские, но они на самом деле повсюду и могут быть частью конфигурации либо частью логики
данными можно назвать как прилепленный на холодильник список покупок так и цвет кнопки, который вы передаёте в метод отрисовки GUI фреймворка
Продолжаю капитанить: данные можно сериализовать!
Но, на самом деле, если хорошенько порефлексировать, то это замечательное свойство данных, которое, как мы потом посмотрим, остутствует у других категорий кода. Данные находятся вне времени - если их хранилище достаточно живучее и у нас есть инструмент для их извлечения - мы можем хоть через 1000 лет за ними вернуть и с ними ничего не будет.
Данные - лучший вид абстракции, они абсолютно открыты для интерпретации Это значит что одни и те же данные можно интерпретировать как угодно например, можно список покупок использовать для того чтобы не забыть ничего в магазине а можно с помощью него же планировать бюджет
Но при этом, как и положено высочайшему уровню абстракции, данные бессмыслены сами по себе. Даже для того чтобы извлечь какой-то смысл из списка покупок, ваш мозг включает встроенный в него интерпретатор и производить из него другие данные, другие смыслы.
Чистые функции без обозримых сайд-эффектов
Вычисления, которые зависят от времени
Основной принцип функционального дизайна
После того, как я вам представил все три категории, можно вывести основной, на мой взгляд, принцип ФД. Стараемся использовать в коде как можно больше данных и вычислений. И контролируем, где и когда мы выполняем действия так как они единственные зависят от времени.
fun onClick(offerId: String) { // Действие api.getOffer(offerId) // Действие .doOnSubscribe { view.showLoading() } // Действие .subscribe { data -> view.hideLoading() // Действие view.display(data) // Действие } }
Если мы вооружимся этим подходом и взглянем на классический MVP принятый в андроиде с точки зрения функционального дизайна - о ужас! Тут сплошь и рядом одни действия! Не удивительно, что типичный конструктор презентера требует десятков моков в тестах
// Logic.kt fun accept(state: State, msg: Msg) = when(msg) { is OnOfferClick -> if (!state.isLoading) { api.getOffer(msg.offerId) // Действие .startWith { SetLoading } .map { UpdateWithData(it) } } else { Observable.just(SetLoading) } } // Reducer.kt fun state(state: State, update: Update): State = when(update) { // Вычисления is UpdateWithData -> state.copy(data = update.data, loading = false) is SetLoading -> state.copy(loading = true) }
// Reducer.kt fun update(state: State, msg: Msg): Pair<State, Cmd?> = when(msg) { is OnOfferClick -> { val cmd = if (!state.loading) Load(msg.offerId) else null state.copy(loading = true) to cmd } is NewDataArrived -> state.copy(data = update.data, loading = false) to null }
// EffectHandler.kt class CommandInterpreter(api: Api) { fun interpret(cmd: Command, listener: (Msg) -> Unit) = when(cmd) { is Load -> { val model = api.getOffer(eff.offerId) listener(NewDataArrived(model)) } } }
Тотальный контроль над эффектами и их исполнением
Так и зачем всё это? Для того чтобы достичь тотального контроля над эффектами. Вам больше неинтересно, какой у вас фреймворк для асинхронной работы. RxJava, корутины, просто треды - практически все они выглядят одинаково.
Вам больше не нужно писать моки в тестах - действия вашей системы и так являются данными, которые можно просто ассертать.
Вы можете одной строчкой встроить логирование всего происходящего для того чтобы выловить баг.
@Mishkun
@lambda61
И эти принципы лежат во многих функциональных библиотеках и фреймворках. В том числе и в основе The Elm Architecture, принятой в функциональном языке Elm, компилирующемся в JavaScript и позволяющем писать фронтенд в в красивом фп стиле.
Space | Forward |
---|---|
Right, Down, Page Down | Next slide |
Left, Up, Page Up | Previous slide |
G | Go to slide number |
P | Open presenter console |
H | Toggle this help |