Как вы расставляете скобки, что вы включаете в блоки, как вы записываете логические выражения?.. Задумываетесь ли вы о том, что следуя простым правилам, вы можете не только сделать код более читабельным, но и облегчить отладку, улучшить диагностику, повысить производительность?..
Предлагаю 5К мыслей и ссылки на style guide-ы (далее SG). Надеюсь, что эта статья станет приятным и лёгким чтивом. И я уверен, что она затрагивает далеко не все вопросы и надеюсь на содержательное обсуждение.
Строго говоря, подобные вещи можно написать не только про if. Именно if выбран почти случайно (в конце концов, а почему бы и нет?). Идею этой статьи подкинул f0b0s. Её (идею) поддержали сразу несколько человек, я тоже начал ждать статью; но так и не дождавшись, решил сам поднять это знамя. f0b0s, спасибо за идею, извиняюсь, если опередил, жду комментариев.
Для полноты уделим секунду банальным вещам.
Идеальный вариант писать фигурные скобки везде, а каждое выражение if/else/else_if
причём некоторые SGы рекомендуют писать else на новой строке
Тогда else получается с таким же отступом, что и соответствующий if. Но в этом вопросе нет единства.
Если вы всё же опускаете скобки (в чём нет никакого криминала), то надо учитывать два аспекта. Во-первых, придерживаться одинаковой тактики для блоков if и else:
И, во-вторых, не записывать весь if в одну строку
Смешение стилей в пределах одного if/else затрудняет его чтение, а размещение нескольких выражений в одной строке ещё и затрудняет отладку; в последнем примере cond() и act() расположены в одной строке; это затрудняет поиск ошибок по номером срок и отладку.
Используйте преимущества С++. Если переменная нужна только в блоке, то и создавайте её только в блоке.
Это сбережёт процессорное время, память… в общем всё, что так дорого каждому человеку, исповедующему гуманистические идеалы :-)
В выражениях if/else сперва описывайте нормальный ход событий, а в блок else выносите обработку нештатных ситуаций. Это упрощает чтение программы:
Прежде всего, в C++ не стоит следовать стилю C писать
Стандарт С++ не гарантирует, что float(0) и даже int(0) реализуются как «все биты 0» (хотя, конечно, это работает, но это чисто случайно :-)).
Если вам нужно проверить на равенство 0, то лучше написать именно это:
В качестве условия следует использовать только логические величины. Это же, кстати относится и к циклам:
Если логическое выражение состоит из нескольких частей, то самой плохой практикой является запись их всех в кучу. Можно ли быстро понять, что тут написано?
Подсветка, конечно, помогла бы, но и без подсветки следующее выражение читается на много проще:
Но совсем идеально, ввести в программу дополнительные переменные
Такую программу на много проще читать, и кроме того, переменные isInside и isGreen могут пригодиться в дальнейшем.
А для пущей читабельности, можно поставить скобки вокруг логических выражений в первых двух строчках.
Выражения лучше выносить из условий
Это делает код на много читабельный, чем
Кроме того, это перекликается с общей идеей по-возможности разгружать логическое выражение, которая уже была высказана выше.
Ну и, конечно, «каждому выражению своя строка!».
Опять же, сложно читать, сложно отлаживать, сложно локализовать ошибки.
По уже изложенным причинам, в логических выражениях лучше не выполнять присвоения:
Это приводит ко множеству бед. Разрастание логического выражения, размещение множества вызовов в одной строке. Появлению рядом двух похожих (внешне) операторов "=" и "==", что дополнительно затрудняет чтение и увеличивает вероятность ошибок и опечаток.
Очень частой ошибкой, является написание одного "=", вместо двух ("=="). Чтобы компилятор сразу же дёрнул вас за руку, некоторые люди вырабатывают привычку писать сравнения наоборот (признаюсь, я не смог её выработать в себе :-))
Если писать не
а
то вы можете не бояться опечатать и написать "=" вместо "==". В хорошем варианте программа просто не скомпилится, если вы допустите такую опечатку.
Если что-то забыл, спасибо за комментарии :-)
Пара хороших SG:
Классический SG. Обратите внимание на ссылки внизу — там есть ещё более обширные документы.
SG от Google. Стоит изучить, так как она не во всём солидарна с вышеупомянутой SG.
upd Спасибо всем прокомментировавшим! Обсуждение (как я и ожидал) получилось интересней статьи :-) Я не вношу замечания в статью по двум причинам. Многие замечания (равносправедливые) противоречат друг другу (что не удивительно, при обсуждении подобных тем). И второе, если я внесу все правки, то обсуждение станет совершенно непонятным и статья утратит свою лучшую часть :-) Пусть уж всё остаётся как есть и «ошибки»(?) допущенные в статье станут иллюстрацией к обсуждению.
Предлагаю 5К мыслей и ссылки на style guide-ы (далее SG). Надеюсь, что эта статья станет приятным и лёгким чтивом. И я уверен, что она затрагивает далеко не все вопросы и надеюсь на содержательное обсуждение.
Строго говоря, подобные вещи можно написать не только про if. Именно if выбран почти случайно (в конце концов, а почему бы и нет?). Идею этой статьи подкинул f0b0s. Её (идею) поддержали сразу несколько человек, я тоже начал ждать статью; но так и не дождавшись, решил сам поднять это знамя. f0b0s, спасибо за идею, извиняюсь, если опередил, жду комментариев.
Для полноты уделим секунду банальным вещам.
Фигурные скобки
Идеальный вариант писать фигурные скобки везде, а каждое выражение if/else/else_if
// ХОРОШО if (a == b) { foo(); } else { bar(); }
причём некоторые SGы рекомендуют писать else на новой строке
// Не } else { // а } else { // или } else {
Тогда else получается с таким же отступом, что и соответствующий if. Но в этом вопросе нет единства.
Если вы всё же опускаете скобки (в чём нет никакого криминала), то надо учитывать два аспекта. Во-первых, придерживаться одинаковой тактики для блоков if и else:
// ПОХУЖЕ if (a == b) { foo(); bar(); } else bazzz();
И, во-вторых, не записывать весь if в одну строку
// СОВСЕМ ПЛОХО if (cond()) act(); else err();
Смешение стилей в пределах одного if/else затрудняет его чтение, а размещение нескольких выражений в одной строке ещё и затрудняет отладку; в последнем примере cond() и act() расположены в одной строке; это затрудняет поиск ошибок по номером срок и отладку.
И ещё два замечания о блоках
Создавайте переменные только при необходимости
Используйте преимущества С++. Если переменная нужна только в блоке, то и создавайте её только в блоке.
// ПЛОХО SomeClass p; // нужен только в блоке if (x == y) { p.moveTo(x, y); p.logResultsOfMovement(); }
// ХОРОШО if (x == y) { SomeClass p; // нужен только в блоке p.moveTo(x, y); p.logResultsOfMovement(); }
Это сбережёт процессорное время, память… в общем всё, что так дорого каждому человеку, исповедующему гуманистические идеалы :-)
Описывайте сперва нормальный ход событий
В выражениях if/else сперва описывайте нормальный ход событий, а в блок else выносите обработку нештатных ситуаций. Это упрощает чтение программы:
// ХОРОШО bool errorFlag = openConnection(); if (!errorFlag) { doJob(); } else { dropJob(); }
Логические выражения
C++ не С
Прежде всего, в C++ не стоит следовать стилю C писать
// ПЛОХО if (float_num) {} if (int_num) {}
Стандарт С++ не гарантирует, что float(0) и даже int(0) реализуются как «все биты 0» (хотя, конечно, это работает, но это чисто случайно :-)).
Если вам нужно проверить на равенство 0, то лучше написать именно это:
// ХОРОШО if (float_num != .0) {} if (int_num != 0) {}
В качестве условия следует использовать только логические величины. Это же, кстати относится и к циклам:
// ХОРОШО while (true) {} // ПЛОХО while (1) {} for (;;) {}
Разделяйте логические выражения на части
Если логическое выражение состоит из нескольких частей, то самой плохой практикой является запись их всех в кучу. Можно ли быстро понять, что тут написано?
// ПЛОХО if (figureX > leftLimit && figureX < fightLimit && figureColor == GREEN)
Подсветка, конечно, помогла бы, но и без подсветки следующее выражение читается на много проще:
// ХОРОШО if (figureX > leftLimit && figureX < fightLimit && figureColor == GREEN)
Но совсем идеально, ввести в программу дополнительные переменные
// ОЧЕНЬ МИЛО bool isInside = figureX > leftLimit && figureX < fightLimit; bool isGreen = figureColor == GREEN; if (isInside && isGreen)
Такую программу на много проще читать, и кроме того, переменные isInside и isGreen могут пригодиться в дальнейшем.
А для пущей читабельности, можно поставить скобки вокруг логических выражений в первых двух строчках.
Выражения в условиях
Выражения лучше выносить из условий
// ХОРОШО bool dataReaded = readDdata(fileName); if (dataReaded) {}
Это делает код на много читабельный, чем
// ПЛОХО if (readDdata(fileName)) {}
Кроме того, это перекликается с общей идеей по-возможности разгружать логическое выражение, которая уже была высказана выше.
Ну и, конечно, «каждому выражению своя строка!».
// СОВСЕМ ПЛОХО if (readDdata(getFileNameFromArray(getEnvAsArray()))) {}
Опять же, сложно читать, сложно отлаживать, сложно локализовать ошибки.
Присвоения в условиях
По уже изложенным причинам, в логических выражениях лучше не выполнять присвоения:
// ПЛОХО if (lid = getLabel() == curentLabel())
Это приводит ко множеству бед. Разрастание логического выражения, размещение множества вызовов в одной строке. Появлению рядом двух похожих (внешне) операторов "=" и "==", что дополнительно затрудняет чтение и увеличивает вероятность ошибок и опечаток.
Очень частой ошибкой, является написание одного "=", вместо двух ("=="). Чтобы компилятор сразу же дёрнул вас за руку, некоторые люди вырабатывают привычку писать сравнения наоборот (признаюсь, я не смог её выработать в себе :-))
Если писать не
// ПЛОХО? if (x == 1)
а
// ХОРОШО? if (1 == x)
то вы можете не бояться опечатать и написать "=" вместо "==". В хорошем варианте программа просто не скомпилится, если вы допустите такую опечатку.
Кажется ничего не забыл
Если что-то забыл, спасибо за комментарии :-)
Пара хороших SG:
Классический SG. Обратите внимание на ссылки внизу — там есть ещё более обширные документы.
SG от Google. Стоит изучить, так как она не во всём солидарна с вышеупомянутой SG.
upd Спасибо всем прокомментировавшим! Обсуждение (как я и ожидал) получилось интересней статьи :-) Я не вношу замечания в статью по двум причинам. Многие замечания (равносправедливые) противоречат друг другу (что не удивительно, при обсуждении подобных тем). И второе, если я внесу все правки, то обсуждение станет совершенно непонятным и статья утратит свою лучшую часть :-) Пусть уж всё остаётся как есть и «ошибки»(?) допущенные в статье станут иллюстрацией к обсуждению.