Именованные блоки для ограничения области видимости переменной: хорошая идея? - PullRequest
45 голосов
/ 15 октября 2008

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

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

  1. избегайте этого или
  2. продвигайте его в надежде, что это станет идиомой.

Пример (в рамках более крупного метода):

final Date nextTuesday;
initNextTuesday: {
    GregorianCalendar cal = new GregorianCalendar();
    ... // About 5-10 lines of setting the calendar fields
    nextTuesday = cal.getTime();
}

Здесь я использую GregorianCalendar просто для инициализации даты, и я хочу убедиться, что я случайно ее не использую.

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

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

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

Преимущества использования именованного объема:

  1. Не может случайно использовать временные переменные
  2. Ограниченная область дает сборщику мусора и JIT-компилятору больше информации о намерениях программиста
  3. Имя блока предоставляет комментарий к блоку кода, который я считаю более читабельным, чем открытые комментарии
  4. Упрощает преобразование кода из большого метода в маленькие методы или наоборот, поскольку именованный блок легче отделить, чем неструктурированный код.

Недостатки:

Не идиоматично: программисты, которые не видели такого использования именованных блоков (т. Е. Все, кроме меня), предполагают, что он глючит, поскольку они не могут найти ссылки на имя блока. (Как и в случае с «Затмением».) Иметь что-то идиоматическое - тяжелая битва.

Может использоваться как оправдание вредных привычек программирования, таких как:

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

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

Ответы [ 14 ]

26 голосов
/ 15 октября 2008

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

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

14 голосов
/ 15 октября 2008

Если это было плохо, то почему эта функция в языке! У него есть цель, и вы ее нашли.

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

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

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

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

10 голосов
/ 15 октября 2008

Иногда я использую неназванные блоки, чтобы изолировать изменяемые вещи, необходимые для подготовки некоторых неизменных вещей. Вместо того, чтобы иметь метку, я помещаю Блок под объявлением неизменной переменной.

final String example;
{
   final StringBuilder sb = new StringBuilder();
   for(int i = 0; i < 100; i++)
     sb.append(i);
   example = sb.toString();

}

Когда я нахожу какое-то другое применение для блока или просто думаю, что он мешает, я превращаю его в метод.

9 голосов
/ 15 октября 2008

Использование блоков для ограничения области видимости - хорошая техника в моей книге.

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

5 голосов
/ 15 октября 2008

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

Хотя я не использую ярлыки и просто комментирую, что я делаю.

Я не согласен со всеми парнями, которые просят вас извлечь его в метод. Большинство вещей, которые мы надеваем в таких блоках, на самом деле не являются повторно используемыми блоками. Это имеет смысл в большой инициализации И ДА, я использовал блоки, чтобы предотвратить ошибки COPY / PASTE.

BR
~ A

3 голосов
/ 15 октября 2008

Если у вас есть 5-10 строк кода, которые можно безопасно поместить в подобный блок, тот же код можно также извлечь в метод.

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

2 голосов
/ 18 октября 2008

Я бы использовал блок с комментарием вместо добавления метки.

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

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

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

2 голосов
/ 16 октября 2008

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

  1. Вы не сможете использовать временные переменные, объявленные в новом методе
  2. Компилятор GC и JIT получит ту же информацию, используя новый метод
  3. Использование описательного имени для нового метода (в вашем случае с использованием «private Date initNextTuesday ()») позволит использовать код с самокомментированием
  4. Нет необходимости в рефакторинге кода, если вы уже «предварительно проанализировали» его

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

1 голос
/ 21 апреля 2015

Name Blocks помогает: Использование разрыва как формы Goto

Использование разрыва как цивилизованной формы goto.

class Break {
    public static void main(String args[]) {
        boolean t = true;
        first: {
            second: {
                third: {
                    System.out.println("Before the break.");
                    if (t)
                        break second; // break out of second block
                    System.out.println("This won't execute");
                }
                System.out.println("This won't execute");
            }
            System.out.println("This is after second block.");
        }
    }
}

Использование разрыва для выхода из вложенных циклов

class BreakLoop4 {
    public static void main(String args[]) {
        outer: for (int i = 0; i < 3; i++) {
            System.out.print("Pass " + i + ": ");
            for (int j = 0; j < 100; j++) {
                if (j == 10)
                    break outer; // exit both loops
                System.out.print(j + " ");
            }
            System.out.println("This will not print");
        }
        System.out.println("Loops complete.");
    }
}

Источник Ссылка

1 голос
/ 29 октября 2014

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

final Date nextTuesday;
initNextTuesday: {
    GregorianCalendar cal = new GregorianCalendar();
    ... // About 5-10 lines of setting the calendar fields
    nextTuesday = cal.getTime();
}

Включение этой логики инициализации облегчает понимание, если вы читаете файл сверху вниз и заботитесь о каждой строке. Но подумайте о том, как вы читаете код. Вы начинаете читать с начала файла и продолжаете до конца? Конечно, нет! Единственный раз, когда вы это сделаете, это во время проверки кода. Вместо этого у вас, вероятно, есть начальная точка, основанная на предыдущих знаниях, трассировке стека и т. Д. Затем вы продолжаете изучать путь выполнения вверх / вниз, пока не найдете то, что ищете. Оптимизация для чтения на основе пути выполнения, а не проверки кода.
Человек, читающий код, который использует nextTuesday, действительно хочет прочитать о том, как он инициализируется? Я бы сказал, что единственная информация, которая им нужна, это то, что Date соответствует следующему вторнику. Вся эта информация содержится в ее декларации. Это прекрасный пример кода, который должен быть разбит на частный метод, потому что нет необходимости понимать логику, которая волнует читателя .

final Date nextTuesday;
initNextTuesday: {
    GregorianCalendar cal = new GregorianCalendar();
    //1
    //2
    //3
    //4
    //5
    nextTuesday = cal.getTime();
}

против

final Date nextTuesday = getNextTuesday();

Что бы вы предпочли прочитать по пути через модуль?

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