Что доказано как хорошая максимальная длина функции? [закрыто]

44

Влияет ли длина функции на производительность программиста? Если да, то какое максимальное количество строк нужно избежать, чтобы избежать потери производительности?

Так как это очень мнительная тема, пожалуйста, подкрепите заявку некоторыми данными.

Peter Mortensen
источник
6
Длина должна измеряться не в LOC, а в количестве времени, необходимого для точного понимания того, что она делает. И эта длина должна быть не более минуты. Если я не могу понять это в течение нескольких секунд, это, вероятно, делает слишком много ... через минуту, это определенно так.
CaffGeek
13
Максимальная длина должна быть 17.
ThomasX
1
Думайте S в ТВЕРДОМ.
Крис Краузе
1
@CaffGeek Или, возможно, функция просто делает что-то нетривиальное. Я видел функции, которые потребовали бы у меня дней, чтобы полностью понять. Даже функции, в которых я понимаю все концепции, могут легко занять полчаса, чтобы проработать детали. Хотя неплохо иметь тривиальные функции, многие проблемы просто сложны.
CodesInChaos

Ответы:

46

С тех пор, как я начал эту сумасшедшую ракетку в 1970 году, я увидел ровно один модуль, который действительно должен был составлять более одной печатной страницы (около 60 строк). Я видел много модулей, которые были длиннее.

В связи с этим я написал модули, которые были длиннее, но обычно это были большие конечные автоматы, написанные как большие операторы switch.

Частично проблема заключается в том, что программистов в наши дни не учат модульности.

Стандарты кодирования, которые максимизируют трата вертикального пространства, также являются частью проблемы. (Мне еще предстоит встретиться с менеджером по программному обеспечению, который прочитал « Психологию компьютерного программирования » Джеральда Вайнберга . Вайнберг отмечает, что многочисленные исследования показали, что понимание программистом, по существу, ограничено тем, что программист может видеть в любой данный момент. программист должен прокрутить или перевернуть страницу, их понимание значительно падает: они должны помнить и абстрагироваться.)

Я по-прежнему убежден, что значительная часть хорошо документированных приростов производительности труда программистов от FORTH произошла благодаря «блочной» системе исходного кода FORTH: модули были жестко ограничены абсолютным максимумом в 16 строк по 64 символа. Вы могли бы учитывать бесконечно, но вы ни при каких обстоятельствах не могли написать подпрограмму из 17 строк.

John R. Strohm
источник
3
Вся философия FORTH была разработана для того, чтобы поощрять это ... Вы должны были создать свой собственный словарный запас, неуклонно разбивая вашу программу на более мелкие и мелкие кусочки, заканчиваясь меньшим количеством сценария и большим количеством словаря. Только ограничения длины не делают этого - вы увидите подпрограмму, разбитую на произвольные части просто для удовлетворения некоторого стандарта кодирования. Я думаю, что вы абсолютно правы, подозревая, что программистов просто «не учат модульности»; это должно было быть одной из главных побед ООП, но по разным причинам это часто преуменьшается как цель для себя.
Shog9
1
@Мистер. CRT: ограничения длины в блочно-ориентированных реализациях FORTH. FORCED modularization. Интерактивный характер большинства FORTH помогает, поощряя небольшие модули и быстрое тестирование этих модулей. DTH, основанный на файлах FORTH, не вызывал модульность, и я видел, как парни, играющие с D85, пишут много готовых модулей сознания программиста навсегда. Отсюда мое убеждение. (Что бы это ни стоило, Лиз Ратер не согласна со мной. Она думает, что в основном именно интерактивность дает FORTH повышение производительности.)
Джон Р. Штром
2
+1, чтобы представить мне замечательную книгу «Психология компьютерного программирования» :)
Самуил
30

Какой правильный размер, правда?

Зависит от языка, который вы используете, но в целом (и на мой личный вкус):

  • В идеале менее 25 строк.
  • Приемлемо менее 35 строк.

Если это больше, то это то, что мне нужно вернуться позже и переделать.

Но реально , любого размера она должна быть , когда вам нужно поставить что - то , и что это имеет смысл на данный момент , чтобы выплюнуть их , как это, делает его еще проще иногда для кого - то перед транспортировкой. (но все же вернемся к этому позже).

