Должны ли мы устранить локальные переменные, если мы можем?

95

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

PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "abc");
wakeLock.acquire();

но я думаю локальные переменные powerManagerи wakeLockмогут быть устранены

((PowerManager)getSystemService(POWER_SERVICE))
    .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakelockTag")
    .acquire();

похожая сцена появляется в окне предупреждений iOS, например: из

UIAlertView *alert = [[UIAlertView alloc]
    initWithTitle:@"my title"
    message:@"my message"
    delegate:nil
    cancelButtonTitle:@"ok"
    otherButtonTitles:nil];
[alert show];

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    [alertView release];
}

чтобы:

[[[UIAlertView alloc]
    initWithTitle:@"my title"
    message:@"my message"
    delegate:nil
    cancelButtonTitle:@"ok"
    otherButtonTitles:nil] show];

-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
    [alertView release];
}

Является ли хорошей практикой устранение локальной переменной, если она используется только один раз в области?

ggrr
источник
95
Не обязательно. Иногда это делает код более понятным для использования одноразовых переменных, и в большинстве приличных языков программирования очень мало (если есть) затрат времени выполнения для этих дополнительных переменных.
Роберт Харви
75
Это также затрудняет пошаговое выполнение кода с отладчиком. И вы также должны быть уверены (в зависимости от языка), что первое выражение не является NULL или ошибкой.
GrandmasterB
78
Нет, это не так. На самом деле, хорошей практикой является введение локальных переменных для разрыва цепочек вызовов методов. Сгенерированный машинный код, вероятно, будет идентичным, а исходный код почти гарантированно будет более читабельным, а значит, и лучше.
Килиан Фот
48
Попробуй это. Теперь подключите отладчик и попробуйте увидеть состояние PowerManager или WakeLock. Осознай свою ошибку. Раньше я думал так же («что со всеми этими местными жителями?»), Пока мне не пришлось тратить большую часть своего времени на изучение кода.
Фаериндел
42
Даже в вашем примере ваша «удаленная» версия имеет полосу прокрутки и не умещается полностью на моем экране, что делает ее намного труднее для чтения.
Эрик

Ответы:

235

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

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

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

Тони БенБрахим
источник
2
Вы не можете знать, много ли кода, оптимизатор может встроить некоторые функции ... В противном случае, согласитесь, стремление к читабельности в первую очередь является правильным шагом.
Матье М.
2
Что ж, я думаю, что наоборот, если я увижу, что локальные переменные инициализируются кратно раз для каждой функции, где она используется, а не являются атрибутами, когда это возможно, я буду искать, почему, в основном, я буду тщательно читать строки и сравнивать их просто с убедитесь, что они одинаковы. Так что я буду терять время.
Вальфрат
15
@Walfrat Локальные переменные инициализируются несколько раз? Уч. Никто не предлагал повторно использовать местных жителей :)
Луаан
32
Члены @Walfrat порождают побочные эффекты и должны использоваться только в том случае, если вы хотите сохранить состояние между вызовами публичных участников. Похоже, этот вопрос касается использования локального для временного хранения промежуточных вычислений.
Гусдор
4
Q: Должны ли мы исключить локальные переменные, если можем? A: Нет.
Simon
94

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

Debuggability

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

С локальными переменными вы можете:

  • Точка останова на линии, где она назначена (со всеми другими украшениями точек останова, такими как условная точка останова и т. Д.)

  • Изучить / посмотреть / распечатать / изменить значение локальной переменной

  • Поймать проблемы, которые возникают из-за бросков.

  • Иметь четкие трассировки стека (строка XYZ имеет одну операцию вместо 10)

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

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

DVK
источник
20
Я бы добавил, что трассировка стека гораздо менее полезна, когда в одной строке много вызовов. Если у вас есть исключение нулевого указателя в строке 50, и в этой строке 10 вызовов, это не слишком сужает вещи. В производственном приложении это часто будет основная часть информации, которую вы получаете из отчета о дефектах.
JimmyJames
3
Переход по коду также намного сложнее. Если есть call () -> call () -> call () -> call (), трудно перейти к третьему вызову метода. Гораздо проще, если есть четыре локальные переменные
gnasher729
3
@FrankPuffer - нам приходится иметь дело с реальным миром. Где отладчики не делают то, что вы предлагаете.
ДВК
2
@DVK: На самом деле отладчиков больше, чем я думал, которые позволяют вам осматривать или наблюдать за любым выражением. MS Visual Studio (с версии 2013) имеет эту функцию для C ++ и C #. В Eclipse это есть для Java. В другом комментарии Faerindel упомянул IE11 для JS.
Фрэнк
4
@DVK: Да, многие отладчики реального мира не делают то, что я предлагаю. Но это не имеет ничего общего с первой частью моего комментария. Я просто нахожу сумасшедшим предположение, что человек, который собирается поддерживать мой код, будет взаимодействовать с ним в основном с помощью отладчика. Отладчики хороши для определенных целей, но если они получат основной инструмент для анализа кода, я бы сказал, что что-то серьезно не так, и я постараюсь исправить это, вместо того чтобы сделать код более отлаживаемым.
Фрэнк
47

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

