Подтвердить что ты не робот

Нужно ли очень долгим методам рефакторинга?

Я сталкиваюсь с ситуацией, когда у нас много очень длинных методов, 1000 строк и более.

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

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

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

Тогда вот вопрос: в общем, как вы думаете, такие очень длинные методы всегда нуждались бы в рефакторинге, или в подобном случае это было бы приемлемо? (к сожалению, рефакторинг спецификаций не является вариантом)


изменить: Я удалил все ссылки на "генерировать", потому что это действительно запутывало. Это не сгенерированный автоматически код.

class InCmd001 {

  OutMsg process ( InMsg& inMsg ) {

     OutMsg outMsg = OutMsg::Create();

     OutCmd001 outCmd001 = OutCmd001::Create();
     outCmd001.SetA( param.getA() );
     outCmd001.SetB( inMsg.getB() );

     outMsg.addCmd( outCmd001 );

     OutCmd016 outCmd016 = OutCmd016::Create();
     outCmd016.SetF( param.getF() );

     outMsg.addCmd( outCmd016 );

     OutCmd007 outCmd007 = OutCmd007::Create();
     outCmd007.SetR( inMsg.getR() );

     outMsg.addCmd( outCmd007 );

     // ......

     return outMsg;
  }
}

здесь приведен пример одного входящего командного класса (написанного вручную в псевдо-С++)

4b9b3361

Ответ 1

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

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

Второй вариант - можно ли автоматически генерировать код из структуры данных? Если у вас есть базовый набор классов, которые выполняют работу осла и крайние случаи, вы можете автоматически генерировать повторяющиеся методы 1000 строк так часто, как вы пожелаете.

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

., но в целом, рефакторинг.

Ответ 2

Код никогда не нуждается в рефакторинге. Код работает, или нет. И если это работает, код ничего не нуждается.

Необходимость рефакторинга исходит от вас, программиста. Человек читает, пишет, поддерживает и расширяет код.

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

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

С другой стороны, если код автоматически создается другим инструментом, вам никогда не понадобится его читать или редактировать. Итак, в чем смысл рефакторинга?

Ответ 3

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

Изменить: Просто перечитайте свой вопрос, и я не понимаю одного момента - вы говорите о машинный код, который никто не должен касаться? В этом случае я оставил бы все, как есть.

Ответ 4

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

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

Ответ 5

Длинные методы нуждаются в рефакторинге, если они поддерживаются (и, следовательно, должны быть поняты) людьми.

Ответ 6

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

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

Найдите баланс.

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

Что касается повторения, это плохая идея. Это то, что нужно исправлять, как утечка памяти. Это тикающая бомба.

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

Ответ 7

Взгляните на соответствующий вопрос Сколько строк кода слишком много?. В ответах есть немало лакомых кусочков мудрости.

Чтобы отправить цитату (хотя я попытаюсь прокомментировать ее немного подробнее)... Некоторое время назад я прочитал этот отрывок из Журнал Ovid:

Недавно я написал код для Класс:: Sniff, который будет обнаруживать "длинный методы" и сообщать о них как о коде запах. Я даже написал сообщение в блоге о как я это сделал (quelle удивление, а?). Это, когда Бен Тилли спросил смущающий очевидный вопрос: как знаю, что длинные методы - это код запах?

Я выбросил обычные оправдания, но он не сдавался. Он хотел информации, и он привел отличную Код книги завершен как контраргумент. Я получил свою копию этой книги и начал читать "Как Долгий Должен Должен Быть" (стр. 175, второе издание). Автор, Стив Макконнелл утверждает, что подпрограммы должны не более 200 строк. святой падла! Это waaaaaay долго. Если процедура длится более 20 или 30 линии, я считаю, что пора сломать его вверх.

К сожалению, у Макконнелла есть щека чтобы привести шесть отдельных исследований, все который обнаружил, что более длительные процедуры были не только не коррелирует с большим уровень дефектов, но также часто дешевле разрабатывать и постигать. В результате, последние версия класса:: Sniff on github теперь документы, которые более длинные процедуры не могут быть запахом кода в конце концов. Бен был правильно. Я ошибался.

