Рефакторинг большого метода в .NET

Если у меня есть большой метод .NET, я хочу знать, является ли хорошей практикой разделение его на несколько методов или нет. Меня беспокоит следующее: 1. Есть ли смысл создавать метод, если он будет вызываться только один раз?
2. Если я вызываю закрытый метод из своего класса, я стараюсь не использовать переменные экземпляра. Это хорошая или плохая практика? Например:

var a, b;
public void TestMe
{
   FirstTest(a,b);
}
private void FirstTest(paramA, paramB)
{
         //code here with passed in parameters
}

//instead of:
private void FirstTest()
{
      //code here with instance variables a,b
}  

изменить: заменить глобальный на экземпляр


person MC.    schedule 09.03.2010    source источник


Ответы (4)


1 - Есть ли смысл создавать метод, если он будет вызываться только один раз?

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

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

2 - Если я вызываю частный метод из своего класса, я стараюсь не использовать переменные экземпляра. Это хорошая или плохая практика?

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

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

При таком рефакторинге обязательно помечайте новые методы как static, если они не обращаются ни к каким полям. Это сделает намерение явным.

person Jeffrey L Whitledge    schedule 09.03.2010

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

Что касается второго вопроса, как указывает @Jason, вы используете переменные экземпляра, а не глобальные переменные в своем классе. Я бы сказал, какая практика предпочтительнее, зависит от контекста метода. Конечно, если метод можно повторно использовать во многих контекстах, только некоторые из которых работают с переменными экземпляра, его следует параметризовать. Если вы всегда используете только переменные экземпляра, вы можете отказаться от параметров и выполнить рефакторинг по мере необходимости позже для удобства использования.

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

person tvanfosson    schedule 09.03.2010
comment
Книга дяди Боба «Чистый код» хорошо объясняет методы разбиения. Простое эмпирическое правило: если вы считаете, что код может использовать комментарий, он, вероятно, может использовать вместо него хорошо названный метод. - person Shane Courtrille; 09.03.2010

Цитата из Чистый код (отличная книга) , метод должен делать одно и только одно. Если вы делаете несколько вещей в методе, это, вероятно, означает, что вам нужно извлечь эту логику в отдельный метод. Это также значительно упрощает следование коду. Так что я бы сказал

Вопрос 1: Да, разбить (разделение концерна).

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

function someFunc() {
  anotherFunc(this.a, this.b);
}

function anotherFunc(int paramA, int paramB) {
  //do stuff with paramA and paramB
}

против

function someFunc() {
  anotherFunc();
}

function anotherFunc() {
  //do stuff with this.a and this.b
}

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

person Vivin Paliath    schedule 09.03.2010

  1. Если часть кода будет вызываться только один раз, скорее всего, нет причин для создания метода в первую очередь. ОДНАКО, с учетом сказанного, если это частный метод и он используется как отдельная часть функциональности (отдельная проблема), то было бы неплохо разделить его. Вы хотели бы сделать это, чтобы избежать одного огромного метода. Например, если я выполняю поиск только один раз в фрагменте кода, я все равно захочу разделить этот поиск, потому что поиск — это отдельная часть функциональности. (Это очень плохой пример. Если у кого-то есть лучший, не стесняйтесь редактировать.

  2. Переменная НЕ является ДЕЙСТВИТЕЛЬНО глобальной, если она не доступна во всей системе. С учетом сказанного, я бы настоятельно рекомендовал избегать глобальных переменных, потому что это очень затрудняет отладку и работу с кодом, поскольку переменные имеют чрезвычайно большую область действия. Вместо этого я бы сосредоточился на том, чтобы иметь больше локализованных переменных и передавать переменные, которые могут понадобиться функции/методу, и возвращать данные, которые ожидает вызывающая функция. Таким образом, вы сохраняете свои данные более локализованными и, в конечном счете, код легче читать и управлять им. Это не относится к частным или общедоступным методам.

person JasCav    schedule 09.03.2010