Функциональный стиль в java: когда он нужен?
Большинство задач в энтерпрайзе делятся на две группы:
1️⃣ Одиночный запрос.
Покупка в интернет-магазине, вывод курса доллара — такие запросы затрагивают мало данных и много компонентов. Эти бизнес-процессы хорошо описываются объектами и отношениями между ними.
2️⃣ Обработка данных.
Статистика покупок, поиск рекомендаций, прогноз погоды - в центре внимания обработка данных, а не взаимодействие сущностей. Задача разработчика — чтобы обработка была быстрой. Нужно по максимуму использовать ресурсы процессора и доступную память, снизить время простоя и количество блокировок.
Вот здесь пригодятся некоторые подходы функционального программирования(ФП) :
1️⃣ Понятие чистой функции.
В многопоточном программировании главная проблема — работа с общими переменными. Чтобы безопасно их обновлять и всегда читать актуальные знвчения используются средства синхронизации. Большинство из них снижают общую пропускную способность.
Чистые функции - методы, которые характеризуются только входными и выходными данными. У них нет сайд-эффектов, Они не меняют внешних объектов и не бросают эксепшены. Для одинаковых входных данных результат всегда будет один и тот же.
✅ Нет общих переменных — нет синхронизации.
✅ Можно кэшировать некоторые результаты.
2️⃣ Неизменяемые данные
Исходные данные не должны меняться. Меняться могут только локальные переменные.
✅ Синхронизация не нужна — общие переменные не меняются и всегда актуальны.
Пример: добавление элемента в список.
Подход ООП: добавляем в исходный список - меняем внешний объект.
❌ Для запуска в многопоточной среде нужна синхронизация.
❌ Если метод прервался, то нужно выяснять, что он успел изменить, а что нет.
Подход ФП: создаём новый список на основе исходного и добавляем туда элемент.
✅ Если что-то пошло не так, просто ещё раз вызываем метод.
❌ Большой расход памяти.
3️⃣ Декларативный стиль написания кода.
Обход коллекции в цикле — это императивный подход. Для каждого элемента задаётся чёткая последовательность действий.
Stream API — декларативный. Благодаря большому количеству встроенных функций можно просто описать то, что нужно: отфильтровать, сгруппировать, просуммировать.
Лучшая читаемость - не единственное преимущество такого подхода. Чистые функции и неизменяемые данные сами по себе тоже не сделают программу эффективной. Но их использование в Stream API дают компилятору и JVM большие возможности для оптимизаций. Cамая мощная из них — опция parallel(), она автоматически распределяет данные по параллельным подзадачам.
Начиная с 8 версии java можно применять и другие идеи ФП — функции высших порядков, currying, pattern matching и т.д. Некоторые подходы были доступны и раньше, например, использовать хвостовую рекурсию вместо итерации. Но наибольшую практическую пользу можно извлечь из обработки больших коллекций с помощью Stream API.
Писать программы в функциональном стиле на чистой java можно, но на практике это пока не оптимально. Для обработки потоков данных больше подходят библиотеки для реактивного программирования и стрим-фреймворки.
Большинство задач в энтерпрайзе делятся на две группы:
1️⃣ Одиночный запрос.
Покупка в интернет-магазине, вывод курса доллара — такие запросы затрагивают мало данных и много компонентов. Эти бизнес-процессы хорошо описываются объектами и отношениями между ними.
2️⃣ Обработка данных.
Статистика покупок, поиск рекомендаций, прогноз погоды - в центре внимания обработка данных, а не взаимодействие сущностей. Задача разработчика — чтобы обработка была быстрой. Нужно по максимуму использовать ресурсы процессора и доступную память, снизить время простоя и количество блокировок.
Вот здесь пригодятся некоторые подходы функционального программирования(ФП) :
1️⃣ Понятие чистой функции.
В многопоточном программировании главная проблема — работа с общими переменными. Чтобы безопасно их обновлять и всегда читать актуальные знвчения используются средства синхронизации. Большинство из них снижают общую пропускную способность.
Чистые функции - методы, которые характеризуются только входными и выходными данными. У них нет сайд-эффектов, Они не меняют внешних объектов и не бросают эксепшены. Для одинаковых входных данных результат всегда будет один и тот же.
✅ Нет общих переменных — нет синхронизации.
✅ Можно кэшировать некоторые результаты.
2️⃣ Неизменяемые данные
Исходные данные не должны меняться. Меняться могут только локальные переменные.
✅ Синхронизация не нужна — общие переменные не меняются и всегда актуальны.
Пример: добавление элемента в список.
Подход ООП: добавляем в исходный список - меняем внешний объект.
public void add(List list, Integer value)
{ list.add(value); }
✅ Минимальный расход памяти.❌ Для запуска в многопоточной среде нужна синхронизация.
❌ Если метод прервался, то нужно выяснять, что он успел изменить, а что нет.
Подход ФП: создаём новый список на основе исходного и добавляем туда элемент.
public List add(List list, Integer value {
List newList=new List();
newList.addAll(list);
newList.add(value);
return list;
}
✅ Можно использовать в многопоточной среде.✅ Если что-то пошло не так, просто ещё раз вызываем метод.
❌ Большой расход памяти.
3️⃣ Декларативный стиль написания кода.
Обход коллекции в цикле — это императивный подход. Для каждого элемента задаётся чёткая последовательность действий.
Stream API — декларативный. Благодаря большому количеству встроенных функций можно просто описать то, что нужно: отфильтровать, сгруппировать, просуммировать.
Лучшая читаемость - не единственное преимущество такого подхода. Чистые функции и неизменяемые данные сами по себе тоже не сделают программу эффективной. Но их использование в Stream API дают компилятору и JVM большие возможности для оптимизаций. Cамая мощная из них — опция parallel(), она автоматически распределяет данные по параллельным подзадачам.
Начиная с 8 версии java можно применять и другие идеи ФП — функции высших порядков, currying, pattern matching и т.д. Некоторые подходы были доступны и раньше, например, использовать хвостовую рекурсию вместо итерации. Но наибольшую практическую пользу можно извлечь из обработки больших коллекций с помощью Stream API.
Писать программы в функциональном стиле на чистой java можно, но на практике это пока не оптимально. Для обработки потоков данных больше подходят библиотеки для реактивного программирования и стрим-фреймворки.