Главная страница

Руководство по стилю программирования и конструированию по


Скачать 7.6 Mb.
НазваниеРуководство по стилю программирования и конструированию по
Дата18.05.2023
Размер7.6 Mb.
Формат файлаpdf
Имя файлаCode_Complete.pdf
ТипРуководство
#1139697
страница92 из 104
1   ...   88   89   90   91   92   93   94   95   ...   104

ГЛАВА 32 Самодокументирующийся код
775
граммой, и после тщательного изучения документации он обнаружил только та- кой комментарий:
MOV AX, 723h ; R. I. P. L. V. B.
Поломав над ним голову всю ночь, программист в итоге все исправил и пошел домой спать. Несколько месяцев спустя он встретился с автором программы на конференции и узнал, что комментарий означал «Rest in peace, Ludwig van Beet- hoven» (Покойся в мире, Людвиг ван Бетховен). Бетховен умер в 1827 году, кото- рому соответствует шестнадцатеричное значение 723. Необходимость использо- вания значения 723h не имела никакого отношения к комментарию. &%@*?#%

!!!
Комментарии в концах строк и связанные с ними проблемы
Пример кода с комментариями в концах строк (Visual Basic)
For employeeId = 1 To employeeCount
GetBonus( employeeId, employeeType, bonusAmount )
If employeeType = EmployeeType_Manager Then
PayManagerBonus( employeeId, bonusAmount ) ‘ полная оплата
Else
If employeeType = EmployeeType_Programmer Then
If bonusAmount >= MANAGER_APPROVAL_LEVEL Then
PayProgrammerBonus( employeeId, StdAmt() ) ‘ стандартная оплата
Else
PayProgrammerBonus( employeeId, bonusAmount ) ‘ полная оплата
End If
End If
End If
Next
Иногда комментарии в концах строк полезны, но вообще они создают несколько проблем. Чтобы они не нарушали восприятие структуры кода, их нужно вырав- нивать по правому краю кода. Если вы не будете их выравнивать, листинг будет выглядеть так, будто его пропустили через мясорубку. Как правило, комментарии в концах строк трудно форматировать. Если их много, выравнивание занимает массу времени. Это время тратится не на изучение кода, а исключительно на нуд- ный ввод пробелов или символов табуляции.
В то же время комментарии в концах строк трудно поддерживать. При удлине- нии какой-либо строки с таким комментарием он сдвигается, что заставляет при- водить в соответствие все остальные комментарии в концах строк. Стили, кото- рые трудно поддерживать, никто не поддерживает, и после изменений кода ком- ментарии начинают ухудшать его, а не улучшать.
Кроме того, комментарии в концах строк часто страдают от таинственности. Справа от строки обычно остается не так много места, и, если вы хотите написать ком- ментарий в той же строке, он должен быть кратким. В результате целью коммен- тирования становится формулирование комментариев в максимально краткой, а не максимально ясной форме.

