вторник, 21 марта 2017 г.

Функциональное программирование в Java 8(Часть 0): Мотивация

Что же такое функциональное программирование?


Возможно, вы слышали о функциональном программировании (ФП) и как сильно оно снижает LOC (строк кода - Lines Of Code) и улучшает читабельность кода. Но что в действительности значит программировать функционально и какие отличия от ООП.
  1. Все переменные final

  2.  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 -&gt; name + " ")
                .reduce("Welcome ",
                        (acc, name) -> acc + name);
        return greeting + "!";
    }
    

    Преимущество final переменных, что их значение всегда одинаковое. Это делает тестирование и дебаггинг намного проще.
  3. Не используйте глобальные пременные.
    Я выбрал пример с глобальным временем. Вы пишете 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. Для решения в ООП это сделать достаточно сложно.
  4. Используйте функции, как параметр.
    У ФП функция может быть аргументом другой функции. Разве это не круто? Предположим, что нам необходимо дабавить каждому элементу List 1. Вот пример релизации в ООП:
    public List<integer> addOne(List<integer> numbers) {
        List<integer> plusOne = new LinkedList&lt;&gt;();
        for(Integer number : numbers) {
            plusOne.add(number + 1);
        }
        return plusOne;
    }
    
    
    В итоге необходимо оперировать двумя списками. Что может привести к ошибкам. Так же существует шанс внести изменения в значения входного параметра, что может привести к дальнейшим проблемам. В ФП вы можете применить функцию map к каждому элементу списка и сохранить значение в другой список:
    public List<Integer> addOne(List<Integer> numbers) {
        return numbers
                .stream()
                .map(number -&gt; number + 1)
                .collect(Collectors.toList());
    }
    
    
    Это уменьшает количество переменных и, следовательно, места, где вы можете допустить ошибки. Вы создаете новый список и не измените числа во входном списке.

Заключение.


Надеюсь, вы заметили преимущества ФП. Позже вы научитесь по-настоящему ценить их. Final переменные - удобнее с точки зрения многопоточности, отсутствие глобальных переменных повышает тестируемость и функции, поскольку параметры улучшают качество кода. И не беспокойтесь, вначале вы можете смешивать ООП и ФП в своем коде. В следующей части мы поговорим о функциях как параметрах. Так же я представлю анонимные функции (lambdas) и ссылки на методы. Надеюсь, вам понравилось это краткое введение в большую тему функционального программирования. Пожалуйста, напишите мне, если что-то непонятно. Или есть ли какая-нибудь причина ненавидеть функциональное программирование? Я хотел бы обсудить эти темы с вами.


PS это мой перевод данной статьи

Комментариев нет :

Отправить комментарий