Длинный рефакторинг метода: оставление как есть и разделение на методы против использования локальных функций

9

Предположим, у меня есть длинный метод, подобный этому:

public void SomeLongMethod()
{
    // Some task #1
    ...

    // Some task #2
    ...
}

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

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


Но если я разделю этот код на методы

public void SomeLongMethod()
{
    Task1();

    Task2();
}

private void Task1()
{
    // Some task #1
    ...
}

private void Task2()
{
    // Some task #1
    ...
}

Я вижу следующие проблемы:

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

  • Загрязнение автозаполнения IDE (например, Intellisense) методов, которые будут использоваться только один раз внутри одного SomeLongMethodметода.


Так что, если я разделю этот код метода на локальные функции

public void SomeLongMethod()
{
    Task1();

    Task2();

    void Task1()
    {
        // Some task #1
        ...
    }

    void Task2()
    {
        // Some task #1
        ...
    }
}

тогда у этого нет недостатков отдельных методов, но это не выглядит лучше (по крайней мере для меня), чем оригинальный метод.


Какая версия SomeLongMethodболее удобна для чтения и почему?

Вадим Овчинников
источник
@gnat мой вопрос относится к C # , а не Java . Моя главная забота - классический метод против локальной функции, а не просто разделение на методы или нет.
Вадим Овчинников
@gnat Также AFAIK Java (в отличие от C #) не поддерживает вложенные функции.
Вадим Овчинников,
1
частное ключевое слово надежно защищает вашу «область определения класса»?
Эван
1
подождите ...... каков ваш класс?
Эван

Ответы:

13

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

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

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

Килиан Фот
источник
Так вы за методы, а не за локальные функции, да?
Вадим Овчинников
2
@VadimOvchinnikov С современной интегрированной средой разработки кода есть небольшая разница. Самым большим преимуществом локальной функции является то, что она может обращаться к переменным, которые в противном случае пришлось бы передавать в качестве параметров, но если их много, то задачи не были действительно независимыми с самого начала.
Килиан Фот
6

Мне не нравится ни один подход. Вы не предоставили более широкий контекст, но в большинстве случаев это выглядит так:

У вас есть класс, который выполняет некоторую работу, объединяя возможности нескольких служб в один результат.

class SomeClass
{
    private readonly Service1 _service1;
    private readonly Service2 _service2;

    public void SomeLongMethod()
    {
        _service1.Task1();
        _service2.Task2();       
    }
}

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

class Service1
{
    public void Task1()
    {
        // Some task #1
        ...
    }

    private void SomeHelperMethod() {}
}

class Service2
{
    void Task2()
    {
        // Some task #1
        ...
    }
}

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

t3chb0t
источник
2

Проблема с созданием функций в методе заключается в том, что он не уменьшает длину метода.

Если вам нужен дополнительный уровень защиты «private to method», вы можете создать новый класс

public class BigClass
{
    public void SomeLongMethod();

    private class SomeLongMethodRunner
    {
        private void Task1();
        private void Task2();
    }
}

Но классы на уроках очень страшные: /

Ewan
источник
2

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

Это говорит в пользу использования локальных функций.

FSL
источник
1
хм, конечно, повторное использование кода == разделяя метод среди многих вызывающих. то есть максимизация его объема
Ewan
1

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

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

На странице « Местные функции» говорится, что:

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

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

Таким образом, в целом, я бы предпочел разделить на методы, но MS по какой-то причине написала Local Functions, так что я бы точно не сказал, что не используйте их,

d219
источник