Для очень длинных методов всегда нужен рефакторинг?

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

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

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

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

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


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

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;
  }
}

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

21
задан Gianluca 4 August 2010 в 14:45
поделиться

19 ответов

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

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

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

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

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

11
ответ дан 29 November 2019 в 06:11
поделиться

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

если вы спросите в общем, мы ответим Да .

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

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

РЕДАКТИРОВАТЬ:

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

0
ответ дан 29 November 2019 в 06:11
поделиться

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

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

0
ответ дан 29 November 2019 в 06:11
поделиться

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

0
ответ дан 29 November 2019 в 06:11
поделиться

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

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

0
ответ дан 29 November 2019 в 06:11
поделиться

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

1
ответ дан 29 November 2019 в 06:11
поделиться

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

1
ответ дан 29 November 2019 в 06:11
поделиться

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


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

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

2
ответ дан 29 November 2019 в 06:11
поделиться

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

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

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

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

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

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

3
ответ дан 29 November 2019 в 06:11
поделиться

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

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

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

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

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

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

Исходя из лагеря «короткие методы - лучше», это дало мне много поводов для размышлений.

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

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

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

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

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

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

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

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

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

3
ответ дан 29 November 2019 в 06:11
поделиться

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

6
ответ дан 29 November 2019 в 06:11
поделиться

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

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

10
ответ дан 29 November 2019 в 06:11
поделиться

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

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

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

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

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

38
ответ дан 29 November 2019 в 06:11
поделиться

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

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

10
ответ дан 29 November 2019 в 06:11
поделиться

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

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

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

1
ответ дан 29 November 2019 в 06:11
поделиться

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

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

2
ответ дан 29 November 2019 в 06:11
поделиться

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

1
ответ дан 29 November 2019 в 06:11
поделиться

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

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

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);

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

0
ответ дан 29 November 2019 в 06:11
поделиться

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

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

1
ответ дан 29 November 2019 в 06:11
поделиться
Другие вопросы по тегам:

Похожие вопросы: