2011-01-14 6 views
6

Я сделал некоторые поиски здесь и не нашел ничего подобного, поэтому я собираюсь идти вперед и спрашивать. Это действительно больше о семантике, чем о реальном программировании. В настоящее время я пишу что-то на C++, но язык не имеет большого значения.Насколько коротким должна быть функция?

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

Первый язык программирования, который я узнал (кроме Applesoft BASIC, который не учитывается ...) был 6502 ассемблером, где скорость и оптимизация - это все. В случаях, когда несколько циклов подсчитывают время всей вашей программы, часто лучше устанавливать местоположение памяти или регистрироваться напрямую, а не переходить на другую подпрограмму. Первая операция может занять 3 или 4 цикла, в то время как последняя может занять 2-3 раза.

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

В частности, скажем, (опять же с помощью C++) у нас есть метод частного класса, который что-то вроде следующего:

int Foo::do_stuff(int x) { 
    this->x = x; 
    // various other operations on x 
    this->y = this->x; 
} 

Я видел некоторые аргументы, которые, по крайней мере, каждый набор операций должен быть его собственной функцией. Например, do_stuff() следует теоретически называть set_x (int x), отдельная функция должна быть записана для набора операций, выполняемых с элементом класса x, а третья функция должна быть записана для назначения конечного значения члена класса x - члену класса y. Но я видел другие аргументы, что каждая операция должна иметь свою собственную функцию.

Для меня это просто кажется неправильным. Опять же, я смотрю на вещи с внутренней точки зрения; каждый вызов метода вызывает адрес в стеке, выполняет его операции, а затем возвращается из подпрограммы. Это просто похоже на накладные расходы для чего-то относительно простого.

Есть ли передовая практика для такого рода вещей или это больше подходит для индивидуального решения?

ответ

8

С момента сборки 6502 произошли две вещи: компьютеры стали намного быстрее, а компиляторы (в соответствующих случаях) стали умнее.

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

Вместо этого, начать думать о том, чтобы ваши функции достаточно малы, чтобы быть:

  • понятно,
  • проверяемым,
  • многоразовый, может быть, где это уместно.

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

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

+0

Спасибо, это определенно полезно. C++ - отнюдь не мой первый неассемблерный язык, но поскольку у меня никогда не было какой-либо формальной подготовки во всем, я думал, что лучше всего получить некоторые отзывы о методах кодирования. – xobicvap

+1

+1 за то, что он не беспокоился о циклах, пока это не проблема. Преждевременная оптимизация - это корень всего зла. – riwalk

5

Важнейшая вещь о решении, где разбить функцию, необязательно , насколько функция выполняет. Это скорее определение API вашего класса.

Предположим, мы сломаем Foo::do_stuff в Foo::set_x, Foo::twiddle_x и Foo::set_y. Имеет ли смысл делать эти операции отдельно? Будет ли что-то плохое, если я закручу x без предварительной установки? Могу ли я позвонить set_y без звонка set_x? Разбивая их на отдельные методы, даже частные методы в одном классе, вы подразумеваете, что они, по крайней мере, потенциально являются отдельными операциями.

Если это не так, то обязательно используйте их в одной функции.

+1

+1. Поддержание инвариантов и согласованность состояния/объектов является ключом к поддерживаемому программному обеспечению. –

+0

Это в значительной степени совпадает с тем, что я думал, но я хотел убедиться. Используя вышеприведенный пример, в функции, которую я сейчас пишу, нет абсолютно никакой причины, чтобы set_y когда-либо происходил без вызова set_x и twiddle_x. Придя от других (не-ООП) языков, я * действительно * ценю инкапсуляцию, так как она помогает мне определить в голове, как именно переменная должна быть изменена и что имеет право ее изменять. Я просто немного боюсь зайти слишком за борт. Эти рекомендации должны помочь. – xobicvap

0

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

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

+0

Я согласен, хотя, чтобы играть адвоката дьявола, я хотел бы сказать, что, по-моему, по крайней мере знание языка ассемблера определенно полезно в том, что у вас есть хотя бы некоторое представление о том, как все работает на уровне процессора. В то же время я вообще не пропускаю программирование сборки. Считываемость практически обязательна в коде ASM, а не только из-за терминологии. – xobicvap

+0

@ubtng, согласен, я не собирался подразумевать обратное. моя точка зрения состояла в том, что когда люди идут на чертежную доску, функция декомпозиции не является высокой в ​​списке приоритетов. насколько это касается, сборка великолепна! нечитабельно, но здорово! – davin

3

Я хорошо знаю, что это хорошо практика программирования держать функции/методы как можно более коротким

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

  1. Держите все функции на одном уровне абстракции
  2. убедитесь, что не никаких побочных эффектов (для исключительных случаев убедитесь, что явно документ их)
  3. Убедитесь функция не делает больше одного (принцип SRP). Но вы можете сломать это в честь 1.

Другое good practices for Method Design

  1. Не заставляйте Клиент Do Anything модуль мог бы сделать
  2. Не Нарушают принцип наименьшего Удивление
  3. Сбой ошибок Перемотка доклада в как Возможное после того, как они произошли
  4. Перегрузка с уходом
  5. Использование соответствующего параметра и типы возвращаемых
  6. Использование Последовательных параметров заказ Через методу
  7. Избегайте длинных Списки параметров
  8. Избегайте Возвращаемые значения, которые требуют Исключительной Обработки
2

После прочтения Clean Code: A Handbook of Agile Software Craftsmanship соприкасались на почти каждый кусок советы на этой странице Я начал писать короче. Не для того, чтобы иметь короткие функции, но для улучшения удобочитаемости и проверки, держите их на одном уровне абстракции, имейте только одну вещь и т. Д.

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

Смежные вопросы