Файл: Руководство по стилю программирования и конструированию по.pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 30.11.2023
Просмотров: 841
Скачиваний: 2
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
1 ... 62 63 64 65 66 67 68 69 ... 104
ГЛАВА 23 Отладка
541
Пример ухудшения кода в результате
его «исправления» (Java)
for ( claimNumber = 0; claimNumber < numClaims[ client ]; claimNumber++ ) {
sum[ client ] = sum[ client ] + claimAmount[ claimNumber ];
}
«Исправление».
if ( client == 45 ) {
sum[ 45 ] = sum[ 45 ] + 3.45;
}
Теперь допустим, что при нулевом числе исков (number of claims) со стороны клиента
37 вы не получаете 0. Плохое решение этой проблемы могло бы быть таким:
Продолжение примера ухудшения кода в результате
его «исправления» (Java)
for ( claimNumber = 0; claimNumber < numClaims[ client ]; claimNumber++ ) {
sum[ client ] = sum[ client ] + claimAmount[ claimNumber ];
}
if ( client == 45 ) {
sum[ 45 ] = sum[ 45 ] + 3.45;
}
Второе «исправление».
else if ( ( client == 37 ) && ( numClaims[ client ] == 0 ) ) {
sum[ 37 ] = 0.0;
}
Если уж и это не заставляет вас содрогнуться от ужаса, вы зря читаете мою книгу:
ничто в ней вас не тронет. Перечислить все недостатки этого подхода в книге,
объемом лишь около 1000 страниц, невозможно, поэтому ниже я указал только три главных.
쐽
В большинстве случаев эти исправления работать не будут. Проблемы в этом примере очень похожи на дефекты инициализации. Дефекты инициализации по определению непредсказуемы, поэтому тот факт, что сумма для клиента 45
отличается сегодня от верного значения на 3,45 доллара, ничего не говорит о том, что будет завтра. Завтра она может отличаться на 10 000,02 доллара, а может быть верной. Таковы ошибки инициализации.
쐽
Такой код трудно сопровождать. Если для исправления ошибок создаются ча- стные случаи, они становятся самой заметной особенностью кода. Значение
3,45 доллара не всегда будет таким, и позднее возникнет другая ошибка. В код придется включить новый частный случай, а частный случай для 3,45 доллара удален не будет. К коду будут прилипать все новые частные случаи. В конце концов эти «прилипалы» станут слишком тяжелыми для кода, и он пойдет на дно, где ему самое место.
>
>
542
ЧАСТЬ V Усовершенствование кода
쐽
Неразумно использовать компьютер для выполнения чего-то, что лучше сде- лать вручную. Компьютеры хороши для выполнения предсказуемых система- тичных вычислений, а творческая фальсификация данных лучше дается лю- дям. Вместо подделки данных в коде лучше было бы исправить их в распечат- ке результатов работы программы.
Изменяйте код только при наличии веских оснований С устранением только симптомов проблемы тесно связана методика случайного изменения кода до тех пор, пока он не покажется верным. Типичный ход мыслей при этом таков: «Похо- же, этот цикл содержит дефект. Наверное, это ошибка занижения или завышения на 1, так что я просто отниму 1 и посмотрю, что получится. Ерунда какая-то. Ну-ка,
а если прибавить 1? Вроде все работает. Думаю, проблема решена».
Какой бы популярной ни была эта методика, она неэффективна. Внесение слу- чайных изменений в код похоже на накачивание шин автомобиля при неисправ- ности двигателя. Так вы ничего не узнаете — только впустую потратите время.
Изменяя программу случайным образом, вы по сути говорите: «Не знаю, в чем тут дело. Будем надеяться, что это изменение сработает». Не делайте так. Это вуду-про- граммирование. Чем сильнее вы измените код, не понимая его, тем более сомни- тельной станет его корректность.
Не вносите в программу изменение, если не уверены в его правильности. Невер- ные изменения должны вызывать у вас удивление Они должны вызывать сомне- ния, пересмотр взглядов и самокритику. Они должны быть редкими.
Вносите в код по одному изменению за раз Одиночные изменения и так до- вольно коварны. При внесении сразу двух изменений дело только осложняется:
они могут привести к тонким ошибкам, похожим на первоначальные. В итоге вы попадаете в затруднительное положение: как узнать, имеете ли вы дело со старой ошибкой, только с новой ошибкой, похожей на старую, или сразу с новой и ста- рой? Не осложняйте себе жизнь и вносите изменения только по одному за раз.
Проверяйте исправления Проверьте программу сами,
попросите сделать это кого-то другого или проанализируй- те программу вместе. Выполните те же триангуляционные тесты, что и при диагностике проблемы, и проверьте, все ли аспекты проблемы устранены. Если решена только часть проблемы, вы об этом узнаете.
Проверьте всю программу на предмет того, не привели ли сделанные изменения к побочным эффектам. Самый легкий и эффективный способ обнаружения по- бочных эффектов — автоматизированное регрессивное тестирование програм- мы в среде JUnit, CppUnit или аналогичной.
Добавляйте в набор тестов блочные тесты, приводящие к проявлению
имеющихся дефектов Обнаружив ошибку, на которую не смогли указать име- ющиеся тесты, добавьте в набор тестов новый тест, позволяющий предотвратить возвращение этой ошибки.
Поищите похожие дефекты Обнаружив один дефект, поищите аналогичные дефекты. Дефекты часто появляются группами, и, обращая внимание на типы своих дефектов, вы сможете исправлять все дефекты конкретного типа. Поиск похожих
Перекрестная ссылка Об авто- матизированном регрессивном тестировании см. подраздел
«Повторное (регрессивное) те- стирование» раздела 22.6.
ГЛАВА 23 Отладка
543
дефектов требует глубокого понимания проблемы. Если вы не можете сообразить,
как искать похожие дефекты, значит, вы еще не полностью понимаете проблему.
23.4. Психологические аспекты отладки
Отладка предъявляет к интеллекту не меньшие требования,
чем любые другие этапы разработки ПО. Самолюбие гово- рит вам, что ваш код не может содержать дефектов, даже если вы уже встречали их в нем. Выдвигая гипотезы, собирая дан- ные, анализируя гипотезы и методично отказываясь от них,
вы должны думать строго, стать немыслимым формалистом.
Отлаживая собственный код, вы должны быстро переклю- чаться между гибким творческим мышлением, характерным для проектирования, и жестким критичным мышлением, нужным для отладки. Читая код, стремитесь забыть о том, что он вам известен, и увидеть то, что написано на самом деле, а не то, что вы ожидаете увидеть.
«Психологическая установка» и слепота при отладке
Встречая в программе элемент
Num, что вы видите? Неправильно написанное слово
«Numb»? Или аббревиатуру слова «Number»? Скорее всего второе. Это одно из проявлений «психологической установки»: вы видите то, что ожидаете увидеть. Что написано тут?
Этот казус уже стал классическим: люди часто замечают только один артикль «the».
Они видят то, что ожидают увидеть. Ниже описаны другие похожие факты.
쐽
Студенты, изучающие циклы
while, часто считают, что условие цикла оцени- вается непрерывно, т. е. ожидают, что цикл завершится сразу, как только усло- вие станет ложным, а не после проверки условия (Curtis et al., 1986). Они ду- мают, что цикл
while должен соответствовать слову «пока» в естественном языке.
쐽
Программист, который неумышленно использовал и переменную
SYSTSTS, и переменную SYSSTSTS, думал, что имеет дело с одной перемен ной. Он не обнаружил проблему, пока программа не была выполнена сотни раз и не была написана книга, содержащая ошибочные результаты (Wein- berg, 1998).
쐽
Программисты, сталкивающиеся с кодом:
if ( x < y )
swap = x;
x = y;
y = swap;
иногда воспринимают его как:
Дополнительные сведения Пре- красное обсуждение психологи- ческих аспектов отладки и мно- гих других областей разработ- ки ПО вы найдете в книге «The
Psychology of Computer Prog- ramming» (Weinberg, 1998).
544
ЧАСТЬ V Усовершенствование кода if ( x < y ) {
swap = x;
x = y;
y = swap;
}
Люди ожидают, что новый феномен будет напоминать аналогичные феномены,
виденные ими ранее. Они ожидают, что новая управляющая стурктура будет ра- ботать так же, как и старые структуры, что оператор
while языка программирова- ния будет соответствовать слову «пока», а имена переменных будут такими же,
какими были раньше. Вы видите то, что ожидаете увидеть, и поэтому не замечае- те отличия, такие как неправильное написание слова «структура» в предыдущем предложении.
Какое отношение психологическая установка имеет к отладке? Во-первых, она под- черкивает важность грамотных методик программирования. Разумное форматиро- вание и комментирование, удачные имена переменных, методов и другие элемен- ты стиля программирования помогают структурировать фоновые условия програм- мирования так, чтобы вероятные дефекты четко выделялись на общем фоне.
Во-вторых, психологическая установка влияет на выбор частей программы, изу- чаемых при обнаружении ошибки. Исследования показали, что программисты,
отлаживающие код максимально эффективно, во время отладки мысленно отде- ляют нерелевантные фрагменты программы (Basili, Selby, and Hutchens, 1986). Это позволяет им сужать область поиска и находить дефекты быстрее. Однако при этом можно по ошибке «отложить в сторону» часть программы, содержащую дефект.
В результате вы будете искать дефект в правильном фрагменте кода, игнорируя фрагмент, который на самом деле содержит дефект. Вы пойдете по неверному пути и должны будете вернуться к перекрестку. Некоторые советы из раздела 23.2 по- могут преодолеть эту «слепоту при отладке».
«Психологическая дистанция»
Психологическую дистанцию можно определить как лег- кость различения двух элементов. Если дать кому-нибудь длинный список слов и сказать, что все они имеют отно- шение к уткам, человек может с легкостью перепутать сло- ва «Queck» и «Quack», потому что они кажутся похожими. Психологическая дис- танция между ними мала. Гораздо труднее перепутать «Tuack» и «Quack», хотя они тоже различаются только одной буквой. Слово «Tuack» похоже на «Quack» мень- ше, чем «Queck», потому что первая буква слова сильнее бросается в глаза, чем буква в середине.
Вот примеры психологических дистанций между именами переменных (табл. 23-1):
Перекрестная ссылка Советы по выбору ясных имен переменных см. в разделе 11.7.
ГЛАВА 23 Отладка
545
Табл. 23-1. Примеры психологических дистанций между именами переменных
Первая переменная
Вторая переменная
Психологическая дистанция
stoppt stcppt
Почти незаметна shiftrn shiftrm
Почти отсутствует dcount bcount
Небольшая claims1
claims2
Небольшая product sum
Большая
Приступая к отладке, будьте готовы к проблемам, обусловленным недостаточной психологической дистанцией между похожими именами переменных и методов.
Выбирайте во время конструирования имена, ясно отличающиеся от других имен,
и вы забудете об этих проблемах.
23.5. Инструменты отладки —
очевидные и не очень
Отладку можно значительно упростить, используя доступ- ные инструменты. Инструментов, способных забить осино- вый кол в сердце дефекта-вампира, еще не существует, но с каждым годом они становятся все лучше и лучше.
Утилиты сравнения исходного кода
Утилиты сравнения исходного кода (такие как Diff) полезны при исправлении ошибок. Если после внесения нескольких изменений некоторые из них нужно отменить, но вы плохо помните, что именно вы изменяли, утилита сравнения исходного кода освежит вашу память, указав на различия кода. Если вы обнару- жили в новой версии кода дефект, которого не было в предыдущей версии, про- сто сравните соответствующие файлы.
Предупреждающие сообщения компилятора
Одним из самых простых и эффективных инструментов отладки явля- ется сам компилятор.
Задайте в компиляторе максимально строгий уровень диагностики и
устраняйте все ошибки и предупреждения Игнорировать сообщения ком- пилятора об ошибках глупо, но отключать вывод предупреждений еще глупее. Детям иногда кажется, что, если они закроют глаза и перестанут вас видеть, вы пропа- дете. Отключая вывод предупреждений, вы совершаете такую же ошибку: вы лишь перестаете их видеть, но их причины никуда не исчезают.
Исходите из того, что разработчики компилятора знают язык гораздо лучше вас.
Если они о чем-то вас предупреждают, рассматривайте это как возможность уз- нать о языке что-то новое. Стремитесь понять подлинный смысл каждого предуп- реждения.
Рассматривайте предупреждения как ошибки Некоторые компиляторы по- зволяют рассматривать предупреждения как ошибки. Одно из достоинств этой
Перекрестная ссылка Граница между инструментами тестиро- вания и отладки размыта. Об инструментах тестирования см.
раздел 22.5, а об инструментах разработки ПО — главу 30.
546
ЧАСТЬ V Усовершенствование кода функции в том, что она повышает важность предупреждений. Как перевод часов на пять минут вперед заставляет думать, что сейчас на пять минут больше, чем на самом деле, так и указание компилятору считать предупреждения ошибками зас- тавляет воспринимать их более серьезно. Кроме того, эта функция часто влияет на процесс компиляции программы. При компиляции и компоновке программы предупреждения в отличие от ошибок обычно не предотвращают компоновку. Если вы хотите проверить предупреждения перед компоновкой, прикажите компиля- тору рассматривать предупреждения как ошибки.
Стандартизуйте параметры компиляции в масштабе всего проекта
Разработайте стандарт, требующий, чтобы все участники проекта компилирова- ли код, используя одинаковые параметры. Иначе при интеграции кода, скомпи- лированного с использованием разных параметров, вы утонете в сообщениях об ошибках и сами узнаете, что такое интеграционный кошмар. Стандарт легко на- вязать, создав один общий make-файл или сценарий сборки программы.
Утилиты расширенной проверки синтаксиса и логики
Существуют инструменты, проверяющие код тщательнее, чем компилятор. Так,
программисты на C используют утилиту lint, которая старательно ищет неиници- ализированные переменные (возникающие, например, при написании
= = вмес- то
=) и похожие тонкие проблемы.
Инструменты профилирования выполнения программы
Вам может показаться, что инструменты профилирования не имеют отношения к отладке, но на самом деле несколько минут, потраченных на изучение профиля программы, могут указать на некоторые неожиданные (и скрытые) дефекты.
Например, при работе над одной из программ у меня возникло подозрение, что метод управления памятью снижает быстродействие программы. Модуль управ- ления памятью изначально был небольшим компонентом, использующим линей- но упорядоченный массив указателей на области памяти. Я заменил линейно упо- рядоченный массив на хэш-таблицу, ожидая, что время выполнения кода сокра- тится минимум вдвое. Однако при профилировании кода я не обнаружил изме- нения быстродействия. Тщательнее изучив код, я обнаружил дефект в алгоритме выделения памяти, приводивший к огромным тратам времени. Узкое место было обусловлено не линейным поиском, а дефектом. Алгоритм поиска вообще можно было не оптимизировать. Исследуйте результаты работы инструмента профили- рования, чтобы гарантировать, что каждая часть программы выполняется за ра- зумный интервал времени.
Среды тестирования и леса
Как уже было сказано в разделе 23.2 в отношении поиска дефектов, выделение проблемного фрагмента кода и его тестирование в изоляции от других фрагментов часто ока- зывается самым эффективным способом изгнания бесов из дефектной программы.
Перекрестная ссылка О лесах см. подраздел «Создание лесов для тестирования отдельных классов» раздела 22.5.