Практические советы о том, как улучшить функции.
Мне посчастливилось работать с некоторыми выдающимися умами в этой отрасли. Однажды я подружился с блестящим Java-разработчиком, и мы потратили приличное количество обедов, обсуждая шаблоны и подходы в программировании. В то время я работал над схемой для валидации форм в моем проекте, который требовал много функциональной тяжелой работы. Каждый раз, когда мы обсуждали конкретную проблему, меня восхищало его подход к функциональному дизайну. В процессе работы я перенял один из его способов мышления, и с тех пор он изменил мое понимание функций.
Если у вас есть опыт работы в области функционального программирования, то, что я собираюсь сказать, может не быть для вас новинкой. Однако я знаю, что есть разработчики, которые не привыкли к строгим типам, и нотации Хиндли-Милнера - не совсем первое, что приходит в голову, когда они думают о функции. Я обращаюсь к этим людям с этой статьей.
Шаблон ввода / вывода
Мы знаем, что функция принимает аргументы и возвращает значение. Что мы иногда упускаем из виду, так это то, что эта характеристика является невероятно мощным шаблоном проектирования, который мы можем использовать для написания более совершенных функций.
Попробуйте написать любую функцию, сначала ответив на эти два вопроса:
- Что вводит эта функция (какие аргументы она принимает)?
- Что дает эта функция (какие данные она возвращает)?
Это позволяет отложить технические детали в сторону и сосредоточиться на функции как на операции ввода / вывода, что на самом деле так и есть. Ответы, которые вы даете, могут указывать на некоторые детали реализации, но, что наиболее важно, они определяют четкие ограничения на обязанности функции задолго до того, как будет написан реальный код.
Вы можете использовать абстрактные типы для своих ответов. Например, функция может принимать список яблок и возвращать счастливую лису. Такие абстракции типов еще больше отделяют сигнатуру вызова от реализации.
Давайте применим это на практике. Скажем, вам нужно написать функцию, которая проверяет поле формы. Есть множество вещей, которые могут повлиять на проверку поля, но вы можете оставить их в стороне и сначала ответить на вопросы ввода / вывода:
- Моя функция принимает поле;
- Моя функция возвращает вердикт проверки (
boolean
).
Записанные ответы представляют собой сигнатуру вызова функции:
Вы можете не знать, что это за тип Field
на данный момент, но вы знаете, что он представляет. То же самое можно сказать и о функции validate
в целом: независимо от того, какие факторы влияют на достоверность поля, в конце концов вы должны принять логический вердикт. Определенные входные и выходные данные действуют как ограничение, которое не позволяет нашей функции стать слишком умной во время разработки, определяемой сроками. Это гарантирует, что написанная нами логика лежит в сфере ответственности функции и остается простой, удовлетворяя как единую ответственность, так и принципы KISS.
Принятие этого шаблона не означает, что вы должны немедленно переписать свой код на TypeScript или на любом другом строго типизированном языке. Прежде всего, это способ думать о своих функциях. Вполне нормально записывать ввод и вывод в блоке JSDoc или в альбоме для рисования. Начните с изменения своего мышления, и инструменты последуют за ним.
Подобно тому, как вы ставите потребности пользователя на первое место, прежде чем принимать правильные решения UX, вы думаете о том, какие данные ваша функция принимает и возвращает, чтобы установить границы ее будущей реализации.
Шаблон в масштабе
Думать о функциях с использованием этого шаблона - здорово, но как насчет реальных операций, которые часто состоят из нескольких функций и представляют более сложную логику?
По правде говоря, даже самую сложную функцию можно записать как набор последовательно выполняемых меньших функций. Когда вы подходите к задаче таким образом, вы можете сосредоточиться на разработке каждой отдельной функции за раз. Однако держать функции изолированными друг от друга опасно, так как в итоге вы можете получить несколько частей головоломки, которые не подходят друг к другу. Чтобы избежать этой проблемы, существует правило функционального состава:
Две функции являются составными, если выход одной из них может служить входом для другой.
Зная это, давайте реализуем довольно сложную операцию, которая принимает пользователя и возвращает количество лайков под всеми его сообщениями. Чтобы не усложнять, мы можем описать эту операцию как последовательность шагов:
Получите пользователя → получите сообщения пользователя → получите количество лайков за публикацию
Каждый из этих шагов является функцией, и мы можем применить шаблон ввода / вывода для разработки его сигнатуры вызова с учетом принципа композиции.
Такой высокоуровневый обзор функциональной цепочки позволяет вам следить за потоком данных, не отвлекаясь, и выделять потенциальные проблемы в логике. Более того, это просто забавное упражнение.
В конце концов, функции предназначены для преобразования данных, поэтому используйте все доступные средства, чтобы преобразование было согласованным и эффективным.
Еще кое-что!
Есть еще одна скрытая жемчужина подхода ввода / вывода. Допустим, вы отвечаете на эти основные вопросы фразой «моя функция принимает список строк и возвращает число». Поздравляем, вы только что написали модульный тест для своей функции!
Результат этого шаблона может быть отражен в сценарии тестирования, подкрепляя решения по проектированию функций фактическим модульным тестом. Это способствует развитию TDD (разработка, управляемая тестированием) и BDD (разработка, управляемая поведением), поскольку мы выражаем намерение нашей функции, описывая ее ввод и вывод.
Резюме
Сосредоточение внимания на типах ввода и вывода функции определяет краткую спецификацию этой функции:
- Подпись вызова функции;
- Минимальный модульный тест для этой функции.
Реализация вещей в соответствии со спецификацией - приятное и безопасное занятие, к которому я настоятельно рекомендую вам привыкнуть.
Послесловие
Надеюсь, этот шаблон поможет вам в следующий раз, когда вы решите написать функцию. Дайте мне знать, если вы хотите узнать больше о практических шаблонах и подходах к функциональному дизайну, лайкнув этот пост и дайте мне знать в комментариях.
Подпишитесь на меня в Twitter, чтобы поговорить о функциях или программировании в целом. Буду рад услышать ваше мнение и ответить на ваши вопросы.