(Недавно моя команда запустила программу на нашей базе кода: мы нашли класс с 197 методами, а другой - только с 3 методами, но один из них был 600 строками. Симпатичная игра: что хуже из 2 зол?)


Теперь для более дзенского ответа ... Вообще считается хорошей практикой (ТМ) цитировать одного или двух великих людей, так что вот так:

Все должно быть сделано как можно проще, но не проще. - А. Эйнштейн

Совершенство, наконец, достигается не тогда, когда уже нечего добавить, а когда уже нечего убрать. - А. де Сент-Экзюпери


Приложение к стилю комментариев

В дополнение к этому ваши функции должны иметь четкие имена, объясняющие их назначение. Что касается комментариев, я обычно не комментирую внутри функции:

  • комментарии говорят "почему?" ,
  • код говорит "как?" ,

Блок комментариев в верхней части каждой функции (который требует объяснения) достаточно. Если ваша функция небольшая, а имена функций достаточно явные, вам просто нужно сказать, чего вы хотите достичь и почему. Я использую встроенные комментарии только для полей в некоторых языках или для запуска блоков для функций, которые нарушают правила строки 25-35, если намерение неясно. Я использую блочный комментарий внутри кода, когда возникают исключительные ситуации (например, блок catch, в котором вы не нуждаетесь или не хотите что-либо делать, должен содержать комментарий, объясняющий почему).

Для более подробной информации, пожалуйста, прочитайте мой ответ по стилю и рекомендации комментирования кода

хайлем
источник
@haylem Полагаю, это версия Фредди и Джейсона для программиста :-)
Gaurav
Я согласен, однако я бы добавил, что он должен быть размером в одну страницу экрана. если на одной странице экрана 72 строки, функция не должна превышать 72 строки.
Отображаемое имя
@ Необходимо обеспечить, чтобы на экранной странице сохранялась одна функция, чтобы не прокручиваться, но обычно это не моя главная задача. Меня беспокоит количество времени, которое требуется для обработки информации при чтении функции, и это почти мгновенно, если она четко написана в 25 строках. Это просто вопрос отслеживания вызовов функций. 72 слишком велик для меня (плюс, что, если у вас есть разделенные экраны? И это будет зависеть от шрифта. Но я согласен с исторической ценностью рекомендации)
Хайлем
1
Конечно, иногда у вас есть функции, где вы просто копируете 70 разных полей в 70 разных местах. Я в основном использую их ttдля генерации, но иногда вы застряли с длинной задницей (или с длинной задницей), которая на самом деле ничего не делает, так что не является реальной проблемой.
конфигуратор
1
Когда функция, например Map(x => x.Property1); Map(x => x.Property2); Map(x => x.Property3);, ясно, что все совершенно одинаково. (Обратите внимание, что это всего лишь пример; такая функция время от времени появляется)
конфигуратор
12

На мой взгляд, каждая функция должна быть как можно меньше. Каждая функция должна делать только одно и делать это хорошо. Это на самом деле не отвечает на вопрос о максимальной длине, но это больше мои чувства по поводу длины функций.

Чтобы использовать слова дяди Боба: «Извлекай, пока больше не сможешь извлечь. Извлеки, пока не упадешь».

Jason
источник
2
Что вы подразумеваете под как можно меньше? Не будет ли настолько малым, насколько возможно, каждая функция будет иметь только две строки: одну для выполнения одной операции, а другую, которая вызывает функцию для выполнения остальных?
Кельмикра
Дядя Боб снова. Подумай сам. Не слушай дядей. Они вводят вас в заблуждение.
gnasher729
10

Какой должна быть максимальная высота здания? Зависит от того, где находится сборка, или от высоты, которую вы хотите, чтобы она была.
Вы можете получить разные ответы от разных людей из разных городов.
Некоторые функции сценариев и обработчики прерываний ядра очень длинные.

Хуан Ф. Лей
источник
Я полностью согласен с тобой. Мне нравятся метафоры. :) Трехэтажное здание, возможно, построил дурацкий архитектор, который не знает, где поставить действующий безопасный выход, а другое здание может иметь десять этажей и быть идеальным архитектурным проектом. Мы всегда должны помнить, что удобочитаемость и ремонтопригодность должны быть основной причиной реорганизации метода уменьшения его размера, а не самого размера. Город не может быть построен с 90% небоскреба, кроме как в научно-фантастических фильмах. :)
Самуил
10

