Поэтому, оглядываясь назад, я заметил несколько комментариев о том, что длинные методы являются плохой практикой.
Я не уверен, что всегда согласен с тем, что длинные методы плохие (и хотел бы узнать мнение других).
Например, у меня есть несколько представлений Django, которые выполняют небольшую обработку объектов перед отправкой их в представление, длинный метод состоит из 350 строк кода. Мой код написан так, чтобы он работал с параметрами - сортировка / фильтрация набора запросов, затем по крупицам выполняется некоторая обработка объектов, возвращенных моим запросом.
Таким образом, обработка в основном является условной агрегацией, которая имеет достаточно сложные правила, которые не могут быть легко выполнены в базе данных, поэтому у меня есть некоторые переменные, объявленные вне основного цикла, которые затем изменяются во время цикла.
variable_1 = 0
variable_2 = 0
for object in queryset :
if object.condition_condition_a and variable_2 > 0 :
variable 1+= 1
.....
...
.
more conditions to alter the variables
return queryset, and context
Таким образом, в соответствии с теорией, я должен разложить весь код на более мелкие методы, чтобы у меня был метод view с максимальной длиной в одну страницу.
Однако, работая в прошлом с различными базами кода, я иногда обнаруживаю, что он делает код менее читабельным, когда вам нужно постоянно переходить от одного метода к другому, выясняя все его части, сохраняя при этом самый внешний метод в своей голове.
Я обнаружил, что с длинным методом, который хорошо отформатирован, вы можете легче увидеть логику, так как она не скрывается во внутренних методах.
Я мог бы разделить код на более мелкие методы, но часто есть внутренний цикл, используемый для двух или трех вещей, так что это приведет к более сложному коду или методам, которые не выполняют одну вещь, а две или три (альтернативно Я мог бы повторить внутренние циклы для каждой задачи, но тогда будет падение производительности).
Так есть ли случаи, когда длинные методы не всегда плохи? Всегда ли есть случаи написания методов, когда они будут использоваться только в одном месте?
ОБНОВЛЕНИЕ: Похоже, я задавал этот вопрос более года назад.
Поэтому я реорганизовал код после (смешанного) ответа, разделив его на методы. Это приложение Django, которое извлекает сложные наборы связанных объектов из базы данных, поэтому аргумент тестирования отсутствует (вероятно, потребовалось бы большую часть года для создания соответствующих объектов для тестовых случаев. У меня есть тип «это нужно сделать вчера») рабочая среда, прежде чем кто-либо жалуется). Исправлять ошибки в этой части кода теперь немного легче, но не так массово.
до :
#comment 1
bit of (uncomplicated) code 1a
bit of code 2a
#comment 2
bit of code 2a
bit of code 2b
bit of code 2c
#comment 3
bit of code 3
в настоящее время:
method_call_1
method_call_2
method_call_3
def method_1
bit of (uncomplicated) code 1a
bit of code 2a
def method_2
bit of code 2a
bit of code 2b
bit of code 2c
def method_3
bit of code 3
источник
Ответы:
Нет, длинные методы не всегда плохи.
В книге Code Complete измеряется, что длинные методы иногда быстрее и проще в написании, и не приводят к проблемам обслуживания.
На самом деле, что действительно важно, так это оставаться сухим и уважать разделение интересов. Иногда вычисление просто долго писать, но в будущем оно не вызовет проблем.
Тем не менее, исходя из моего личного опыта, в большинстве длительных методов, как правило, отсутствует разделение интересов. На самом деле, длинные методы - это простой способ обнаружить, что что-то МОЖЕТ быть неправильным в коде, и это требует особой осторожности при проверке кода.
РЕДАКТИРОВАТЬ: как комментарии сделаны, я добавляю интересный момент в ответ. На самом деле я бы также проверил метрики сложности для функции (NPATH, цикломатическая сложность или даже лучше CRAP).
На самом деле, я рекомендую не проверять такие метрики в длинных функциях, но включать оповещение о них с помощью автоматических инструментов (таких как checkstyle для java, например) НА КАЖДУЮ ФУНКЦИЮ.
источник
Кажется, что основное внимание здесь уделяется слову всегда . Да, абсолюты плохие, а разработка программного обеспечения - это почти столько же искусство, сколько наука, и все такое ... но я должен сказать, что для приведенного вами примера метод был бы лучше, если бы он был разделен вверх. Вот аргументы, которые я обычно использую для обоснования разделения вашего метода:
Читаемость: я не уверен насчет других, но я не могу быстро прочитать 350 строк кода. Да, если это мой собственный код, и я могу сделать много предположений по этому поводу, я мог бы просмотреть его очень быстро, но это не главное. Подумайте, насколько проще было бы прочитать этот метод, если бы он состоял из 10 вызовов других методов (каждый с описательным именем). Делая это, вы ввели код в многоуровневую структуру, а высокоуровневый метод дает читателю краткую и приятную информацию о том, что происходит.
Изменить - чтобы представить это в другом свете, подумайте об этом так: как бы вы объяснили этот метод новому члену команды? Конечно, у него есть некоторая структура, которую вы можете суммировать следующим образом: «Ну, он начинает с A, затем B, затем иногда C и т. Д.». Короткий «обзорный» метод, вызывающий другие методы, делает эту структуру очевидной. Чрезвычайно редко можно найти 350 строк кода, которые не приносят пользы; человеческий мозг не предназначен для того, чтобы иметь дело со списками сотен предметов, мы группируем их.
Возможность повторного использования: длинные методы, как правило, имеют низкую когезию - они часто делают больше, чем одно. Низкая сплоченность - враг повторного использования; если вы объедините несколько задач в один метод, он будет использоваться повторно в меньшем количестве мест, чем следовало бы.
Тестируемость и сплоченность: я упомянул цикломатическую сложность в комментарии выше - это довольно хорошая мера того, насколько сложен ваш метод. Он представляет нижнюю границу числа уникальных путей через ваш код в зависимости от входных данных (правка: исправлено согласно комментарию MichaelT). Это также означает, что для правильного тестирования вашего метода вам нужно иметь как минимум столько тест-кейсов, сколько у вас число цикломатической сложности. К сожалению, когда вы собираете куски кода, которые на самом деле не зависят друг от друга, невозможно быть уверенным в этом отсутствии зависимости, а сложность имеет тенденцию к умножению. Вы можете думать об этой мере как о количестве разных вещей, которые вы пытаетесь сделать. Если оно слишком высоко, пора делиться и побеждать.
Рефакторинг и структура: длинные методы часто являются признаками отсутствия какой-либо структуры в коде. Часто разработчик не мог понять, что общего между различными частями этого метода, и где можно провести черту между ними. Понимание того, что длинный метод - это проблема, и попытка разбить его на более мелкие методы - это первый шаг на более долгом пути к фактической идентификации лучшей структуры для всего этого. Возможно, вам нужно создать класс или два; это не обязательно будет более сложным в конце!
Я также думаю, что в этом случае оправданием для использования метода long является «... некоторые переменные, объявленные вне основного цикла, затем изменяются во время цикла». Я не эксперт по Python, но я уверен, что эта проблема может быть тривиально решена путем передачи по ссылке.
источник
someVar.toString()
и чувствовали Вам нужно было увидеть код toString, чтобы знать, что он делает? Вы только что прочитали прямо из-за хорошего названия метода.Длинные методы всегда плохи, но иногда лучше, чем альтернативы.
источник
Длинные методы - это запах кода . Они обычно указывают, что что-то не так, но это не жесткое правило. Как правило, случаи, когда они оправданы, включают множество государственных и довольно сложных бизнес-правил (как вы уже нашли).
Что касается вашего другого вопроса, часто полезно разделить куски логики на отдельные методы, даже если они вызываются только один раз. Это облегчает понимание логики высокого уровня и может сделать обработку исключений немного чище. Так же, как вам не нужно передавать двадцать параметров для представления состояния обработки!
источник
Длинные методы не всегда плохи. Они обычно являются признаком того, что может быть проблема.
В системе, над которой я работаю, у нас есть с полдюжины или около того методов длиной более 10000 строк. Одна из них в настоящее время 54 830 строк. И это нормально.
Эти смехотворно длинные функции очень просты и генерируются автоматически. Этот большой монстр длиной 54 830 строк содержит ежедневные данные о полярном движении с 1 января 1962 года по 10 января 2012 года (наш последний выпуск). Мы также выпустили процедуру, с помощью которой наши пользователи могут обновить этот автоматически созданный файл. Этот исходный файл содержит данные о полярном движении из http://data.iers.org/products/214/14443/orig/eopc04_08_IAU2000.62-now , автоматически переведенные на C ++.
Чтение этого веб-сайта на лету невозможно при безопасной установке. Нет связи с внешним миром. Загрузка веб-сайта в качестве локальной копии и синтаксический анализ в C ++ также не возможны; Парсинг идет медленно , и это должно быть быстро. Загрузка, автоматический перевод на C ++ и компиляция: теперь у вас есть что-то быстрое. (Только не компилируйте его оптимизированным. Удивительно, сколько времени оптимизирующему компилятору требуется для компиляции 50 000 строк чрезвычайно простого кода с прямыми строками. На моем компьютере уходит более получаса, чтобы скомпилировать этот оптимизированный файл. И оптимизация абсолютно ничего не дает Оптимизировать нечего. Это простой прямой код, один оператор присваивания за другим.)
источник
Скажем так, есть хорошие и плохие способы разбить длинный метод. Необходимость «[держать] самый дальний метод в вашей голове» является признаком того, что вы не разбиваете его наиболее оптимальным образом или что ваши подметоды плохо названы. Теоретически, есть случаи, когда длинный метод лучше. На практике это крайне редко. Если вы не можете понять, как сделать более короткий метод читабельным, попросите кого-нибудь просмотреть ваш код и спросить его конкретно о том, как сократить методы.
Что касается нескольких циклов, вызывающих предполагаемое снижение производительности, то нет способа узнать это без измерения. Несколько меньших циклов могут быть значительно быстрее, если это означает, что все необходимое может оставаться в кэше. Даже если производительность падает, она обычно ничтожна в пользу читабельности.
Я скажу, что часто длинные методы легче писать , хотя их сложнее читать . Вот почему они размножаются, хотя никто не любит их. Нет ничего плохого в планировании с самого начала до рефакторинга, прежде чем вы его зарегистрируете.
источник
Длинные методы могут быть более вычислительными и компактными, легче видеть логику и легче отлаживать их. Однако эти правила действительно применяются только тогда, когда только один программист касается этого кода. Расширение кода будет затруднительно, если он не является атомарным, по сути, следующему человеку придется начинать с нуля, а затем отладка и тестирование будут длиться вечно, поскольку он не использует какой-либо известный хороший код.
источник
Есть кое-что, что мы называем функциональной декомпозицией, подразумевающей разбиение ваших более длинных методов на более мелкие, где это возможно. Поскольку вы упомянули, что ваш метод включает в себя сортировку / фильтрацию, то вам лучше иметь отдельные методы или функции для этих задач.
Точно, ваш метод должен быть сосредоточен только на выполнении 1 задачи.
И если по какой-то причине необходимо вызвать другой метод, то в противном случае продолжайте работу с тем, который вы уже пишете. Также для удобства чтения вы можете добавлять комментарии. Традиционно программисты используют многострочные комментарии (/ ** / в C, C ++, C # и Java) для описания методов и используют однострочные комментарии (// в C, C ++, C # и Java). Есть также хорошие инструменты документирования, доступные для лучшей читаемости кода (например, JavaDoc ). Вы также можете посмотреть комментарии на основе XML, если вы являетесь разработчиком .Net.
Циклы влияют на производительность программы и могут вызвать перегрузку приложения, если не используются должным образом. Идея состоит в том, чтобы спроектировать ваш алгоритм так, чтобы вы использовали как можно меньше вложенных циклов.
источник
Это нормально писать длинные функции. Но это зависит от контекста, действительно ли вам это нужно или нет. Например, некоторые из лучших algorthms выражены лучше всего, когда это peice. С другой стороны, большой процент подпрограмм в объектно-ориентированных программах будет подпрограммами доступа, которые будут очень короткими. Некоторые из длинных процедур обработки, которые имеют длительные случаи переключения, если условия могут быть оптимизированы с помощью табличных методов.
В Code Complete 2 есть отличная короткая дискуссия о длине подпрограмм.
источник
Еще один голос, что это почти всегда неправильно. Я нахожу два основных случая, когда это правильный ответ:
1) Метод, который в основном просто вызывает кучу других методов и сам по себе не работает. У вас есть процесс, который выполняет 50 шагов, вы получаете метод с 50 вызовами в нем. Обычно ничего нельзя получить, пытаясь это сломать.
2) Диспетчеры. Конструкция ООП избавилась от большинства таких методов, но входящие источники данных по своей природе являются просто данными и поэтому не могут следовать принципам ООП. В коде, который обрабатывает данные, нет ничего необычного в том, чтобы иметь какую-то диспетчерскую подпрограмму.
Я также сказал бы, что не следует даже рассматривать вопрос при работе с автоматически сгенерированным материалом. Никто не пытается понять, что делает автоматически сгенерированный код, не имеет значения ни на йоту, легко ли это понять человеку.
источник
Я хотел бы привести пример, который вы привели:
В моей компании наш самый большой проект построен на Django, и у нас также есть функции длинного просмотра (многие из них более 350 строк) Я бы сказал, что наши не должны быть такими длинными, и они причиняют нам боль.
Эти функции представления выполняют много свободно связанной работы, которая должна быть извлечена в модель, вспомогательные классы или вспомогательные функции. Кроме того, мы заканчиваем тем, что повторно используем представления, чтобы делать разные вещи, которые вместо этого должны быть разделены на более связные представления.
Я подозреваю, что ваши взгляды имеют схожие характеристики. В моем случае я знаю, что это вызывает проблемы, и я работаю, чтобы внести изменения. Если вы не согласны с тем, что это вызывает проблемы, вам не нужно это исправлять.
источник
Я не знаю, упоминал ли кто-то об этом, но одна из причин, почему длинные методы плохие, заключается в том, что они обычно включают несколько разных уровней абстракции. У вас есть переменные цикла и все виды вещей, происходящих. Рассмотрим фиктивную функцию:
Если бы вы выполняли всю анимацию, вычисления, доступ к данным и т. Д. В этой функции, это было бы беспорядком. Функция nextSlide () поддерживает согласованный уровень абстракции (система состояний слайдов) и игнорирует другие. Это делает код читабельным.
Если вам нужно постоянно переходить к более мелким методам, чтобы увидеть, что они делают, то упражнение по разделению функции провалилось. То, что код, который вы читаете, не делает очевидных вещей в дочерних методах, не означает, что дочерние методы - плохая идея, просто то, что это было сделано неправильно.
Когда я создаю методы, я обычно делю их на более мелкие методы, как своего рода стратегия «разделяй и властвуй». Метод как
конечно более читабельно, чем
Правильно?
Я согласен с тем, что абсолютные утверждения плохи, а также согласен, что обычно длинный метод плох.
источник
Правдивая история. Однажды я столкнулся с методом длиной более двух тысяч строк. Метод имел регионы, которые описывали, что он делал в этих регионах. Прочитав регион, я решил использовать метод автоматического извлечения, назвав его в соответствии с названием региона. к тому времени, как я закончил, метод представлял собой не что иное, как 40 вызовов метода по пятьдесят строк в каждой, и все они работали одинаково.
То, что слишком велико, субъективно. Иногда метод не может быть разбит дальше, чем он есть в настоящее время. Это похоже на написание книги. Большинство людей согласны с тем, что длинные абзацы обычно должны быть разделены. Но иногда есть только одна идея, и ее разделение вызывает больше путаницы, чем было бы вызвано длиной этого абзаца.
источник
Суть метода заключается в том, чтобы помочь уменьшить регургитацию кода. У метода должна быть определенная функция, за которую он отвечает. Если в конечном итоге вы перефразируете код во многих местах, вы рискуете получить неожиданный результат, если спецификации программного обеспечения будут изменены.
Для метода, имеющего 350 строк, можно предположить, что многие выполняемые им задачи реплицируются в других местах, поскольку для выполнения специализированной задачи необычно требовать такого большого количества кода.
источник
На самом деле плохие методы - это не длинные методы, а скорее плохие методы их применения.
Я имею в виду, что фактический акт рефакторинга вашего образца из:
в
а затем
Теперь вы находитесь на пути к не только более короткому, но и более понятному методу.
источник
Я думаю, что факт, что метод очень длинный, это то, что нужно проверить, но определенно не мгновенный анти-паттерн. Большая вещь, которую нужно искать в огромных методах - много вложений. Если у вас есть
и тело цикла не очень локализовано (т. е. вы можете отправить его менее чем с 4 параметрами), тогда, вероятно, лучше преобразовать его в:
В свою очередь, это может значительно сократить продолжительность функции. Кроме того, убедитесь, что вы нашли дублирующийся код в функции и перенесите его в отдельную функцию.
Наконец, некоторые методы просто длинные и сложные, и вы ничего не можете сделать. Некоторые проблемы нуждаются в решениях, которые не поддаются легкому кодированию. Например, синтаксический анализ грамматики большой сложности может привести к созданию очень длинных методов, с которыми вы действительно не сможете ничего поделать, не сделав его хуже.
источник
Правда, это зависит. Как уже упоминалось, если код не разделяет проблемы и пытается сделать все в одном методе, то это проблема. Разделение кода на несколько модулей облегчает чтение кода, а также написание кода (несколькими программистами). Для начала неплохо придерживаться одного модуля (класса) на исходный файл.
Во-вторых, когда дело доходит до функций / процедур:
хороший метод, если он проверяет диапазон только «данных». Это плохой метод, когда один и тот же диапазон применяется для нескольких функций (пример плохого кода):
Это должно быть изменено на:
Повторно используйте код как можно больше. И это возможно, когда каждая функция вашей программы достаточно ПРОСТА (не обязательно проста).
Цитата: Гора состоит из крошечных зерен земли. Океан состоит из крошечных капель воды .. (- Шивананда)
источник
Длинные методы имеют тенденцию к «плохому» в императивных языках, которые предпочитают операторы, побочные эффекты и изменчивость именно потому, что эти функции увеличивают сложность и, следовательно, ошибки.
В функциональных языках программирования, которые предпочитают выражения, чистоту и неизменность, меньше причин для беспокойства.
Как в функциональных, так и в императивных языках всегда лучше выделить многократно используемые фрагменты кода в общие подпрограммы верхнего уровня, но в функциональных языках, которые поддерживают лексическую область видимости с вложенными функциями и т. Д., На самом деле лучше скрыть подпрограммы в верхних подпрограммах. -уровневая функция (метод), чем разбивать их на другие функции верхнего уровня.
источник