Что же такое функциональное программирование?
Возможно, вы слышали о функциональном программировании (ФП) и как сильно оно снижает LOC (строк кода - Lines Of Code) и улучшает читабельность кода. Но что в действительности значит программировать функционально и какие отличия от ООП.
- Все переменные final
- Не используйте глобальные пременные.
Я выбрал пример с глобальным временем. Вы пишете static функцию, которая возвращает текущее время строкой. ООП версия может выглядеть так:
public class Utils { private static Time time; public static String currTime() { return time.now().toString(); } }
Если вызвать currTime() дважды - результат будет разный, Хотя у нас один и тот же входной параметр(которого нет), результат разный.
Это не может произойти в ФП. Каждый метод зависит только от своих собственных параметров и все. Т.е. если мы хотим это реализовать, то объект time должен быть входным параметром:
public class Utils { public static String currTime(Time time) { return time.now().toString(); } }
Это может выглядеть странно для ООП, но у такого решения есть преимущества. Во-первых, такой код легче читать. Если вы знаете, что метод зависит только от своих параметров, вам не нужно искать глобальные паременные, которые добавляют магии в ваш метод. Во-вторых, такой когд лече тестировать.Когда вам нужно протестировать данный метод, вы можете просто замокировать объект Time. Для решения в ООП это сделать достаточно сложно. -
Используйте функции, как параметр.
У ФП функция может быть аргументом другой функции. Разве это не круто? Предположим, что нам необходимо дабавить каждому элементу List 1. Вот пример релизации в ООП:
public List<integer> addOne(List<integer> numbers) { List<integer> plusOne = new LinkedList<>(); for(Integer number : numbers) { plusOne.add(number + 1); } return plusOne; }
В итоге необходимо оперировать двумя списками. Что может привести к ошибкам. Так же существует шанс внести изменения в значения входного параметра, что может привести к дальнейшим проблемам. В ФП вы можете применить функцию map к каждому элементу списка и сохранить значение в другой список:public List<Integer> addOne(List<Integer> numbers) { return numbers .stream() .map(number -> number + 1) .collect(Collectors.toList()); }
Это уменьшает количество переменных и, следовательно, места, где вы можете допустить ошибки. Вы создаете новый список и не измените числа во входном списке.
public String greet(List<String> names) { String greeting = "Welcome "; for(String name : names) { greeting += name + " "; // We don't care about the last space right now. } greeting += "!"; return greeting; }
Это полностью валидная функция для создания строки приветствия в Java. Но если вы используете ФП, то такой способ не сработает. Вы изменяете состояние приветствия (greeting), что не разрешено в ФП. Если вы попытаетесь сделать final greeting, то получите ошибку. Всякий раз, когда вы используете += с String, значение меняется. Обычно в ФП вы конкатенируете все имена в одну строку:
public String greet(List<String> names) { final String reducedString = "Welcome " + names.get(0) + " " + names.get(1) + " " + ... + names.get(names.size()-1) + " "; return reducedString + "!"; }
Если это решение вам кажется ужасным - вы правы. Но ФП может все исправить:
public String greet(List<string> names) { String greeting = names .stream() .map(name -> name + " ") .reduce("Welcome ", (acc, name) -> acc + name); return greeting + "!"; }
Преимущество final переменных, что их значение всегда одинаковое. Это делает тестирование и дебаггинг намного проще.
Заключение.
PS это мой перевод данной статьи
Комментариев нет :
Отправить комментарий