776
ЧАСТЬ VII Мастерство программирования
Не используйте комментарии в концах строк, относящиеся к одиночным
строкам С комментариями в концах строк связаны не только практические, но и концептуальные проблемы. Вот пример набора комментариев в концах строк:
Пример бесполезных комментариев в концах строк (C++)
Здесь комментарии просто повторяют код.
memoryToInitialize = MemoryAvailable(); // получение объема доступной памяти pointer = GetMemory( memoryToInitialize ); // получение указателя на память
ZeroMemory( pointer, memoryToInitialize ); // обнуление памяти
FreeMemory( pointer ); // освобождение памяти
Общий недостаток комментариев в концах строк в том, что трудно придумать выразительный комментарий для одной строки кода. Большинство таких коммен- тариев просто повторяют строку кода, что скорее вредит, чем помогает.
Не используйте комментарии в концах строк, относящиеся к нескольким
строкам кода Если комментарий в конце строки относится к нескольким стро- кам, форматирование не позволяет узнать, к каким именно:
Пример непонятного комментария
в конце строки (Visual Basic)
For rateIdx = 1 to rateCount ‘ Вычисление цен со скидкой
LookupRegularRate( rateIdx, regularRate )
rate( rateIdx ) = regularRate * discount( rateIdx )
Next
Суть данного комментария ясна, но чтó он комментирует? Чтобы узнать, относится ли комментарий к отдельной команде или целому циклу, придется прочитать и комментарий, и код.
Когда использовать комментарии в концах строк?
Ниже описаны три разумных причины использования комментариев в концах строк.
Используйте комментарии в концах строк для пояс-
нения объявлений данных Комментарии в концах строк полезны для аннотирования объявлений данных, потому что с ними не связаны проблемы, характерные для аналогич- ных комментариев кода, если, конечно, строки имеют до- статочную длину. При 132 символах в строке вы обычно мо- жете написать выразительный комментарий около каждо- го объявления данных:
Пример объявлений данных с хорошими комментариями в концах строк (Java)
int boundary = 0; // верхний индекс отсортированной части массива
String insertVal = BLANK; // элемент, вставляемый в отсортированную часть массива int insertPos = 0; // индекс вставки элемента в отсортиров. часть массива
>
Перекрестная ссылка О других аспектах использования ком- ментариев в концах строк для пояснения объявлений данных см. ниже подраздел «Коммен- тирование объявлений данных».

ГЛАВА 32 Самодокументирующийся код
777
Не используйте комментарии в концах строк для вставки пометок во
время сопровождения ПО Комментарии в концах строк иногда используют для регистрации изменений, вносимых в код после выпуска первоначальной версии программы. Эти комментарии обычно включают дату изменения и инициалы программиста; иногда — номер отчета об ошибке и т. д. Вот пример:
for i = 1 to maxElmts – 1 — исправлена ошибка #A423 01.10.05 (scm)
Наверное, написать такой комментарий после ночного сеанса отладки програм- мы приятно, но в итоговом коде им не место. Их лучше включать в системы управления версиями. Комментарии должны объяснять, почему код работает
сейчас,
а не почему он когда-то не работал.
Используйте комментарии в концах строк для обозна-
чения концов блоков В комментариях в концах строк полезно отмечать окончание объемных блоков кода — на- пример, циклов
while или операторов if. Подробнее об этом см. ниже.
За исключением нескольких специальных случаев коммен- тарии в концах строк сопряжены с концептуальными проблемами и обычно слу- жат для объяснения слишком сложного кода. Кроме того, их сложно форматиро- вать и поддерживать. В общем, их лучше избегать.
Комментирование абзацев кода
Большинство комментариев в хорошо документированной программе состоят из одного-двух предложений и описывают абзацы кода:
Пример хорошего комментария абзаца кода (Java)
// перемена корней местами oldRoot = root[0];
root[0] = root[1];
root[1] = oldRoot;
Этот комментарий не повторяет код — он описывает цель кода. Такие коммента- рии относительно легко поддерживать. Даже если вы найдете ошибку в коде, ме- няющем корни местами, изменять комментарий не придется. Комментарии, на- писанные не на уровне цели, поддерживать труднее.
Пишите комментарии на уровне цели кода Описывайте цель блока кода, сле- дующего за комментарием. Вот пример комментария, неэффективного из-за того,
что он не относится к уровню цели:
Пример неэффективного комментария (Java)
/* проверка символов строки “inputString”, пока не будет обнаружен знак доллара или пока не будут проверены все символы
*/
done = false;
maxLen = inputString.length();
i = 0;
Перекрестная ссылка О коммен- тариях в концах строк, служа- щих для обозначения концов блоков см. ниже подраздел
«Комментирование управляю- щих структур».
Перекрестная ссылка Этот код,
выполняющий простой поиск символа в строке, служит только в качестве примера. В реальном коде вы использовали бы для этого встроенные библиотечные функции Java. О важности по- нимания возможностей языка см. подраздел «Читайте!» раз- дела 33.3.