Метод, который работает для меня: могу ли я иметь часть более длинной функции, дающую имя, которое имеет смысл. Я думаю, что длина метода не так важна, как хорошее именование. Метод должен делать то, что говорит название, не больше и не меньше. И вы должны быть в состоянии дать хорошее имя. Если вы не можете назвать свой метод хорошим, код, вероятно, не очень хороший.

Mnementh
источник
И ваш метод должен делать только одно, чтобы иметь хорошее имя ... без 'And', 'Or' или чего-либо еще, что делает имя метода длиной 50 символов.
Самуэль
10

Пока это нужно, чтобы делать то, что нужно, но больше не нужно.

Пол Натан
источник
как говорится в c: «В c нет проблемы, которую вы не могли бы решить, добавив еще один указатель на этот указатель». Вы всегда можете добавить еще одну функцию под ним, его вопрос будет к вашему прозрению "где это заканчивается?"
Отображаемое имя
1
Я бы на самом деле перевернул его и сказал бы: «Короче, как нужно, но не короче», но вы получите мой +1 за то, что достаточно близко :)
Бен Хьюз
6

Я думаю, что есть компромисс. Если у вас много коротких методов, отладить их часто сложнее, чем один длинный метод. Если вам придется прыгать по редактору 20 или 30 раз, чтобы отследить один вызов метода, вам будет сложно держать все это в голове. Между тем, если есть один хорошо написанный понятный метод, даже если он содержит 100 строк, его часто легче держать в голове.

Реальный вопрос заключается в том, почему элементы должны быть разными методами, и ответ, как указано выше, - повторное использование кода. Если вы не используете код повторно (или не знаете), то, возможно, имеет смысл оставить его в одном гигантском методе, которому легко следовать, и затем, когда вам потребуется его повторно использовать, разделите части, которые нужно повторно использовать. используя в меньшие методы.

На самом деле частью хорошего метода разработки является создание функционально связанных методов (по сути, они делают одно). Длина методов не имеет значения. Если функция выполняет одну четко определенную вещь и имеет 1000 строк, то это хороший метод. Если функция делает 3 или 4 вещи и только 15 строк, то это плохой метод ...

Cervo
источник
Мне нравятся короткие методы.
Марси
Мне нравится то, что вы сказали, потому что сказать, что метод не должен содержать более 10 строк, на мой взгляд, утопия. Хорошо, это хорошее правило, о котором нужно помнить каждый раз, когда вы пишете метод, но это не должно быть математическим правилом вроде 1 + 1 = 2. Если вы уважаете такие принципы, как KISS, DRY, YAGNI и т. Д. ... и ваши методы не полные комментарии, объясняющие некоторые детали, потому что они слишком длинные, методы могут иметь 100 строк кода и могут быть абсолютно чистыми для понимания и сопровождения. Однако это должно быть скорее исключением, чем привычкой. Я думаю, что переключение регистра в фабричном методе является хорошим примером исключения.
Самуил
5

Мне легче отслеживать, что я делаю, если я вижу всю функцию одновременно. Итак, вот как я предпочитаю писать функции:

  1. Достаточно короткий, чтобы поместиться на моем мониторе с разумным шрифтом.
  2. Если он должен быть длиннее № 1, достаточно коротким, чтобы печатать на листе бумаги приемлемым шрифтом.
  3. Если он должен быть длиннее, чем № 2, достаточно коротким, чтобы напечатать 2-на листе бумаги.

Я редко пишу функции дольше, чем это. Большинство из них - гигантские операторы переключения C / C ++.

Боб Мерфи
источник
Достаточно короткая, чтобы поместиться на мониторе, это хорошо, но указав тип и размер шрифта. Бумага не должна быть правилом, на мой взгляд, потому что мы находимся в 2013 году, и кто все еще печатает код на бумаге, кто будет печатать, чтобы посмотреть, подходит ли он под размер бумаги? С такими инструментами, как Visual Studio, intellisense, больше нет причин анализировать код на бумаге.
Самуил
5

Для меня функция - это любая длина, которой она должна быть. Большую часть времени я делю это на то, когда я буду повторно использовать код.

В основном, я буду придерживаться принципа «высокая когезия, низкая связь», и нет никаких ограничений на длину.

Ross
источник
5

