Чи може хтось кинути виклик дядькові Бобу за його любов до зняття «марних брекетів»?


94

Я ненавиджу посилатися на вміст, що платить, але це відео показує саме те, про що я говорю. Точно 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);

У нього немає такої ж візуальної проблеми, як помилка в стилі iOS SSL, але економить навіть більше "непотрібних" ліній, ніж дядько Боб;) Читає добре, якщо ви звикли до цього, і в ньому використовується звичайне використання в Ruby ( includePage(mode, suiteSetup) unless suiteSetup.nil?). Ну добре, просто знайте, що думок багато.


13
Крім того, якщо ви використовуєте } else[if(...)] {, це не додає додаткових додаткових рядків, тому він додає лише один додатковий рядок у всій справі.
Випадково832

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

4
Щодо помилки "goto fail", то варто зазначити, що інші аспекти стилю кодування дядька Боба також завадили б цьому, що найголовніше - його сильна перевага для надзвичайно коротких методів. Він ніколи б не допустив методу 79 рядків в першу чергу, і якби метод був розбитий на більш дрібні (а) конфлікт злиття, який спричинив дублювання, мабуть, ніколи б не стався і (b) простіший контроль потоку в методі ймовірно, це означало б, що будуть створені попередження компілятора щодо недосяжного коду, який мав би запобігти проблемі.
Жуль

16
"goto fail" не був викликаний однорядковими блоками, це було викликано неохайним програмуванням, навіть оглядом коду sloppier і навіть не один раз переходити через код вручну.
gnasher729

35
Мех, відсутність брекетів не викликала цієї помилки. Захоплюючі програмісти (і відсутність будь-якого змістовного перегляду, тестування коду). Вирішіть проблему, звільнивши відповідальних, а не зв’язуючи руки решти нас!
Гонки легкості на орбіті

62

У дядька Боба багато шарів захисту від такої помилки, яка була не такою звичною, коли "завжди використовуйте брекети" була переважаючою мудрістю:

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

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


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

9
Чи є у нас автоматизований спосіб перевірки, що includePage()це не погано написаний макрос з більш ніж одним твердженням?
Мартін Йорк

51
@LokiAstari Так, це називається "У Java немає макросів".
svick

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

16
@Jules О так! ВСІ продукти, які я отримав або випустив на роботу, мають 100% охоплення тестом (як мінімум), рецензують (кілька разів), і всі справи навколо відділів програмного забезпечення QA. Так. Напевно, ви знайшли те саме у своїй професійній кар’єрі, адже в кожному бізнесі є команди програмістів замість одиноких програмістів, і вони дають їм більше ніж достатньо часу, щоб зробити все тестування, яке вони хотіли б зробити. Так, це ідеально описує всі робочі місця. Чому варто турбуватися про додавання додаткових помилок, коли ми впевнені, що наше програмне забезпечення ЗАВЖДИ на 100% вільне?
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 (svn, git, hg ... і т. Д.), Покаже, ніби весь рядок змінився, навіть із вигадливими інструментами, іноді вам може знадобитися швидко переглянути звичайний рядок -оснований Dif, щоб побачити, що змінилося.
  • інструменти для анотацій (такі як git blame) показуватимуть рядок як змінений, зробивши відстеження походження рядка ще кроком до пошуку реальних змін.

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

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


Є виняток із цього, для баз кодів, які мають {свій рядок - це не проблема.

Аргумент різного шуму не дотримується, якщо ви пишете в цьому стилі:

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

Однак це не така поширена конвенція, тому головним чином додається відповідь на повноту (не пропонуючи проекти використовувати цей стиль) .


8
Мені подобається, що коментований шум зменшується, що є визначальним плюсом
Мартін Йорк

2
Якби тільки кожен, хто злий на JSON, мав будь-який розгляд щодо різного шуму! Я дивлюся на тебе, правило без останньої коми.
Роман Старков

1
Так, я не вважав перевагу {різного шуму стилю -on-its-own-line. Я дуже ненавиджу цей стиль і не люблю працювати над проектами, де потрібен цей стиль. Можливо, маючи це на увазі, я вважаю, що трохи менше засмучує необхідність кодувати цей спосіб. Важлива щільність коду. Якщо ви зможете побачити всю функцію на моніторі одразу, ви можете легше продемонструвати всю справу. Мені подобається залишати порожні рядки між окремими блоками коду всередині функції для читабельності (до групових висловлювань), а змушення витрачати місця в інших місцях робить передбачувані перерви слабкішими.
Пітер Кордес

1
@ peter-cordes, також не шанувальник {стилю -on -its-own-line, включив його лише для повноти відповідей. Що стосується довіри макросів для використання do{}while(0), то я вважаю, що проблема полягає в тому, що ви можете довіряти їй майже весь час. Проблема полягає в тому, що трапляються нещасні випадки, помилки можуть ковзати за допомогою перегляду коду ... і можуть бути введені помилки, що не було б інакше.
ideaman42

2
Якщо ми перераховуємо потенційні помилки, можливість коментувати окремі рядки - ще одна добра річ. Я відстежував помилку, яку викликав хтось, сліпо коментуючи тіло однорядного if-заяви. Наступне твердження тоді було умовно виконане, а не безумовно виконане.
сир Eldritch

15

Роки тому мене привезли, щоб налагодити якийсь код С. Помилку було важко знайти, але врешті-решт він звів до твердження на зразок:

if (debug)
   foo (4);

І виявилося, що людина, яка її написала, визначила fooяк макрос. Макрос з двома рядками коду в ньому. І звичайно, тільки перший із цих двох рядків підлягав цьому if. (Отже, другий рядок був виконаний беззастережно.)

Це може бути абсолютно унікальним для C та його препроцесора - що робить заміни перед компіляцією - але я ніколи цього не забував. Така річ залишає слід на вас, а чому б не захистити її, особливо якщо ви використовуєте різноманітні мови та ланцюжки інструментів і не можете бути впевнені, що подібні шенаги неможливі в іншому місці?

Зараз я відступаю і вживаю брекети на відміну від усіх інших, мабуть. Для одного рядка ifя б зробив:

if (debug) { foo (4); }

тому для включення брекетів не потрібні додаткові рядки.


6
У цьому випадку той, хто написав, що макрос, той винен.
gnasher729

6
Написання макросів препроцесора C може бути не інтуїтивно зрозумілим, але це можна зробити. І це слід робити, як вказує gnasher729. І ваш приклад є причиною того, що будь-який макрос, який містить більше одного оператора, повинен включати його тіло в do { ... } while(0)цикл. Це не тільки забезпечить правильне поводження з ним, додаючи if()висловлювання, але й правильну проковтнення крапки з комою в кінці заяви. Хто не знає про такі прості правила, просто не повинен писати препроцесорні макроси. Захист на виклику - неправильне місце для захисту.
cmaster

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

@ gnasher729, кого байдуже, чи не чиясь вина? Якщо ви робите перевірку коду та дотримуєтесь деяких розумних стандартів коду, це може бути помилковим для вашої команди. І якщо він дійде до користувачів, вони звинуватить організацію у випуску програмного забезпечення.
ideaman42

1
Хто придумав макроси, той винен.
Нема

14

"Дядьку Бобу" дозволяється мати свою думку, вам дозволяється мати свою думку. Не потрібно йому кидати виклик.

Якщо ви хочете звернутися до влади, візьміть Кріса Леттнера. У Swift, якщо заяви втратили дужки, але завжди поставляються дужками. Ніяких дискусій, це частина мови. Тож якщо "Дядько Боб" починає видаляти брекети, код припиняється збирати.

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

Це здебільшого нешкідливий стиль дискусії. Перевага має перевагу в тому, що ви можете вставити інший рядок коду без додавання дужок. Як заява журналу, або коментар (якщо за ним йде коментар, то за твердженням це просто жахливо). заява в тому ж рядку, ніби має практичний недолік, що у вас проблеми з багатьма налагоджувачами. Але робіть все, що вам зручніше.


1
Я здогадуюсь, що це "складне", тому що UB (sic!) Представляє свою думку як факти - принаймні це вражає мене так, коли я його слухаю.
vaxquis

3
Говорити "Робіть все, що вам зручніше" - це насправді домовитися з дядьком Бобом з цього приводу, тому що це те, що він стверджує, "це не має значення". Для багатьох мов це питання не стосується. Раніше я думав у всіх тих, де це ВИНАГИ слід використовувати брекети, і не бачив, щоб хтось кидав цьому виклик. Тепер ось дядько Боб кидає виклик цьому, і мені цікаво, чи не вдається йому це втекти, тому що він працює в таких сильно реконструйованих крихітних методах або тому, що він повний цим. Я не вважав це нешкідливим саме через помилки, які у своїй відповіді згадує @PaulDraper.
candied_orange

Знову завжди : синтаксичний шум відволікає, а додатковий "порожній" рядок для близької дужки додає візуальний роздільник в абзаці, де лінії не повинні бути розділені один від одного, що ще більше заважає читабельності. Це особливо важливо в стислих крихітних блоках, які ви згадуєте.
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
Дядько Боб, напевно, думає, що він пише кращий код швидше без них.
djechlin

2
Як і я, і інші вільно володію C ++.
JDługosz

1
"якщо це дозволяє йому швидше писати код, це лише один привід тримати їх" ++ У мене є молодший, який сказав те саме, таким чином ми їх зберігаємо.
RubberDuck

... і ось тому лише причина робити це так, як хоче дядько Боб.
djechlin

-1

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

Сказавши, що, коли ви в команді, усунення брекетів, певно, призведе до несподіваної поведінки одного дня. Я кажу, тримайте їх, і ви заощадите собі багато головних болів, коли все піде не так.


це, здається, не пропонує нічого суттєвого щодо питань, викладених та пояснених у попередніх 10 відповідях
гнат

-3

Мене вчили не робити цього, тому що це легко вводити помилку, додаючи ще одну з відступів, думаючи, що це контролюється, якщо це не так.

Якщо ваш код добре перевірений , цей «помилка» вибухне обличчям.

Чистка коду, уникаючи непотрібних і некрасивих дужок - це практика, яку я дотримуюся.


9
Зрозуміло, і зворотне: і якщо ваш код не містить помилок, вам не потрібні одиничні тести. Реальність полягає в тому, що ми живемо в недосконалому світі, де іноді є помилки в коді, а іноді і помилки в тестах. (На мій досвід, останні насправді є більш поширеними.) Тож тести, безумовно, знижують ризик виникнення помилок, це не є приводом для пошуку інших способів знову підвищити ризик.
ruakh

3
Щоб додати коментар ruakh: неможливо перевірити код на 100% одиниці.
BЈовић

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

@ Mik378 це, мабуть, неправда. Справа в тому, що якщо ви використовували брекети, вам навіть не доведеться турбуватися про це.
GoatInTheMachine
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.