Исключение переменных в скомпилированном коде - тривиальная операция для любого респектабельного компилятора. Вы можете проверить вывод самостоятельно, чтобы проверить.

как зовут
источник
1
Это связано как со стилем, так и с использованием переменных. Вопрос теперь отредактирован.
Джодрелл
29

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

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

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

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

Может быть.

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

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

На мой взгляд, это лучший компромисс:

PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE);
powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "abc").acquire();

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

9Rune5
источник
13

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

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

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

((PowerManager)getSystemService(POWER_SERVICE))
        .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"MyWakelockTag")
        .acquire();

Сравните это с версией с локальными переменными:

PowerManager powerManager=(PowerManager)getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"abc");
wakeLock.acquire();

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

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

Еще три комментария:

  1. По моему мнению, добавление кода, который не имеет функционального использования, может иногда сбивать с толку, поскольку читатель должен понимать, что он действительно не имеет функционального использования и просто предназначен для «документации». Например, если бы этот метод был длиной в 100 строк, не было бы очевидно, что локальные переменные (и, следовательно, результат вызова метода) не требовались в какой-то более поздний момент, если вы не прочитали весь метод.

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

  3. Отладка может быть проще с локальными переменными, если ваш отладчик не показывает значения, возвращаемые методами. Но исправление для этого состоит в том, чтобы исправить недостатки в отладчиках, а не изменить код.

rghome
источник
Что касается ваших дальнейших комментариев: 1. Это не должно быть проблемой, если вы следуете соглашению о том, что локальные переменные объявляются как можно ближе к месту их использования, и вы сохраняете свои методы разумной длины. 2. Не на языке программирования с выводом типа. 3. Хорошо написанный код часто не нуждается в отладчике вообще.
Роберт Харви
2
Ваш первый пример кода работает только в том случае, если вы создаете свои объекты с использованием свободно распространяемых интерфейсов или шаблонов «построителя», которые я бы не использовал повсюду в своем коде, но только выборочно.
Роберт Харви
1
Я думаю, что вопрос предполагает, что локальные переменные являются функционально избыточными и, следовательно, для этого случая требуется интерфейс такого типа. Мы могли бы, вероятно, добиться удаления местных жителей, если бы это было иначе с помощью различных искажений, но я имел в виду, что мы рассматривали простой случай.
rghome
1
Я считаю ваш вариант еще хуже для удобства чтения. Возможно, я слишком много сталкивался с VB.net, но мое первое знакомство с вашим примером: «Куда делся оператор WITH? Я случайно удалил его?» Мне нужно дважды посмотреть, что происходит, и это не очень хорошо, когда мне нужно пересмотреть код много месяцев спустя.
Тонни
1
@ Тонно для меня, это более читабельно: местные жители не добавляют ничего, что не очевидно из контекста. У меня есть голова Java, так что никаких withзаявлений. Это были бы нормальные соглашения о форматировании в Java.
rghome
6

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

О мотивах этих последних рефакторингов Фаулер пишет :

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

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

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

Джонатан Хартли
источник
1
Отличный ответ. Лучший код, который я написал (и видел от других), часто имел множество маленьких (2, 3 строки) методов, которые могли бы заменить такие временные переменные. С этим связан спорный вывод, что частные методы воняют ... Хорошо продумано, и я часто находил это правдой.
Стейн де Витт
Темпы в этом вопросе уже относятся к функциям, поэтому этот ответ не имеет отношения к вопросу OP.
user949300
@ user949300 Я не думаю, что это не имеет значения. Да, временные значения в конкретном примере OP - это возвращаемые значения из функций или методов. Но ОП очень ясно, что это просто пример сценария. Фактический вопрос гораздо более общий: «Должны ли мы исключить временные переменные, когда сможем?»
Джонатан Хартли
1
Хорошо, я продан, ** пытался ** изменить свой голос. Но часто, когда я выхожу за пределы ограниченного объема ОП-вопроса, меня раздражают. ТАК может быть непостоянным ... :-).
Э-
@ user949300 Святая корова! Человек, который передумает при вдумчивом рассмотрении вопросов! Вы, сэр, редкость, и я снимаю с вас шапку.
Джонатан Хартли
3

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

acquireWakeLock(POWER_SERVICE, PARTIAL, "abc");

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

leftaroundabout
источник
2

Давайте рассмотрим закон Деметры здесь. В статье Википедии о LoD говорится следующее:

Закон Деметры для функций требует, чтобы метод m объекта O мог вызывать методы только следующих типов объектов: [2]

  1. О себе
  2. параметры м
  3. Любые объекты, созданные / созданные в течение м
  4. Прямые составляющие объекты О
  5. Глобальная переменная, доступная O, в области видимости m

Одним из последствий следования этому Закону является то, что вам следует избегать вызова методов объектов, возвращаемых другими методами, в длинной пунктирной строке, как показано во втором примере выше:

((PowerManager)getSystemService(POWER_SERVICE)).newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"MyWakelockTag").acquire();

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

PowerManager powerManager=(PowerManager)getSystemService(POWER_SERVICE);
WakeLock wakeLock=powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,"abc");
wakeLock.acquire();

ясно показывает, что вы пытаетесь сделать - вы получаете объект PowerManager, получаете объект WakeLock из PowerManager, а затем получаете wakeLock. Вышеизложенное следует правилу № 3 в LoD - вы создаете экземпляры этих объектов в своем коде и, следовательно, можете использовать их, чтобы выполнить то, что вам нужно.

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

Удачи.

Боб Джарвис
источник
3
Мне было бы интересно узнать, как гибкий синтаксис вписывается в этот ответ. При правильном форматировании текучий синтаксис легко читается.
Гусдор
12
Это не то, что означает «Закон Деметры». Закон Деметры - не упражнение по подсчету точек; по сути, это означает «говорите только со своими непосредственными друзьями».
Роберт Харви
5
Доступ к тем же методам и членам через промежуточную переменную все еще является серьезным нарушением закона Деметры. Вы просто скрыли это, а не исправили.
Джонатан Хартли
2
С точки зрения «закона Деметры» обе версии одинаково «плохие».
Халк
@Gusdor согласился, это еще один комментарий к стилю в примере вопроса. Вопрос теперь отредактирован.
Джодрелл
2

Стоит отметить, что код читается часто, на разных «глубинах». Этот код:

PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE);
WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "abc");
wakeLock.acquire();

легко "снять". Это 3 заявления. Сначала мы придумали PowerManager. Тогда мы придумали WakeLock. Тогда мы . Я могу легко это увидеть, просто посмотрев на начало каждой строки; простые присвоения переменных действительно легко частично распознаются как «Type varName = ...» и просто мысленно скользят по «...». Точно так же последнее утверждение, очевидно, не является формой присвоения, но оно включает только два имени, поэтому «основная суть» становится очевидной сразу. Часто это все, что мне нужно знать, если я просто пытаюсь ответить «что делает этот код?» на высоком уровне.acquirewakeLock

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

((PowerManager)getSystemService(POWER_SERVICE))
    .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakelockTag")
    .acquire();

Теперь это все одно утверждение. Структура верхнего уровня не так проста для чтения; в исходной версии OP, без разрывов строк и отступов для визуальной передачи любой структуры, мне пришлось бы считать скобки, чтобы декодировать ее в последовательность из 3 шагов. Если некоторые выражения, состоящие из нескольких частей, вложены друг в друга, а не организованы как цепочка вызовов методов, то все равно это может выглядеть примерно так, поэтому я должен быть осторожен с этим, не считая скобок. И если я доверяю отступу и просто проскакиваю вперед до последней вещи как предполагаемой точки всего этого, что .acquire()мне скажет само по себе?

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

WakeLock wakeLock =
     ((PowerManeger)getSystemService(POWER_SERVICE))
    .newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLockTage");
wakeLock.acquire();

Теперь это говорит о быстром снятии "получить WakeLock, тогда acquireэто". Еще проще, чем в первой версии. Сразу видно, что приобретаемая вещь является WakeLock. Если получение - PowerManagerэто просто деталь, которая довольно незначительна с точки зрения этого кода, но wakeLockважна, тогда это может фактически помочь похоронить PowerManagerматериал, поэтому естественно просмотреть его, если вы просто пытаетесь быстро получить Идея того, что делает этот код. И не называя сообщается , что он будет использоваться только один раз, а иногда это что важно (если остальная часть области очень длинная, мне придется прочитать все это, чтобы узнать, будет ли она когда-либо использоваться снова; хотя использование явных вложенных областей может быть другим способом решения этой проблемы, если ваш язык их поддерживает).

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

