Мне всегда было интересно, что другие программисты думают об идее создания чисто эстетических функций.
Скажем , у меня есть функция , которая обрабатывает кусок данных: Function ProcessBigData
. Скажем , мне нужно несколько шагов процесса, действительны только для этих данных: Step1
, Step2
, Step3
.
Обычный подход, который я вижу больше всего в исходном коде, - это писать комментарии примерно так:
Function ProcessBigData:
# Does Step1
Step1..
Step1..
#Does Step2
Step2..
Step2..
Что я обычно делаю, но всегда чувствовал себя неправильно из-за отсутствия такого стиля кодирования со стороны коллег:
Function ProcessBigData:
Function Step1:
Step1..
Step1..
Function Step2:
Step2..
Step2..
Step1() -> Step2()
Я в основном обеспокоен, есть ли недостатки такого стиля в Javascript и Python
Есть ли альтернативы, которых я не вижу?
javascript
python
coding-style
Slytael
источник
источник
Ответы:
Это не так странно, как вы думаете. Например, в Standard ML принято ограничивать область действия вспомогательных функций. Конечно, SML имеет синтаксис для облегчения этого:
Я хотел бы рассмотреть этот хороший стиль, учитывая, что 1) маленькие функции облегчают рассуждения о программе, и 2) он сигнализирует читателю, что эти функции не используются вне этой области.
Я предполагаю, что возможно создание некоторых внутренних функций при каждом вызове внешней функции (я не знаю, оптимизируют ли это JS или Python), но вы знаете, что они говорят о преждевременной оптимизации.
источник
Обычно это хорошо делать, когда это возможно, но мне нравится думать о такой работе не как о «шагах», а как о подзадачах .
Подзадача - это конкретная единица работы, которая может быть выполнена: она несет определенную ответственность, а также определенные входные данные и выходные данные (подумайте о «S» в SOLID ). Подзадача не нуждается в повторном использовании: некоторые люди склонны думать: «Мне никогда не придется вызывать это из чего-либо еще, так зачем писать это как функцию?» но это заблуждение
Я постараюсь также обрисовать преимущества, а также то, как это применяется к вложенным функциям (замыканиям) по сравнению с просто еще одной функцией в классе. Вообще говоря, я бы рекомендовал не использовать замыкания, если они вам не нужны (их много, но разделение кода на логические блоки не входит в их число).
Читаемость.
Более 200 строк процедурного кода (тела функции) трудно читать. Функции 2-20 строк легко читаются. Код для людей.
Вложенные или нет, вы в основном получаете преимущество читабельности, если вы не используете много переменных из родительской области, в этом случае это может быть так же трудно читать.
Предел переменной области
Наличие другой функции заставляет вас ограничивать область видимости переменной и, в частности, передавать то, что вам нужно.
Это часто также делает вашу структуру лучше, потому что если вам нужна какая-то переменная состояния из более раннего «шага», вы можете обнаружить, что на самом деле есть другая подзадача, которая должна быть написана и выполнена первой, чтобы получить это значение. Или, другими словами, это затрудняет написание сильно связанных кусков кода.
Наличие вложенных функций позволяет обращаться к переменным в родительской области изнутри вложенной функции (замыкание). Это может быть очень полезно, но также может привести к тонким, трудно обнаруживаемым ошибкам, поскольку выполнение функции может не происходить так, как написано. Это даже больше в случае, если вы изменяете переменные в родительской области (вообще очень плохая идея).
Модульные тесты
Каждая подзадача, реализованная функцией (или даже классом), является отдельным, тестируемым фрагментом кода. Преимущества модульного тестирования и TDD хорошо документированы в других местах.
Использование вложенных функций / замыканий не позволяет выполнять модульное тестирование. Для меня это нарушение условий и причина, по которой вы должны просто выполнять другую функцию, если нет особой необходимости закрытия.
Работа в команде / Дизайн сверху вниз
Подзадачи могут быть написаны разными людьми, независимо, при необходимости.
Даже при написании кода может быть полезно просто вызвать некоторую подзадачу, которая еще не существует, при создании основной функциональности и заботиться о фактической реализации подзадачи только после того, как вы узнаете, что она получит необходимые вам результаты в осмысленно. Это также называется нисходящим дизайном / программированием.
Повторное использование кода
Хорошо, так что, несмотря на то, что я сказал ранее, иногда на самом деле в конечном итоге возникает причина для повторного использования подзадачи для чего-то другого. Я вовсе не сторонник "архитектурного астронавта", а просто потому, что при написании слабосвязанного кода вы можете в конечном итоге получить выгоду от повторного использования.
Часто такое повторное использование означает некоторый рефакторинг, что вполне ожидаемо, но рефакторинг входных параметров для небольшой автономной функции НАМНОГО проще, чем извлечение ее из 200+ строковых функций через месяцы после ее написания, что на самом деле является моей точкой зрения здесь.
Если вы используете вложенную функцию, то ее повторное использование, как правило, в любом случае является вопросом рефакторинга на отдельную функцию, и поэтому я бы сказал, что вложенная функция - это не тот путь.
источник