Вопрос должен состоять в том, сколько функций должна выполнять функция. И, как правило, редко требуется 100 строк, чтобы сделать «одну» вещь. Опять же, это зависит от уровня, с которого вы смотрите на код: хэширование пароля - это одно? Или хеширование и сохранение пароля - это одно?

Я бы сказал, начать с сохранения пароля в качестве одной функции. Когда вы чувствуете, что хеширование отличается, и вы реорганизуете код. Я ни в коем случае не являюсь опытным программистом, но ИМХО, вся идея функций начинается с малого: чем более элементарны ваши функции, тем выше вероятность повторного использования кода, никогда не делая одинаковых изменений в более чем одном месте , так далее.

Я видел хранимые процедуры SQL, которые запускают более 1000 строк. Число строк хранимых процедур также меньше 50? Я не знаю, но это делает чтение кода, черт возьми. Вы не только должны продолжать прокручивать вверх и вниз, вам нужно дать нескольким строкам кода имя типа «это делает валидацию1», «это обновляет базу данных» и т. Д. - работу, которую должен был выполнить программист.

Нараяна
источник
+1 только за первый абзац. все остальное относится к делу, над которым вы работаете.
Отображаемое имя
И как помогает разделение на 50 функций? Когда функции должны взаимодействовать, то вы не закончите с 500 строками, а с тысячами?
gnasher729
5

Из цикломатической сложности (Википедия):

Цикломатическая сложность раздела исходного кода - это количество линейно независимых путей в исходном коде.

  • Я рекомендую вам сохранить это число до 10 в одном методе. Если он доходит до 10, то пришло время пересмотреть.

  • Существуют инструменты, которые могут оценить ваш код и дать вам число цикломатической сложности.

  • Вы должны стремиться интегрировать эти инструменты в свой конвейер сборки.

  • Не буквально преследуйте размер метода, но попытайтесь взглянуть на его сложность и обязанности. Если на него лежит более одной ответственности, то, вероятно, это хорошая идея для перефакторинга. Если его цикломатическая сложность возрастает, то, вероятно, пришло время пересмотреть фактор.

  • Я вполне уверен, что есть и другие инструменты, которые дают вам аналогичные отзывы, но у меня еще не было возможности изучить это.

CodeART
источник
Цикломатическая сложность показала, что на реальном коде из одного из крупных публичных репозиториев, а не надуманных примеров, очень сильно коррелирует с необработанным SLOC. Это делает его в основном бесполезным, так как намного легче рассчитывать возврат каретки. (Да, можно играть в SLOC. Если честно, то здесь: как долго кому-то, кто разыгрывал показатели SLOC у вашего работодателя, будет позволено продолжать получать зарплату?)
Джон Р. Штром,
4

Обычно я стараюсь, чтобы мои методы / функции соответствовали экрану монитора 1680x1050. Если он не подходит, то используйте вспомогательные методы / функции для разделения задачи.

Это помогает удобочитаемости как на экране, так и на бумаге.

Джейсон
источник
Я делаю то же самое, но стоит указать, какой тип и размер шрифта вы используете. Для себя я предпочитаю «консолы» размером 14, как это предложил Скотт Хансельман. hanselman.com/blog/… В первый раз сложно работать с таким крупным шрифтом, но лучше всегда помнить о том, что ваш метод должен быть как можно меньше.
Самуил
4

Я не ставлю жесткие ограничения на что-либо, потому что некоторые функции реализуют алгоритмы, которые по своей природе являются сложными, и любая попытка сделать их короче сделает взаимодействие между новыми, более короткими функциями настолько сложным, что общий результат не уменьшит простоту. Я также не верю, что идея о том, что функция должна выполнять только «одну вещь», является хорошим руководством, поскольку «одна вещь» на высоком уровне абстракции может быть «многими вещами» на более низком уровне.

Для меня функция определенно слишком длинная, если ее длина прямо сейчас вызывает незначительные нарушения DRY, и извлечение части функции в новую функцию или класс может решить эту проблему. Функция может быть слишком длинной, если это не так, но функция или класс могут быть легко извлечены, что сделает код более модульным, что может оказаться полезным в условиях ожидаемых изменений в будущем.