Бен
источник
Для меня, хотя я и не знаю API, совершенно очевидно, что делает код, даже без переменных. getSystemService(POWER_SERVICE)получает менеджер по мощности. .newWakeLockполучает замок от пробуждения .acquireприобретает это. Хорошо, я не знаю всех типов, но я могу узнать это в IDE, если мне нужно.
rghome
Для вас может быть очевидным, что должен делать код , но вы понятия не имеете, что он на самом деле делает. Если я ищу ошибку, все, что я знаю, это то, что какой- то код где-то не выполняет то, что должен делать, и я должен найти какой код.
gnasher729
@ gnasher729 Да. Охота на ошибки была примером, который я привел, когда вам нужно будет внимательно прочитать все детали. И одна из вещей, которая действительно помогает найти код, который не выполняет то, что он должен делать, это уметь легко увидеть, каковы были намерения оригинального автора.
Бен
когда я возвращаюсь, я полностью понимаю «одну единицу» и затем могу перейти к следующему утверждению. Вообще-то, нет. На самом деле локальные переменные не позволяют этого полного понимания. Потому что они все еще задерживаются ... занимают ментальное пространство ... что произойдет с ними в следующих 20 высказываниях ... барабанная дробь ... Без местных жителей вы были бы уверены, что объекты пропали и невозможно что-либо сделать с ними в следующих строках. Не нужно помнить о них. Мне нравится returnкак можно быстрее из методов, по той же причине.
Стейн де Витт
2

Это даже антипаттерн с собственным именем: Train Wreck . Уже было указано несколько причин, чтобы избежать замены:

  • Труднее читать
  • Сложнее отлаживать (как для просмотра переменных, так и для обнаружения исключений)
  • Нарушение закона Деметры (LoD)

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

Также помните, что ссылки на объекты действительно дешевы.

Borjab
источник
Эмм, разве пересмотренный код не делает цепочку методов? РЕДАКТИРОВАТЬ прочитайте связанную статью, так что я вижу разницу сейчас. Довольно хорошая статья спасибо!
Стейн де Витт
Спасибо за обзор. В любом случае, цепочка методов может работать в некоторых случаях, в то время как просто выход из переменной может быть подходящим решением. Всегда приятно знать, почему люди не согласны с вашим ответом.
Борхаб
Я думаю, что противоположностью "Train Wreck" является "Local Line". Это тот поезд между двумя крупными городами, который останавливается в каждой деревне между ними на случай, если кто-то захочет сесть или выйти, хотя в большинстве дней никто не хочет.
rghome
1

Поставленный вопрос: «Должны ли мы исключить локальные переменные, если сможем»?

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

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

По большей части локальные переменные облегчают чтение и отладку кода.

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

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

using(SQLconnection conn = new SQLconnnection())
{
    using(SQLcommand cmd = SQLconnnection.CreateCommand())
    {
    }
}
папараццо
источник
Это на самом деле не связано с локальными переменными, но с очисткой ресурсов ... Я не очень хорошо разбираюсь в C #, но я был бы удивлен, если using(new SQLConnection()) { /* ... */ }бы не было также законно (будет ли это полезно, другой вопрос :).
Стейн де Витт
1

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

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

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

Фрэнк Пуффер
источник
2
Я не понизил ваш ответ, но думаю, что это связано с тем фактом, что локальные переменные обычно не считаются «состоянием» в императивных языках, если вы не говорите о каком-то долговременном методе. Я согласен с использованием final / const в качестве передового опыта, но маловероятно, что введение переменной для простого прерывания цепочки вызовов приведет к проблемам, вызванным изменяемым состоянием. Фактически, использование локальных переменных помогает справиться с изменяемым состоянием. Если метод может возвращать разные значения в разное время, в противном случае вы можете получить действительно интересные ошибки.
JimmyJames
@JimmyJames: добавили пояснения к моему ответу. Но есть одна часть вашего комментария, которую я не понял: «использование локальных переменных помогает справиться с изменяемым состоянием». Не могли бы вы объяснить это?
Фрэнк
1
Например, скажем, мне нужно проверить значение и, если оно больше 10, запустить оповещение. Предположим, что это значение происходит от метода getFoo(). Если я избегу локального объявления, я в конечном итоге if(getFoo() > 10) alert(getFoo());получу, но getFoo () может возвращать разные значения для двух разных вызовов. Я мог бы отправить предупреждение со значением, которое меньше или равно 10, что в лучшем случае сбивает с толку и вернется ко мне как дефект. Такие вещи становятся более важными с параллелизмом. Местное назначение является атомным.
JimmyJames
Очень хороший момент. Может быть, причина, по которой мне не нравятся эти местные жители ... Собираетесь ли вы что- то делать с этим PowerManagerэкземпляром после вызова этого метода? Позвольте мне проверить остальную часть метода .... ммм хорошо нет. И эта WakeLockвещь, которую вы получили ... что вы делаете с этим (снова сканируя оставшуюся часть метода) ... ммм снова ничего ... хорошо, так почему они там? О да, это должно быть более читабельным ... но без них я уверен, что вы их больше не используете, поэтому мне не придется читать остальную часть метода.
Стейн де Витт
В недавнем выступлении докладчик утверждал, что лучший способ показать, что переменная является конечной, - это написать короткие методы, в которых очевидно, что переменная не изменяется . Если finalв методе необходима локальная переменная, ваш метод слишком длинный.
user949300