(Остальная часть сообщения, в TDD, тоже стоит прочитать.)

Из лагеря "более короткие методы лучше", это дало мне много о чем подумать.

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

В вашей ситуации, однако, у меня был бы большой уклон в сторону не касаясь вещей: рефакторинг несет в себе неотъемлемый риск, и в настоящее время он может перевесить награду. (Отказ от ответственности: я немного параноик, я обычно тот парень, который заканчивает исправление сбоев.)

Рассмотрите возможность потратить свои усилия на тесты, утверждения или документацию, которые могут усилить существующий код и наклон масштаба риска/вознаграждения перед любой попыткой реорганизации: инвариант проверки, связанная функция, и pre/postcondition Тесты; любые другие полезные понятия из DBC; возможно, даже параллельную реализацию на другом языке (возможно, что-то сообщение, ориентированное, как Эрланг, даст вам лучшую перспективу, учитывая ваш образец кода) или даже какой-то формальный логический представление спецификации, которую вы пытаетесь выполнить, если у вас есть время для записи.

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

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

Это не значит, что "у вас будет комплект для полного охвата, и DBC утверждает, и формальная логическая спецификация". Это просто, что вы находитесь в типично несовершенной ситуации и немного разнообразна - ищете новые способы приблизиться к обнаруженным проблемам (удобство обслуживания? Нечеткая спецификация? Легкость обучения системе?) - может дать вам немного вперед прогресс и некоторую повышенную уверенность, после чего вы можете делать более крупные шаги.

Так что меньше думайте о том, что "слишком много строк - это проблема", и больше от "это может быть запах кода, какие проблемы он вызовет для нас, и есть ли что-нибудь легкое и/или полезное, мы можем сделать это?"

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

Неужели мне удалось быть достаточно-размытым? Моя точка зрения, я думаю, состоит в том, что код пахнет, шаблоны/антипаттерны, лучшие практики и т.д. - они там, чтобы служить вам. Поэкспериментируйте, чтобы привыкнуть к ним, а затем возьмите то, что имеет смысл для вашей текущей ситуации, и оставьте остальные.

Ответ 8

Думаю, вам сначала нужно "реорганизовать" спецификации. Если в спецификациях есть повторения, это также станет легче читать, если оно использует некоторые "основные строительные блоки".


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

Некоторые люди спрашивали, генерируется ли код. По моему мнению, это не имеет значения: если код следует за спецификацией "строка за строкой", это не имеет никакого значения, если код сгенерирован или написан вручную.

Ответ 9

Вам когда-нибудь нужно читать или поддерживать сгенерированный код?

Если да, то я думаю, что некоторые рефакторинги могут быть в порядке.

Если нет, то язык более высокого уровня - это действительно тот язык, с которым вы работаете, - С++ - это просто промежуточное представление на пути к компилятору - и рефакторинг может не понадобиться.

Ответ 10

Мне кажется, что вы внедрили отдельный язык в своем приложении - считаете ли вы, что это так?

Ответ 11

Насколько я понимаю, он рекомендовал реорганизовать любой метод, содержащий более 100 строк кода.

Ответ 12

Я думаю, что некоторые правила могут быть немного разными в его эпоху, когда код чаще всего просматривается в среде IDE. Если код не содержит допустимого повторения, так что есть 1000 строк, на которые будут ссылаться один раз, и которые четко разделяют значительное число переменных, деля код на 100-строчные подпрограммы, каждый из которых вызывается один раз может не быть таким большим улучшением по сравнению с хорошо отформатированным 1000-строчным модулем, который включает теги #region или эквивалент, чтобы разрешить просмотр в стиле.