778
ЧАСТЬ VII Мастерство программирования while ( !done && ( i < maxLen ) ) {
if ( inputString[ i ] == ‘$’ ) {
done = true;
}
else {
i++;
}
}
Прочитав код, можно понять, что цикл ищет символ $, и это полезно отразить в комментарии. Недостаток этого комментария в том, что он просто повторяет код и ничего не говорит о его цели. Следующий комментарий чуть лучше:
// поиск символа ‘$’ в строке inputString
Этот комментарий лучше потому, что он говорит, что целью цикла является по- иск символа
$. Но мы все еще не знаем причину поиска символа $ — иначе гово- ря, более глубокую цель цикла. Вот комментарий, который еще лучше:
// поиск терминального символа слова команды ($)
Этот комментарий на самом деле сообщает то, чего нет в листинге: что символ
$
завершает слово команды. Просто прочитав код, этого никак не понять, так что данный комментарий действительно важен.
При комментировании на уровне цели полезно подумать о том, как бы вы назва- ли метод, выполняющий то же самое, что и код, который вы хотите прокоммен- тировать. Если каждый абзац кода имеет одну цель, это несложно: примером мо- жет служить комментарий в предыдущем фрагменте кода. Имя
FindCommand-
WordTerminator() было бы вполне приемлемым именем метода. Другие варианты

Find$InInputString() и CheckEachCharacterInInputStrUntilADollarSignIsFoundOrAll-
CharactersHaveBeenChecked() — по очевидным причинам являются плохими (или некорректными) именами. Опишите суть кода как имя соответствующего метода,
не используя сокращений и аббревиатур. Это описание и будет комментарием, и скорее всего он будет относиться к уровню цели.
Во время документирования сосредоточьтесь на самом коде Стро- го говоря, сам код — это документация, которую всегда следует прове- рять в первую очередь. В предыдущем примере литерал
$ следовало бы заменить на именованную константу, а имена переменных должны были бы пре- доставлять больше информации о том, что происходит. Если бы вы хотели добиться максимальной читабельности, вы добавили бы переменную, содержащую резуль- тат поиска. Это провело бы ясное различие между индексом цикла и результатом выполнения цикла. Вот предыдущий пример, переписанный в хорошем стиле и дополненный хорошим комментарием:
Пример хорошего кода с хорошим комментарием (Java)
// поиск терминального символа слова команды foundTheTerminator = false;
commandStringLength = inputString.length();
testCharPosition = 0;

ГЛАВА 32 Самодокументирующийся код
779
while ( !foundTheTerminator && ( testCharPosition < commandStringLength ) ) {
if ( inputString[ testCharPosition ] == COMMAND_WORD_TERMINATOR ) {
foundTheTerminator = true;
Эта переменная содержит результат поиска.
terminatorPosition = testCharPosition;
}
else {
testCharPosition = testCharPosition + 1;
}
}
Если код достаточно хорош, его цель можно понять при чтении и без коммента- риев. Фактически они даже могут стать избыточными, но от этого страдают очень немногие программы.
Наконец, этот код следовало бы выделить в метод с именем вроде
FindCommandWordTerminator(). Аналогичный коммента- рий также полезен, но при изменении ПО комментарии уста- ревают и становятся неверными чаще, чем имена методов.
Придумывая комментарий абзаца, стремитесь отве-
тить на вопрос «почему?», а не «как?» Комментарии,
объясняющие, «как» что-то выполняется, обычно относятся к уровню языка про- граммирования, а не проблемы. Цель операции почти невозможно выразить в таком комментарии, поэтому он часто оказывается избыточным. Разве следующий ком- ментарий сообщает что-то такое, чего не ясно из самого кода?
Пример комментария,
отвечающего на вопрос «как?» (Java)
// если флаг счета равен нулю if ( accountFlag == 0 ) ...
Нет, не сообщает. А этот?
Пример комментария , отвечающего на вопрос «почему?» (Java)
// если создается новый счет if ( accountFlag == 0 ) ...
Этот комментарий гораздо лучше, так как он говорит что-то, чего нельзя понять,
исходя из самого кода. Код также можно улучшить, заменив
0 на элемент пере- числения с выразительным именем и уточнив имя переменной. Вот самая лучшая версия этого кода и этого комментария:
Пример дополнения хорошего комментария хорошим стилем программирования (Java)
// если создается новый счет if ( accountType == AccountType.NewAccount ) ...
Когда код достигает такого уровня читабельности, ценность комментариев ста- новится сомнительной. В нашем случае после улучшения кода комментарий стал
Перекрестная ссылка О вынесе- нии фрагмента кода в отдель- ный метод см. также подраздел
«Извлечение метода из друго- го метода» раздела 24.3.
>

