Кто-нибудь может бросить вызов дяде Бобу за его любовь к удалению «бесполезных скобок»?


94

Я ненавижу ссылаться на контент Paywalled, но это видео показывает именно то, о чем я говорю. Точно 12 минут Роберта Мартина выглядит так:

Некоторый код

И говорит: «Одна из моих любимых вещей - избавиться от бесполезных скобок», когда он превращает это в это:

Больше кода

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

Честно говоря, дядя Боб реорганизует длинный Java-метод, превращая его в крошечные маленькие функции, которые, я согласен, гораздо более читабельны. Когда он закончил изменять его, (22.18) это выглядит так:

Еще больше кода

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


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

35
Дайте ему несколько лет, возможно, он тоже уберет бесполезный разрыв строки и "если (видите (что-то)) скажет (что-то);" на самом деле будет строка кода ;-)
Стив Джессоп

8
@SteveJessop Приятно слышать, что я впереди. : D Без новой строки нет риска печально известной ошибки. Добавление / удаление фигурных скобок по мере необходимости - это дополнительные издержки, но при чтении сохраняется гораздо больше.
Maaartinus

9
Дядя Боб делает несколько хороших замечаний для этого в Чистом коде, стр. 35. Если ifблок состоит всего из одной строки, это не имеет значения. Если вам нужно добавить больше строк, это должна быть другая функция, которая сокращает ifблок до одной строки (вызов функции). Если вы придерживаетесь этого, то брекеты просто не имеют значения - вообще .

13
он должен просто делать это, return (page==null) ? "" : includePage(mode,page);если он так сильно увлекается ... Я думал, что стиль без скобок - это круто, пока я не начал профессионально разрабатывать приложения. Различный шум, возможные ошибки опечаток и т. Д. Скобки, будучи там все время, экономят ваше время и накладные расходы, которые вам понадобятся, чтобы представить их позже.
vaxquis

Ответы:


47

Читаемость не маленькая вещь.

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

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

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

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


7
Я был немного обеспокоен этим слишком недавно, когда отступы были ошибочными и вводили в заблуждение.
Энди

12
Потому что это был C без GCC 6-Wmisleading-indentation .
wchargin

2
@CandiedOrange: Это дядя Боб бросает вызов установленной догме.
Роберт Харви

1
@RobertHarvey Разве я не прояснил это? Да, он бросает вызов догме. Он бросает вызов МОЕЙ догме. Я просто пытаюсь быть хорошим учеником и, по крайней мере, обдумать это. Но дядя Боб настолько чертовски харизматичен, что заставляет задуматься, теряю ли я объективность. Поэтому я прошу помощи, чтобы найти противоядие, прежде чем выпить коалаид.
candied_orange

1
@CandiedOrange: Это абсолютно контекстная вещь. Те, кто просто слепо принимают догму, не учитывают контекст; они все еще используют правило «только один возврат» в управляемых языках, еще долго после того, как правило изжило свою полезность и фактически стало препятствием.
Роберт Харви

133

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

Отойдя от велосипедных сараев, помните отличную ошибку OS X / iOS SSL 2014 года?

if ((err = SSLHashSHA1.update(&hashCtx, &serverRandom)) != 0)
    goto fail;
if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)
    goto fail;
    goto fail;
if ((err = SSLHashSHA1.final(&hashCtx, &hashOut)) != 0)
    goto fail;

Да, "вызвано" блоками без скобок https://www.imperialviolet.org/2014/02/22/applebug.html

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

if (suiteSetup)
{
    includePage(mode, suiteSetup);
}

Я мог бы быть склонен к экономии места тоже. Но

if (suiteSetup) {
    includePage(mode, suiteSetup);
}

использует только одну «лишнюю» строку.


Я знаю, что ты не спрашивал, но если я работаю один, я еретик. Если я снимаю брекеты, я предпочитаю

if (suiteSetup != null) includePage(mode, suiteSetup);