dsimcha
источник
Функции выполняют «одну вещь» на определенном уровне абстракции, и вы беспокоитесь только об этом одном уровне абстракции. В этом весь смысл. Если вы не можете понять это, то я не думаю, что вы понимаете абстракцию.
Зоран Павлович
4

Достаточно короткий, чтобы быть правильно оптимизированным

Методы должны быть настолько короткими, чтобы делать ровно одну вещь. Причина этого проста: ваш код может быть должным образом оптимизирован.

В языке JIT-ted, таком как Java или C #, важно, чтобы ваши методы были простыми, чтобы JIT-компилятор мог быстро создавать код. Более длинные, более сложные методы, естественно, требуют больше времени JIT. Кроме того, JIT-компиляторы предлагают только несколько оптимизаций, и от этого выигрывают только самые простые методы. Этот факт был даже назван в Эффективном C # Билла Вагнера .

В языке более низкого уровня, таком как C или C ++, наличие коротких методов (может быть, дюжина или около того строк) также важно, потому что таким образом вы минимизируете необходимость хранения локальных переменных в оперативной памяти, а не в регистре. (Также известный как «Регистрация разливов».) Обратите внимание, что в этом неуправляемом случае относительная стоимость каждого вызова функции может быть довольно высокой.

И даже в динамическом языке, таком как Ruby или Python, наличие коротких методов также помогает в оптимизации компилятора. В динамическом языке, чем более «динамична» функция, тем сложнее ее оптимизировать. Например, длинный метод, который принимает X и может вернуть Int, Float или String, вероятно, будет работать намного медленнее, чем три отдельных метода, каждый из которых возвращает только один тип. Это связано с тем, что, если компилятор точно знает, какой тип будет возвращать функция, он также может оптимизировать сайт вызова функции. (Например, не проверка на преобразование типов.)

Крис Смит
источник
Около 99,999% приложений имеют гораздо больше вещей, которые замедляют скорость таких программ, как доступ к базе данных, доступ к файлам или задержка в сети. Думая о скорости при разработке методов может быть веской причиной для игр, приложений в реальном времени или отчетов с тоннами данных, но не в других случаях. Тем не менее, это хороший момент, но, несмотря на то, что я маленький программист, мне редко приходится проводить такой тип оптимизации в своих приложениях.
Самуил
Вы делаете такие вещи, когда вы измерили скорость своего кода и обнаружили, что он слишком медленный, если вы знаете, что делаете (это не так просто, как вы думаете), и если вы измеряете ее впоследствии и скорость улучшилась ,
gnasher729
3

Это очень сильно зависит от того, что в коде.

Я видел рутину из тысячи строк, с которой у меня не было проблем. Это был огромный оператор switch, ни один параметр не превышал дюжину строк, и единственной структурой управления в любом варианте был один цикл. В наши дни это было бы написано объектами, но тогда это было невозможно.

Я также смотрю на 120 линий в коммутаторе передо мной. Ни один случай не превышает 3 строки - охранник, назначение и перерыв. Это анализ текстового файла, объекты не возможны. Любую альтернативу будет сложнее читать.

Лорен Печтель
источник
2

Большинство компиляторов не обращают внимания на длину функции. Функция должна быть функциональной, но при этом быть легкой для понимания, изменения и повторного использования для людей. Выберите длину, которая подходит вам лучше всего.

LennyProgrammers
источник
1

Мое общее правило заключается в том, что функция должна помещаться на экране. Я нашел только три случая, которые имеют тенденцию нарушать это:

1) Диспетчерские функции. В старые времена они были распространены, но большинство из них заменены наследованием объектов в эти дни. Тем не менее, объекты работают только внутри вашей программы, и, таким образом, вы все равно увидите случайные функции отправки при работе с данными, поступающими из других мест.

2) Функции, которые выполняют целую кучу шагов для достижения цели и в которых шагам не хватает хорошего подразделения. В итоге вы получаете функцию, которая просто вызывает по очереди длинный список других функций.

3) Как # 2, но где отдельные шаги настолько малы, что они просто встроены, а не вызваны отдельно.

Лорен Печтель
источник
1

Может быть, длина функции не очень хорошая метрика. Мы пытаемся использовать цикломатическую сложность , также и в методах, и одно из будущих правил контроля исходного кода, согласно которому цикломатическая сложность в классах и методах должна быть ниже X.

Для методов X установлен на 30, и это довольно сложно.

Peter Mortensen
источник