780
ЧАСТЬ VII Мастерство программирования избыточным, и его вполне можно удалить. Можно поступить и иначе, чуть изме- нив роль комментария:
Пример комментария, играющего роль «заголовка раздела» (Java)
// создание нового счета if ( accountType == AccountType.NewAccount ) {
}
Если этот комментарий относится ко всему блоку кода после проверки условия
if, он является резюмирующим комментарием, и не будет лишним сохранить его в качестве заголовка абзаца кода.
Используйте комментарии для подготовки читателя кода к последующей
информации Хорошие комментарии говорят человеку, читающему код, чего ожи- дать. Просмотра комментариев должно быть достаточно для получения хороше- го представления о том, что делает код и где искать места выполнения отдельных операций. Из этого следует, что комментарии должны всегда предшествовать описываемому ими коду. На занятиях по программированию об этом говорят не всегда, но в отрасли разработки коммерческого ПО эта конвенция стала почти стандартом.
Не размножайте комментарии без необходимости В избыточном коммен- тировании ничего хорошего: если комментариев слишком много, они только скрывают смысл кода, который должны пояснять. Вместо того чтобы писать до- полнительные комментарии, постарайтесь сделать читабельнее сам код.
Документируйте сюрпризы Если что-то не следует из самого кода, отразите это в комментарии. Если вы использовали хитрую методику вместо простой для повышения производительности, укажите в комментариях, каким был бы простой подход, и оцените прирост производительности, достигаемый благодаря хитро- сти, например:
Пример документирования сюрприза (C++)
for ( element = 0; element < elementCount; element++ ) {
// Для деления на 2 используется операция сдвига вправо.
// Это сокращает время выполнения цикла на 75%.
elementList[ element ] = elementList[ element ] >> 1;
}
Операция сдвига вправо в этом примере выбрана намеренно. Почти всем програм- мистам известно, что в случае целых чисел сдвиг вправо функционально эквива- лентен делению на 2.
Если это известно почти всем, зачем это документировать? Потому что целью данного кода является не сдвиг вправо, а деление на 2. Важно то, что програм- мист использовал не ту методику, которая лучше всего соответствует цели. Кроме того, большинство компиляторов в любом случае заменяют целочисленное деле- ние на 2 операцией сдвига вправо, из чего следует, что ухудшение ясности кода обычно не требуется. В данной ситуации комментарий говорит, что компилятор