Он не имеет той же визуальной проблемы, что и ошибка в стиле SSL в iOS, но сохраняет еще больше «ненужных» строк, чем это делает дядя Боб;) Хорошо читает, если вы к этому привыкли, и широко используется в Ruby ( includePage(mode, suiteSetup) unless suiteSetup.nil?). Ну да ладно, просто знай, что мнений много.


13
Кроме того, если вы используете } else[if(...)] {, это не добавляет никаких дополнительных строк, так что в целом это добавляет только одну дополнительную строку.
Random832

12
Я рад, что вы подняли ошибку iOS SSL. С тех пор я все чаще и чаще использую брекеты в таких ситуациях.
Зак Липтон

4
Что касается ошибки «goto fail», то стоит отметить, что другие аспекты стиля кодирования дяди Боба также помешали бы этому, что наиболее важно его сильное предпочтение чрезвычайно коротких методов. Во-первых, он никогда не допустил бы метод с 79 строками, и если бы метод был разбит на более мелкие (а) конфликт слияния, вызвавший дублирование, вероятно, никогда бы не произошел, и (б) более простое управление потоком в методе Скорее всего, это означало бы, что предупреждения компилятора о недоступном коде были бы сгенерированы, что должно было предотвратить проблему.
Жюль

16
«goto fail» не был вызван однострочными блоками, он был вызван неаккуратным программированием, даже небрежным просмотром кода, и ни разу не выполняя код вручную.
gnasher729

35
Мех, отсутствие фигурных скобок не привело к этой ошибке. Пугающие программисты (и отсутствие какого-либо осмысленного обзора кода, тестирования) сделали. Исправьте проблему, уволив ответственных, а не связывая руки остальным!
Гонки легкости на орбите

62

У дяди Боба есть много уровней защиты от такой ошибки, которая не была настолько распространенной, когда «всегда использовать фигурные скобки» было преобладающей мудростью:

  1. Сильное личное предпочтение однострочных условных выражений, поэтому многострочные условно выделяются и подвергаются дополнительной проверке.
  2. Редактор, который автоматически выделяется при вставке второй строки.
  3. Полный набор юнит-тестов, которые он запускает постоянно.
  4. Полный набор интеграционных тестов.
  5. Рецензирование сделано до слияния его кода.
  6. Перезапуск всех тестов сервером непрерывной интеграции.
  7. Тестирование выполнено отделом качества продукции.

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


16
Я никогда не боялся краха, как я боюсь вещей, которые молча терпят неудачу. По крайней мере, вы знаете, когда происходит сбой. Задняя часть моего мозга покалывает, когда я вижу это. Он покалывает так же, как и когда я вижу while(true);{break;}. Если для этого действительно есть действительный контекст, мне понадобится время, чтобы его преодолеть.
candied_orange

9
Есть ли у нас автоматический способ проверки, который includePage()не является плохо написанным макросом с более чем одним оператором?
Мартин Йорк,

51
@LokiAstari Да, это называется "Java не имеет макросов".
свик

11
Все вышеперечисленное - это больше «способы избежать того, что провалы, связанные с политикой« бесполезных скобок », причиняют мне боль», чем «причины использовать« политику бесполезных скобок ». Факты, которые вам нужны (особенно № 1), следует рассматривать скорее как несчастье, а не как преимущество.
SJuan76

16
@ Джулс О да! ВСЕ продукты, которые я получил или выпустил на работе, имеют 100% тестовое покрытие (по крайней мере), проверены коллегами (несколько раз), и у всего бизнеса есть отделы Software QA. Да. Я полагаю, вы нашли то же самое в своей профессиональной карьере, потому что в каждом бизнесе есть команды программистов, а не отдельные программисты, и они дают им более чем достаточно времени, чтобы выполнить все тестирование, которое они хотели бы провести. Да, это прекрасно описывает все рабочие места. Зачем беспокоиться о добавлении некоторых дополнительных ошибок, если мы уверены, что наше ПО ВСЕГДА поставляется без ошибок?
SJuan76

31

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

Возможные ошибки

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

Действительный код (который читается как это может быть ошибка)

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

Начнем с:

if (foo)
    bar();

Разработчик добавляет полезный комментарий.

if (foo)
    // At this point we know foo is valid.
    bar();

Позже разработчик расширяет его.

if (foo)
    // At this point we know foo is valid.
    // This never fails but is too slow even for debug, so keep disabled.
    // assert(is_valid(foo));
    bar();

Или добавляет вложенный блок:

if (foo)
    while (i--) {
        bar(i);
        baz(i);
    }

Или использует макрос:

if (foo)
    SOME_MACRO();

«... Поскольку макросы могут определять несколько строк кода, используется ли макрос do {...} while (0)для нескольких строк? Это необходимо, потому что это в нашем руководстве по стилю, но я лучше проверю на всякий случай!»


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

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

Диффузный шум

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

То есть меняется:

if (foo)
    bar();

Для того, чтобы:

if (foo) {
    bar();
    baz();
}

... заставляет условную строку отображаться в diff как измененную, это добавляет небольшие, но ненужные накладные расходы.

  • линии отображаются как измененные в обзорах кода, если ваши инструменты сравнения основаны на словах, вы можете легко увидеть, что изменилась только фигурная скобка, но требуется больше времени, чтобы проверить, изменилась ли строка вообще.
    Тем не менее, не все инструменты поддерживают дифференцирование на основе слов, diff (svn, git, hg ... и т. Д.) Будет отображаться так, как будто вся строка изменилась, даже с помощью необычных инструментов, иногда вам может понадобиться быстро просмотреть простую линию на основе различий, чтобы увидеть, что изменилось.
  • Инструменты аннотации (например, git blame) покажут линию как измененную, делая отслеживание происхождения линии еще одним шагом, чтобы найти реальное изменение.

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

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


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

Дифф шум аргумент не имеет места , если вы пишете в этом стиле:

if (foo)
{
    bar();
    baz();
}

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


8
Мне нравится снижение шума, которое комментируется, что является определенным плюсом.
Мартин Йорк,

2
Если бы все, кто без ума от JSON, имели какое-либо отношение к разным шумам! Я смотрю на тебя, правило без запятой.
Роман Старков

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

1
@ peter-cordes, также не фанат стиля {-on-own-own-line, включил его только для полноты ответов. Что касается доверия к макросам do{}while(0), я обнаружил, что проблема в том, что вы можете доверять ему почти все время. Проблема в том, что аварии случаются, ошибки могут ускользнуть из-за проверки кода ... и могут быть обнаружены ошибки, которых не было бы иначе.
ideasman42

2
Если мы перечисляем потенциальные ошибки, возможность комментировать отдельные строки - это еще одна хорошая вещь. Я обнаружил ошибку, которая была вызвана тем, что кто-то вслепую комментировал тело однострочного оператора if. Следующее утверждение было тогда выполнено условно, а не безоговорочно выполнено.
Eldritch Cheese

15

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

if (debug)
   foo (4);

И оказалось, что человек, который написал это, определил fooкак макрос. Макрос с двумя строками кода. И, конечно, только первая из этих двух строк была предметом if. (Таким образом, вторая строка была выполнена безоговорочно.)

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

Теперь я делаю отступы и использую фигурные скобки не так, как все остальные. Для одной строки ifя бы сделал:

if (debug) { foo (4); }

поэтому не требуется никаких дополнительных строк для включения скобок.


6
В этом случае тот, кто написал этот макрос, виноват.
gnasher729

6
Правильное написание макросов препроцессора C может быть не интуитивно понятным, но это можно сделать. И это должно быть сделано, как указывает gnasher729. И ваш пример - причина, по которой любой макрос, содержащий более одного оператора, должен включать свое тело в do { ... } while(0)цикл. Это не только гарантирует, что оно всегда будет корректно обрабатываться вложенными if()операторами, но также и правильное проглатывание точки с запятой в конце оператора. Тот, кто не знает о таких простых правилах, просто не должен писать макросы препроцессора. Защита на вызывающем сайте - это неправильное место для защиты.
Мастер

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

@ gnasher729, кого это волнует, если кто-то виноват? Если вы делаете рецензии кода и соблюдаете некоторые разумные стандарты кода, это может быть и вашей командой. И если это дойдет до пользователей, они будут обвинять организацию, выпускающую ошибочное программное обеспечение.
ideasman42

1
Тот, кто придумал макросы, виноват.
NEME

14

«Дяде Бобу» разрешено иметь свое мнение, вам разрешено иметь свое мнение. Не нужно бросать ему вызов.

Если вы хотите обратиться к власти, возьмите Криса Латтнера. В Swift, если операторы потеряли свои скобки, но всегда идут с фигурными скобками. Без обсуждения, это часть языка. Так что, если «Дядя Боб» начинает удалять фигурные скобки, код перестает компилироваться.

Просматривать чужой код и «избавляться от бесполезных скобок» - плохая привычка. Только вызывает дополнительную работу, когда код должен быть проверен, и когда ненужные конфликты создаются. Может быть, «Дядя Боб» такой невероятно хороший программист, что ему не нужны обзоры кода? Я потратил впустую одну неделю своей жизни, потому что один главный программист изменил «if (p! = NULL)» на «if (! P)» без проверки кода, скрытого в худшем из возможных мест.

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


1
Я предполагаю, что это «вызов», потому что UB (sic!) Представляет свое мнение как факты - по крайней мере, мне так кажется, когда я его слушаю.
vaxquis

3
Сказать «Делай, что хочешь» - значит согласиться с дядей Бобом в этом, потому что он утверждает, что «это не имеет значения». Во многих языках это не проблема. Ранее я думал о том, что это проблема, которую вы ВСЕГДА должны использовать в скобках, и никто не видел, чтобы кто-то оспаривал это. Теперь вот дядя Боб оспаривает это, и мне интересно, сходит ли ему с рук это, потому что он работает с такими сильно переработанными крошечными методами или потому, что он полон этого. Я не думал об этом как о безвредном именно из-за ошибок, которые @PaulDraper упоминает в своем ответе.
candied_orange

Re всегда : синтаксический шум отвлекает внимание и дополнительная «пустая» линия для закрывающей фигурной скобки добавляет визуальный разделитель в пункте , где линии не должны быть разделены другом от друга, дополнительно затрудняет читаемость. Это особенно важно в кратких крошечных блоках, которые вы упоминаете.
JDługosz

9

Мои причины не снимать брекеты:

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

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


0

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

FWIW, мы согласились на компромиссе , что ставит их на одной линии не делает такие короткие предпосылки ООН читаемыми / отвлекая мне. Например

if (!param) { throw invalid_argument (blahblah); }
if (n < 2) { return 0; }
// real code for function starts here...

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

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


1
Дядя Боб, вероятно, думает, что он быстрее пишет лучший код без них
Джечлин

2
Как и я, и другие свободно говорят на C ++.
JDługosz

1
«если это позволяет ему быстрее писать лучший код, то это единственное основание для их сохранения». ++ У меня есть младший, который сказал то же самое, поэтому мы сохраняем их.
RubberDuck

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

-1

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

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


кажется, это не дает ничего существенного по сравнению с замечаниями, сделанными и объясненными в предыдущих 10 ответах
комнат

-3

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

Если ваш код хорошо протестирован модулем , этот «баг» взорвется прямо в лицо.

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


9
Конечно, верно и обратное: если ваш код не содержит ошибок, вам не нужны юнит-тесты. Реальность такова, что мы живем в несовершенном мире, где иногда бывают ошибки в коде, а иногда ошибки в тестах. (По моему опыту, последние на самом деле более распространены.) Таким образом, хотя тесты, безусловно, снижают риск ошибок, это не оправдывает поиск других способов повышения риска снова.
Руах

3
Добавьте к комментарию Руаха: 100% юнит-тестирование кода невозможно.
BЈовић

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

@ Mik378 это неправдоподобно. Дело в том, что если вы использовали брекеты, вам даже не пришлось бы об этом беспокоиться.
GoatInTheMachine
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.