Руководство по стилю программирования и конструированию по
Скачать 7.6 Mb.
|
ГЛАВА 31 Форматирование и стиль 721 Отступы Применяйте отступы для демонстрации логической структуры про- граммы. Как правило, операторы выделяются отступами, когда они следуют пос- ле некоторого выражения, от которого они логически зависят. Существуют данные, что отступы влияют на способность программиста понимать код. В статье «Program Indentation and Comprehensibility» со- общается, что некоторые исследования выявили корреляцию между на- личием отступов и способностью к пониманию кода (Miaria et al., 1983). В тесте на понимание испытуемые показали результат на 20–30% лучше, когда програм- мы использовали схему отступов из 2–4-х пробелов, чем когда программы вооб- ще не содержали отступов. То же исследование выявило, что нельзя не только недостаточно выде- лять, но и чрезмерно подчеркивать логическую структуру программы. Меньше всего баллов за понимание получили программы, совсем не со- держащие отступов. Второй с конца результат принадлежал программам, исполь- зующим отступы из 6 пробелов. Авторы пришли к выводу, что оптимальными яв- ляются отступы из 2–4-х пробелов. Интересно, что многим испытуемым отступы из 6 пробелов показались удобнее, чем другие, даже несмотря на то, что оконча- тельный результат оказался хуже. Возможно, это связано с тем, что отступы из 6 пробелов выглядят приятнее. Но независимо от того, насколько красиво они вы- глядят, отступы из 6 пробелов оказались хуже читаемыми. Это один из примеров коллизии между эстетикой и читабельностью. Скобки Используйте скобки чаще, чем вам это кажется необходимым. Применяйте скоб- ки для разъяснения выражений, состоящих из двух и более членов. Возможно, в скобках нет нужды, но они добавляют ясности и ничего вам не стоят. Например, скажите, как вычисляются следующие выражения? Вариант на C++: 12 + 4% 3 * 7 / 8 Вариант на Microsoft Visual Basic: 12 + 4 mod 3 * 7 / 8 Пришлось ли вам задуматься о том, как эти выражения вычисляются, вот в чем вопрос? Можете ли вы быть уверенными в своем ответе без обращения к спра- вочной информации? Даже опытные программисты не отвечают с полной уверен- ностью, и именно поэтому следует использовать скобки, если есть хоть малейшее сомнение в том, как вычисляется выражение. 31.3. Стили форматирования Большинство вопросов форматирования касается размещения блоков — групп операторов, располагающихся под управляющими выражениями. Блок окружен скобками или ключевыми словами: { и } в C++ и Java, if-then-endif в Visual Basic и другими похожими структурами в других языках. Для простоты в большей части этого обсуждения используются общие обозначения begin и end. Я предполагаю, что вы поймете, как это можно соотнести со скобками в C++ и Java или анало- гичными механизмами выделения блоков в других языках. Ниже описаны четы- ре основных стиля форматирования: 722 ЧАСТЬ VII Мастерство программирования 쐽 явные блоки; 쐽 эмуляция явных блоков; 쐽 использование пар begin-end (скобок) для обозначения границ блока; 쐽 форматирование в конце строки. Явные блоки Большинство споров по поводу форматирования возникает из-за несовершенства большинства популярных языков программирования. Хорошо спроектированный язык имеет явную структуру блоков, которая приводит к естественному стилю отступов. Так, в Visual Basic у каждой управляющей структуры есть свой термина- тор, и вы не сможете ее использовать без этого терминатора. Код разбивается на блоки естественным образом. Несколько примеров на Visual Basic приведено в листингах 31-6, 31-7 и 31-8: Листинг 31-6. Пример явного блока if (Visual Basic) If pixelColor = Color_Red Then statement1 statement2 End If Листинг 31-7. Пример явного блока while (Visual Basic) While pixelColor = Color_Red statement1 statement2 Wend Листинг 31-8. Пример явного блока case (Visual Basic) Select Case pixelColor Case Color_Red statement1 statement2 Case Color_Green statement1 statement2 Case Else statement1 statement2 End Select Управляющая структура на Visual Basic всегда состоит из начального выражения (в этих примерах — If-Then, While и Select-Case) и всегда содержит соответствую- ГЛАВА 31 Форматирование и стиль 723 щий оператор End. Отступы внутри такой структуры не подвергаются сомнению, а варианты выравнивания других ключевых слов частично ограничены. Листинг 31-9 показывает абстрактное представление того, как выглядит такой вид форма- тирования: Листинг 31-9. Абстрактный пример стиля форматирования явного блока A XXXXXXXXXXXXXXXXXXXX B XXXXXXXXXXXX C XXXXXXXXXXXXXXX D XXXX В этом примере управляющая конструкция начинается оператором A и кончает- ся оператором D. Выравнивание между этими двумя операторами обеспечивает визуальное единство конструкции. Дебаты по поводу форматирования управляющих структур частично возникают потому, что некоторые языки не требуют применения блоковых структур. Вы можете использовать оператор if-then, за которым следует единственное выраже- ние, а не формальный блок. Для создания блока вам приходится добавлять пару begin-end или открывающую и закрывающую скобки вместо их автоматического получения с каждой управляющей структурой. Отделение begin и end от самой структуры — так, как языки, подобные C++ и Java, делают это со скобками { и }, — приводит к вопросу: куда поместить эти begin и end? Поэтому меньше проблемы с отступами становятся проблемами только потому, что вам приходится компен- сировать недостатки плохо спроектированных языковых структур. Способы та- кой компенсации описаны ниже. Эмуляция явных блоков Хорошим подходом в языках, не имеющих явных блоков, будет рассмотрение ключевых слов begin и end (или символов { и }) в виде расширений управляющих структур, с которыми они используются. Далее, имеет смысл попробовать сэму- лировать форматирование языка Visual Basic на вашем языке. Листинг 31-10 со- держит абстрактное представление визуальной структуры, которую вы пытаетесь эмулировать: Листинг 31-10. Абстрактный пример стиля форматирования явного блока A XXXXXXXXXXXXXXXXXXXX B XXXXXXXXXXXX C XXXXXXXXXXXXXXX D XXXX При таком стиле управляющая конструкция открывает блок в операторе A и за- крывает блок в операторе D. Это предполагает, что begin должен находиться в конце оператора A, а end должен быть оператором D. Говоря абстрактно, для эмуляции явных блоков вам надо сделать нечто, подобное листингу 31-11: 724 ЧАСТЬ VII Мастерство программирования Листинг 31-11. Абстрактный пример эмуляции стиля явного блока A XXXXXXXXXXXXXX{X B XXXXXXXXXXXXXX C XXXXXXXXXXXXXXXXX D }X Несколько примеров такого стиля на C++ приведено в листингах 31-12, 31-13 и 31-14: Листинг 31-12. Пример эмуляции явного блока if (C++) if ( pixelColor == Color_Red ) { statement1; statement2; } Листинг 31-13. Пример эмуляции явного блока while (C++) while ( pixelColor == Color_Red ) { statement1; statement2; } Листинг 31-14. Пример эмуляции явного блока switch/case (C++) switch ( pixelColor ) { case Color_Red: statement1; statement2; break; case Color_Green: statement1; statement2; break; default: statement1; statement2; break; } Этот стиль выравнивания вполне функционален. Он хорошо выглядит, его мож- но применять единообразно, а также его удобно сопровождать. Он соответствует Основной теореме форматирования в том плане, что помогает показать логичес- кую структуру кода. Это вполне разумный вариант стиля. Такой стиль является стандартом в Java и широко распространен в C++. { } ГЛАВА 31 Форматирование и стиль 725 Использование пар begin-end (скобок) для обозначения границ блока Альтернативой структуре явного блока может служить использование пар begin- end в качестве границ блока. (В дальнейшем обсуждении пары begin-end приме- няются для общего обозначения операторов begin-end, скобок и других эквива- лентных языковых структур.) Если вы берете за основу этот подход, то рассмат- риваете begin и end как операторы, следующие за управляющей структурой, а не как фрагменты, являющиеся ее частью. Графически это выглядит идеально, так же, как и при эмуляции явного блока, приводимой еще раз в листинге 31-15: Листинг 31-15. Абстрактный пример стиля форматирования явного блока A XXXXXXXXXXXXXXXXXXX B XXXXXXXXXXXX C XXXXXXXXXXXXXX D XXXX Но для того, чтобы в новом стиле begin и end трактовались как составные части структуры блока, а не управляющего выражения, надо поместить begin в начало блока (а не в конец управляющего выражения), а end — в конец блока (а не в ка- честве терминатора управляющего выражения). Говоря абстрактно, вам нужно сделать нечто, подобное структуре, изображенной в листинге 31-16: Листинг 31-16. Абстрактный пример использования begin и end в качестве границ блока A XXXXXXXXXXXXXXXXXXXX {XXXXXXXXXXXXXXXX B XXXXXXXXXXXXXXXXX C XXXXXXXXXXXXXXXXX }X Несколько примеров использования begin и end в качестве границ блоков, пока- зано в листингах 31-17, 31-18 и 31-19: Листинг 31-17. Пример применения begin и end в качестве границ блока if (C++) if ( pixelColor == Color_Red ) { statement1; statement2; } Листинг 31-18. Пример применения begin и end в качестве границ блока while (C++) while ( pixelColor == Color_Red ) { statement1; statement2; } } { 726 ЧАСТЬ VII Мастерство программирования Листинг 31-19. Пример применения begin и end в качестве границ блока switch/case (C++) switch ( pixelColor ) { case Color_Red: statement1; statement2; break; case Color_Green: statement1; statement2; break; default: statement1; statement2; break; } Такой стиль выравнивания также вполне функционален и согласуется с Основ- ной теоремой форматирования (напомним: он показывает логическую структу- ру, лежащую в основе данного кода). Единственное его ограничение в том, что его нельзя применять буквально в операторах switch/case в C++ и Java, что пока- зано в листинге 31-19. (Ключевое слово break служит заменой закрывающей скобке, а у открывающей скобки нет эквивалента.) Форматирование в конце строки Еще одна стратегия форматирования называется «форматированием в конце стро- ки», и объединяет большую группу методик, в которых отступ в коде делается к середине или концу строки. Отступы в конце строки служат для выравнивания блока относительно ключевого слова, с которого он начинается, выравнивания следующих параметров метода под первым параметром, размещения вариантов в операторе case и подобных случаев. Листинг 31-20 представляет абстрактный пример: Листинг 31-20. Абстрактный пример стиля форматирования в конце строки A XXXXXX XXXXXXXXXXXXXXXXXXXXXXXXXXX B XXXXXXXXXXXXXXX C XXXXXXXXXXXXXXX D XX В этом примере оператор A начинает управляющую конструкцию, а оператор D завершает. Операторы B, C и D выровнены под ключевым словом, с которого на- чинается блок в операторе A. Одинаковые отступы B, C и D показывают, что эти операторы сгруппированы вместе. Листинг 31-21 содержит менее абстрактный пример кода, отформатированного в соответствии с этой стратегией: ГЛАВА 31 Форматирование и стиль 727 Листинг 31-21. Пример форматирования в конце строки в блоке while (Visual Basic) While ( pixelColor = Color_Red ) statement1; statement2; Wend В этом примере end помещен в конец строки, а не под соответствующим ключе- вым словом. Некоторые предпочитают располагать end под ключевым словом, но выбор между этими двумя вполне приемлемыми вариантами — наименьшая из проблем этого стиля. Стиль форматирования в конце строки иногда работает вполне удовлетворительно. Листинг 31-22 демонстрирует пример, в котором этот стиль работает: Листинг 31-22. Редкий пример, в котором форматирование в конце строки выглядит привлекательно (Visual Basic) If ( soldCount > 1000 ) Then markdown = 0.10 profit = 0.05 Ключевое слово else выровнено относительно слова then, расположенного над ним. Else markdown = 0.05 End If В этом случае ключевые слова Then, Else и End If выровнены, и код, следующий за ними, также выровнен. Визуальный эффект соответствует ясной логической структуре. Критически взглянув на приводимый ранее пример case-оператора, вы, вероят- но, сможете указать на недостаток данного стиля. По мере усложнения условного выражения этот стиль начнет давать бесполезные или обманчивые подсказки о логической структуре кода. В листинге 31-23 приведен пример ухудшения этого стиля при его применении в более сложном условном выражении: Листинг 31-23. Более типичный пример, в котором форматирование в конце строки является неудачным решением (Visual Basic) If ( soldCount > 10 And prevMonthSales > 10 ) Then If ( soldCount > 100 And prevMonthSales > 10 ) Then If ( soldCount > 1000 ) Then markdown = 0.1 profit = 0.05 Else markdown = 0.05 End If Else markdown = 0.025 End If Else markdown = 0.0 End If > 728 ЧАСТЬ VII Мастерство программирования В чем причина причудливого форматирования выражений Else в конце примера? Они последовательно выровнены под соответствующими ключевыми словами, но вряд ли можно утверждать, что такие отступы проясняют логическую структуру. И если при модификации кода изменится длина первой строки, такой стиль фор- матирования потребует изменения отступов во всех соответствующих выражениях. Так что возникает проблема сопровождения, которой не существует для стилей явных блоков, их эмуляции и использования пар begin-end для обозначения гра- ниц блоков. Вы можете решить, что эти примеры придуманы лишь в демонстрационных це- лях, но такой стиль применяется очень упорно, несмотря на все его недостатки. Масса учебников и справочников по программированию рекомендует этот стиль. Самая первая увиденная мной книга, содержащая эту рекомендацию, была опуб- ликована в середине 1970-х, последняя — в 2003 году. Вообще форматирование в конце строки неаккуратно, его сложно применять еди- нообразно и тяжело сопровождать. Далее я приведу другие проблемы такого стиля. Какой стиль наилучший? При работе в Visual Basic используйте отступы для явных блоков (тем более что в среде разработки Visual Basic затруднительно не придерживаться этого стиля). В Java стандартной практикой является применение формата явных блоков. В C++ можно просто выбрать тот стиль, который вам больше нравится или кото- рому отдают предпочтение большинство разработчиков вашей команды. Как эмуля- ция явных блоков, так и обозначение границ с помощью begin-end работает оди- наково хорошо. Единственное исследование, сравнивавшее эти два стиля, не об- наружило статистически значимых различий между ними с точки зрения понят- ности кода (Hansen and Yim, 1987). Ни один из этих стилей не обеспечивает защиты от дурака, и оба время от време- ни требуют «разумного и очевидного» компромисса. Вы можете предпочесть тот или иной стиль по эстетическим причинам. В этой книге в примерах кода при- меняется стиль явных блоков, так что вы можете увидеть массу иллюстраций это- го стиля, просто просмотрев листинги. Выбрав однажды стиль, вы получите наи- большую выгоду от хорошего форматирования, применяя его единообразно. 31.4. Форматирование управляющих структур Форматирование некоторых программных элементов час- то является только эстетическим вопросом. Однако форма- тирование управляющих структур влияет на удобство чте- ния и понимания и поэтому имеет практическое значение. Тонкие моменты форматирования блоков управляющих структур Работа с блоками управляющих структур требует внимания к деталям. Вот неко- торые советы. Перекрестная ссылка О доку- ментировании управляющих структур см. подраздел «Ком- ментирование управляющих структур» раздела 32.5. О дру- гих аспектах управляющих структур см. главы 14–19. ГЛАВА 31 Форматирование и стиль 729 Избегайте отсутствия отступов в парах begin-end В стиле форматиро- вания, проиллюстрированном в листинге 31-24, пара begin-end выровнена по гра- нице управляющей структуры, а в выражениях, охватываемых операторами begin и end, сделаны отступы относительно begin. Листинг 31-24. Пример пары begin-end, не выделенной отступами (Java) Ключевое слово begin выровнено по границе for. for ( int i = 0; i < MAX_LINES; i++ ) { В выражениях сделан отступ относительно begin. ReadLine( i ); ProcessLine( i ); Слово end также выровнено по границе структуры for. } Хотя такой подход выглядит хорошо, он нарушает Основную теорему формати- рования, так как не показывает логическую структуру кода. При таком располо- жении begin и end не являются частью управляющей структуры, но в то же время, они не являются и частью блока выражений, расположенного далее. Листинг 31-25 демонстрирует абстрактное представление этого подхода: Листинг 31-25. Абстрактный пример вводящего в заблуждение выравнивания A XXXXXXXXXXXXXXXXXXXX B XXXXXXX C XXXXXXXX D XXXXXXXXXXXXXX E XXXX Можно ли сказать, что оператор B подчиняется оператору A? Он не выглядит ча- стью оператора A, и нет оснований считать, что он ему подчиняется. Если вы используете такой подход, смените его на один из двух вариантов стиля, описан- ных ранее, и ваше форматирование будет более целостным. Избегайте двойных отступов при использовании begin и end Следствием правила относительно использования пары begin-end без отступов является слу- чай, касающийся дополнительных отступов после begin-end. Этот стиль, продемон- стрированный в листинге 31-26, содержит отступы как перед begin и end, так и перед выражениями, которые они охватывают: Листинг 31-26. Пример неуместного двойного отступа в блоке begin-end (Java) for ( int i = 0; i < MAX_LINES; i++ ) { > > > 730 ЧАСТЬ VII Мастерство программирования В выражениях после begin сделан лишний отступ. ReadLine( i ); ProcessLine( i ); } Еще один пример стиля, внешне привлекательного, но нарушающего Основную теорему форматирования. Исследования показали, что с точки зрения понимания программы, использующие одинарные и двойные отступы, не отличаются друг от друга (Miaria et al., 1983), но приведенный стиль неточно отображает логическую структуру программы. ReadLine() и ProcessLine() показаны так, будто они подчи- нены паре begin-end, что не соответствует действительности. Этот подход также преувеличивает сложность логической структуры программы. Какая из структур, приведенных в листингах 31-27 и 31-28, выглядит сложнее? Листинг 31-27. Абстрактная структура 1 XXXXXXXXXXXXXXXXXXXX XXXXX XXXXXXXXX XXXXXXXXXXXX XXXXX Листинг 31-28. Абстрактная структура 2 XXXXXXXXXXXXXXXXXXXX XXXXX XXXXXXXXXX XXXXXXXXXXXXX XXXXX Обе являются абстрактными представлениями структуры цикла for. Структура 1 выглядит сложней, хотя и представляет тот же код, что и Структура 2. Если бы вам понадобилось создать 2 или 3 уровня вложенности операторов, то из-за двойных отступов вы получили бы 4 или 6 уровней отступов. Такое форматирование вы- глядело бы сложнее, чем на самом деле. Избавьтесь от этой проблемы, эмулиро- вав явные блоки или применив begin и end в качестве границ блока, выравнивая begin и end так же, как и выражения, которые они охватывают. Другие соглашения Хотя отступы в блоках являются главным вопросом форматирования управляю- щих структур, вы также можете столкнуться с другими проблемами, поэтому да- лее я привожу еще несколько советов. Используйте пустые строки между абзацами Некоторые блоки не разгра- ничиваются с помощью пар begin-end. Логический блок — группа подходящих друг к другу операторов — должны рассматриваться так же, как абзацы в обычной книге. Отделяйте их друг от друга с помощью пустых строк. Листинг 31-29 представля- ет пример абзацев, которые следует разделить: > |