ГЛАВА 32 Самодокументирующийся код
781
не оптимизирует деление на 2, поэтому замена деления на сдвиг вправо существен- но ускоряет выполнение кода. Благодаря комментарию программист, читающий код, тут же поймет мотивацию использования неочевидного подхода. Если бы комментария не было, тот же программист мог бы ворчать, что код усложнен без причины. Обычно такое ворчание оправданно, поэтому документировать исклю- чения из правил важно.
Избегайте сокращений Комментарии должны быть однозначны и не застав- лять программистов расшифровывать сокращения. Никаких сокращений, кроме самых распространенных, в комментариях быть не должно. Обычно такой соблазн возникает только при использовании комментариев в концах строк. Это еще один аргумент против данного вида комментариев.
Проведите различие между общими и детальными комментариями
Иногда желательно провести различие между комментариями разных уровней, ука- зав, что детальный комментарий является частью предыдущего, более общего ком- ментария. Эту задачу можно решить несколькими способами. Вы можете подчер- кивать общие комментарии и не подчеркивать детальные:
Пример проведения различия между общим и детальными комментариями
при помощи подчеркивания — не рекомендуется (C++)
Общий комментарий подчеркивается.
// копирование всех строк таблицы,
// кроме строк, подлежащих удалению
//—————————————————————————————————————
Детальный комментарий, являющийся частью действия, описываемого общим комментарием, не подчеркивается ни здесь…
// определение числа строк в таблице
…ни здесь.
// маркирование строк, подлежащих удалению
Слабость этого подхода в том, что он заставляет подчеркивать больше коммента- риев, чем хотелось бы. Подчеркивание какого-то комментария предполагает, что все следующие неподчеркнутые комментарии являются подчиненными по отно- шению к нему. Соответственно первый комментарий, не относящийся к подчер- кнутому комментарию, также должен быть подчеркнут, и все начинается сызно- ва. Результат? Избыток подчеркивания или его несогласованность.
Эта тема имеет несколько вариаций, с которыми связана та же проблема. Если вы используете в общем комментарии только заглавные буквы, а в детальных — только строчные, вы просто заменяете избыток подчеркивания на избыток заглавных букв.
Некоторые программисты проводят различие между общим и детальными ком- ментариями при помощи регистра только первой буквы, но это различие слиш- ком мало, и его легко не заметить.
>
>
>

782
ЧАСТЬ VII Мастерство программирования
Лучше предварять детальные комментарии многоточием:
Пример проведения различия между общим и детальными комментариями
при помощи многоточий (C++)
Общий комментарий форматируется как обычно.
// копирование всех строк таблицы,
// кроме строк, подлежащих удалению
Детальный комментарий, являющийся частью действия, описываемого общим комментарием, пред- варяется многоточием здесь…
// ... определение числа строк в таблице
…и здесь.
// ... маркирование строк, подлежащих удалению
Другой подход, который часто оказывается самым лучшим, — вынесение кода,
соответствующего общему комментарию, в отдельный метод. В идеале методы должны быть логически «плоскими»: все выполняемые в них действия должны относиться примерно к одному логическому уровню. Если в методе выполняют- ся и общие, и детальные действия, метод не является плоским. В результате выне- сения сложной группы действий в отдельный метод вы получите два логически ясных метода вместо одного логически скомканного.
Данное обсуждение общих и детальных комментариев не относится к коду с от- ступами, содержащемуся внутри циклов и условных операторов. В этих случаях часто имеется общий комментарий перед циклом и более детальные коммента- рии в коде с отступами. Логическая организация комментариев характеризуется отступами. Таким образом, вышесказанное относилось только к последователь- ным абзацам кода, когда полная операция охватывает несколько абзацев, а неко- торые абзацы подчинены другим.
Комментируйте все, что имеет отношение к ошибкам или недокументи-
рованным возможностям языка или среды Если в языке или среде есть ошиб- ка, она, вероятно, недокументирована. Даже если она где-то описана, не помеша- ет сделать это еще раз в коде. Недокументированная возможность по определе- нию не описана нигде, и ее следует задокументировать в коде.
Допустим, вы обнаружили, что библиотечный метод
WriteData( data, numItems,
blockSize ) работает неверно, если blockSize имеет значение 500. Метод прекрасно обрабатывает значения
499, 501 и все остальные, которые вы когда-либо пробо- вали, но имеет дефект, проявляющийся, только когда параметр
blockSize равен 500.
Напишите перед вызовом
WriteData(), почему вы создали специальный случай для этого значения параметра. Вот как это могло бы выглядеть:
Пример документирования кода, предотвращающего ошибку (Java)
blockSize = optimalBlockSize( numItems, sizePerItem );
>
>
>

1   ...   88   89   90   91   92   93   94   95   ...   104


написать администратору сайта