Моя философия состоит в том, что некоторые макеты кода обычно подразумевают определенные вещи. На мой взгляд, когда фрагмент кода помещается в его собственную процедуру, это предполагает, что код будет использоваться в нескольких контекстах (исключение: обработчики обратного вызова и т.п. На языках, которые не поддерживают анонимные методы). Если сегмент кода # 1 оставляет объект в неясном состоянии, который может использоваться только сегментом кода 2, а сегмент кода 2 доступен только для объекта данных, который остается в состоянии, созданном # 1, то отсутствует какая-то веская причина чтобы поместить сегменты в разные подпрограммы, они должны появляться в одной и той же процедуре. Если программа помещает объекты через цепочку неясных состояний, простирающихся на многие сотни строк кода, может быть полезно переделать код design кода, чтобы разделить операцию на более мелкие куски, которые имеют более "естественные" "pre- и post-conditions", но при отсутствии каких-либо веских оснований для этого я бы не стал разделять код без изменения дизайна.

Ответ 13

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

Ответ 14

1000 тысяч строк кода ничего. У нас есть функции длиной от 6 до 12 тысяч строк. Конечно, эти функции настолько велики, что в буквальном смысле вещи теряются там, и ни один инструмент не может помочь нам даже взглянуть на их абстракции на высоком уровне. код сейчас, к сожалению, непонятен. Мое мнение о столь больших функциях состоит в том, что они не были написаны блестящими программистами, а некомпетентными хаками, которых не следует оставлять рядом с компьютером, - но их нужно уволить и оставить в руке гамбургеры в McDonald's. Такой код вызывает хаос, оставляя за собой функции, которые нельзя добавить или улучшить. (слишком плохо для клиента). Код настолько хрупкий, что его никто не может изменить, даже оригинальные авторы.

И да, эти методы должны быть реорганизованы или выброшены.

Ответ 15

Я видел случаи, когда это не так (например, для создания электронной таблицы Excel в .Net часто требуется много строк кода для форматирования листа), но большую часть времени, лучшая вещь было бы действительно реорганизовать его.

Я лично пытаюсь сделать функцию достаточно маленькой, чтобы все появилось на моем экране (не затрагивая читаемость, конечно).

Ответ 16

1000 строк? Определенно, их нужно реорганизовать. Также не так, например, по умолчанию максимальное количество исполняемых операторов равно 30 в Checkstyle, известном стандартном контролере кодирования.

Ответ 17

Тогда вот вопрос: вообще говоря, do вы думаете, что такие очень длинные методы всегда нужно рефакторинг,

Если вы спросите в целом, мы скажем Да.

или в аналогичный случай был бы приемлем? (к сожалению, рефакторинг спецификаций это не вариант)

Иногда приемлемо, но очень необычно, я приведу вам пару примеров: Есть несколько 8-разрядных микроконтроллеров под названием Microchip PIC, которые имеют только фиксированный уровень в 8 уровней, поэтому вы не можете вложить более 8 вызовов, поэтому необходимо избегать "переполнения стека", поэтому в этом специальном случае, Функция (вложенная) - не лучший способ. Другим примером является оптимизация кода (на очень низком уровне), поэтому вам нужно учитывать стоимость перехода на скачок и контекст. Используйте его с осторожностью.

EDIT:

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

Ответ 18

Если вы рефакторинг, когда вы рефакторинг, добавьте некоторые комментарии, чтобы объяснить, что он делает.

Если бы у него были комментарии, было бы гораздо менее вероятным кандидатом на рефакторинг, потому что было бы легче читать и следовать за кем-то, начинающим с нуля.

Ответ 19

Было очень хорошее общее пособие, вот практическая рекомендация для вашего образца:

общие шаблоны могут быть выделены в простых методах подачи:

void AddSimpleTransform(OutMsg & msg, InMsg const & inMsg, 
                        int rotateBy, int foldBy, int gonkBy = 0)
{
   // create & add up to three messages
}

Вы даже можете улучшить это, сделав это членом OutMsg и используя свободный интерфейс, чтобы вы могли писать

OutMsg msg;
msg.AddSimpleTransform(inMsg, 12, 17)
   .Staple("print")
   .AddArtificialRust(0.02);

что может быть дополнительным улучшением при обстоятельствах.