Файл: Руководство по стилю программирования и конструированию по.pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 30.11.2023
Просмотров: 817
Скачиваний: 2
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
ГЛАВА 8 Защитное программирование
195
Решив передать исключение, удостоверьтесь, что уровень абстракции исключения и метода совпадают. Вот как не надо делать:
Плохой пример Java-класса, который генерирует исключение
на неверном уровне абстракции
class Employee {
Объявление исключения с неправильным уровнем абстракции.
public TaxId GetTaxId() throws EOFException {
}
}
Функция
GetTaxId() передает низкоуровневое исключение EOFException вызыва- ющей стороне. Она не обрабатывает исключение сама, а раскрывает некоторые детали своей реализации, генерируя низкоуровневое исключение. Это привязы- вает клиентский код не к классу
Employee, а к коду внутри класса Employee, гене- рирующему исключение
EOFException. Инкапсуляция нарушена, управляемость кода ухудшается.
Вместо этого код
GetTaxId() должен передавать исключение, соответствующее интерфейсу класса, частью которого он является, например, так:
Хороший пример Java-класса, который генерирует исключение
на правильном уровне абстракции
class Employee {
Объявление исключения, соответствующего уровню абстракции.
public TaxId GetTaxId() throws EmployeeDataNotAvailable {
}
}
Код обработки исключений внутри
GetTaxId(), возможно, просто устанавливает соответствие между исключениями
io_disk_not_ready и EmployeeDataNotAvailable,
что гораздо лучше, так как сохраняется абстракция интерфейса.
Вносите в описание исключения всю информацию о его причинах Каж- дое исключение возникает при определенных обстоятельствах, обнаруженных ко- дом в момент генерации этого исключения. Эти сведения недоступны тому, кто читает сообщение об исключении. Убедитесь, что это сообщение содержит до- статочно информации для понимания причины генерации исключения. Напри- мер, если причиной был неправильный индекс элемента массива, включите в опи- сание верхнюю и нижнюю границы массива и некорректное значение индекса.
>
>
196
ЧАСТЬ II Высококачественный код
Избегайте пустых блоков catch Иногда возникает искушение оставить без внимания исключение, которое вы не знаете, как обработать. Например:
Плохой пример игнорирования исключения (Java)
try {
// много кода
} catch ( AnException exception ) {
}
Такой подход говорит о том, что либо код внутри блока
try генерирует исключе- ние без причины, либо код в блоке
catch не обрабатывает возможную исключи- тельную ситуацию. Выясните, в чем суть проблемы, и исправьте блоки
try или catch.
Изредка можно столкнуться с ситуацией, когда исключение более низкого уровня не соответствует уровню абстракции вызывающего метода. В этом случае хотя бы задокументируйте, почему блок
catch должен быть пустым. Это можно сделать в комментариях или записав сообщение в файл журнала, например:
Хороший пример игнорирования исключения на Java
try {
// много кода
} catch ( AnException exception ) {
LogError( ”Unexpected exception” );
}
Выясните, какие исключения генерирует используемая библиотека Ес- ли вы работаете с языком, не требующим, чтобы метод или класс объявляли воз- можные исключения, убедитесь, что вам известно, какие исключения могут воз- никнуть в коде используемых библиотек. Неперехваченное исключение из биб- лиотеки приведет к аварийному завершению программы так же легко, как и исключение, сгенерированное в вашем коде. Если библиотечные исключения не документированы, создайте код-прототип и протестируйте библиотеки, чтобы их выявить.
Рассмотрите вопрос о централизованном выводе информации об исклю-
чениях Поддержание целостности в обработке исключений обеспечивает цен- трализованный генератор сообщений об исключениях. Он содержит базу знаний о том, какие это исключения, как каждое из них должно быть обработано, каков формат их сообщений и т. п.
Вот пример упрощенного обработчика исключений. Он просто печатает диагно- стическое сообщение:
ГЛАВА 8 Защитное программирование
197
Пример централизованного генератора сообщений
об исключениях, часть 1 (Visual Basic)
Sub ReportException( _
ByVal className, _
ByVal thisException As Exception _
)
Dim message As String
Dim caption As String message = ”Exception: ” & thisException.Message & ”.” & ControlChars.CrLf & _
”Class:
” & className & ControlChars.CrLf & _
”Routine: ” & thisException.TargetSite.Name & ControlChars.CrLf caption = ”Exception”
MessageBox.Show( message, caption, MessageBoxButtons.OK, _
MessageBoxIcon.Exclamation )
End Sub
Этот обработчик можно использовать следующим образом:
Пример централизованного генератора сообщений
об исключениях, часть 2 (Visual Basic)
Try
Catch exceptionObject As Exception
ReportException( CLASS_NAME, exceptionObject )
End Try
Код этой версии
ReportException() несложен. В реальных приложениях вы може- те сделать отчет настолько кратким или подробным, насколько это необходимо в вашем обработчике исключений.
Если вы решили создать централизованный генератор сообщений, примите во вни- мание основные проблемы с централизованной обработкой ошибок, обсуждаемые в подразделе «Вызвать процедуру или объект — обработчик ошибок» раздела 8.3.
Стандартизуйте использование исключений в вашем проекте Чтобы со- хранить процедуру обработки исключений максимально интеллектуально управ- ляемой, вы можете стандартизовать использование исключений несколькими спо- собами.
쐽
Если вы работаете с языком, таким как C++, который позволяет генерировать исключения разных типов, стандартизуйте, что конкретно будет создаваться.
В целях совместимости с другими языками подумайте об использовании только объектов, порожденных от базового класса
Exception.
쐽
Подумайте о создании собственного класса исключений, который может слу- жить базовым классом для всех исключений, возникающих в вашем проекте.
Это поможет централизовать и стандартизовать регистрацию, обработку и другие действия с ошибками.
Дополнительные сведения Об этой технологии см. «Practical
Standards for Microsoft Visual
Basic .NET» (Foxall, 2003).
198
ЧАСТЬ II Высококачественный код
쐽
Определите конкретные случаи, в которых код может использовать синтаксис
throw-catch для локальной обработки ошибок.
쐽
Определите конкретные случаи, в которых код может сгенерировать исклю- чение, не перехватываемое локально.
쐽
Решите, будет ли использоваться централизованный генератор сообщений об исключениях.
쐽
Определите, допускаются ли исключения в конструкторах и деструкторах.
Рассмотрите альтернативы исключениям Некоторые языки поддерживают исключения 5–10 лет и более. Одна- ко до сих пор нет общепринятых правил их безопасного использования.
Некоторые программисты применяют исключения для обработки ошибок толь- ко потому, что их язык программирования предоставляет такой механизм. Вам все- гда следует принимать во внимание все возможные методы обработки ошибок:
локальную обработку ошибок, возврат кода ошибки, запись отладочной инфор- мации в файл, прекращение работы системы и др. Обрабатывать ошибки с помо- щью исключений только потому, что это позволяет язык, — классический пример программирования
на языке, а не с использованием языка (см. разделы 4.3 и 34.4).
И напоследок подумайте, действительно ли вашей программе необходимо обра- батывать исключения. Точка. Как заметил Бьерн Страуструп, иногда лучшей реак- цией на серьезную ошибку периода выполнения будет освобождение всех ресур- сов и прекращение работы. Пусть пользователь перезапустит программу с надле- жащими входными данными (Stroustrup, 1997).
8.5. Изоляция повреждений,
вызванных ошибками
Изоляция повреждений, или баррикада, — это стратегия, сходная с тем, как изо- лируются отсеки в трюме корабля. Если корабль налетает на айсберг и в днище появляется пробоина, отсеки задраиваются, и остальная часть корабля не страда- ет. Баррикады также аналогичны брандмауэрам, предотвращающим распростра- нение огня из одной части здания в другую. (Ранее баррикады и назывались бран- дмауэрами, но сейчас термин «брандмауэр» обычно относится к блокировке не- желательного сетевого трафика.)
Один из способов изоляции в целях защитного программирования состоит в разработке набора интерфейсов в качестве оболочки для «безопасных» частей кода.
Проверяйте корректность данных, пересекающих границу безопасной области,
и реагируйте соответственно, если данные неправильные (рис. 8-2).
Перекрестная ссылка Об альтер- нативных подходах к обработ- ке ошибок см. раздел 8.3.
ГЛАВА 8 Защитное программирование
199
Рис. 8-2. Выделение части кода для работы с непроверенными данными, а части —
для работы с только корректными данными, может быть эффективным способом
освободить большую часть программы от ответственности за проверку
допустимости данных
Тот же подход применим и на уровне класса. Открытые методы класса предпола- гают, что данные небезопасны и отвечают за их проверку и исправление. Если данные были проверены открытыми методами класса, закрытые методы могут считать, что данные безопасны.
Этот подход можно представить и такой аналогией: данные стерилизуются, прежде чем войти в операционную. Все, что находится в ней, считается безопасным. Клю- чевой вопрос проектирования — решить, что должно быть в операционной, что
— остаться снаружи и где быть дверям. Иначе говоря, какие методы поместить внутри безопасной зоны, какие — снаружи, а какие будут проверять данные. Про- стейший способ — проверка внешних данных по мере их поступления. Но ин- формацию часто необходимо проверять неоднократно, на нескольких уровнях,
поэтому иногда требуется многоуровневая стерилизация.
Преобразовывайте входные данные к нужному типу в момент ввода
Данные на входе обычно представлены в форме строки или числа. Иногда значе- ние соответствует булевому типу, например, «да» или «нет». Иногда — перечисли- мому, скажем,
Color_Red, Color_Green, и Color_Blue. Сохранение данных неопреде- ленного типа в течение неизвестного периода времени усложняет программу и увеличивает шансы, что кто-нибудь может вывести программу из строя, указав «Да»
в качестве цвета. Преобразуйте входные данные в надлежащую форму как можно раньше.
200
ЧАСТЬ II Высококачественный код
Связь между баррикадами и утверждениями
Применение баррикад делает отчетливым различие между утверждениями и об- работкой ошибок. Методы с внешней стороны баррикады должны использовать обработчики ошибок, поскольку небезопасно делать любые предположения о данных. Методы внутри баррикад должны использовать утверждения, так как дан- ные, переданные им, считаются проверенными при прохождении баррикады. Если один из методов внутри баррикады обнаруживает некорректные данные, это сле- дует считать ошибкой в программе, а не в данных.
Использование баррикад также иллюстрирует значимость принятия решения об обработке ошибок на уровне архитектуры. Решение, какой код находится внут- ри, а какой — снаружи баррикады, принимается на уровне архитектуры.
8.6. Отладочные средства
Еще один ключевой аспект защитного программирования — отладка, способная стать могучим союзником в быстром обнаружении ошибок.
Не применяйте ограничения промышленной версии
к отладочной версии автоматически
Известным заблуждением программистов является предпо- ложение, что ограничения промышленной версии относятся и ко времени разработки. Промышленная версия должна работать быстро. Отладочная — может себе позволить ра- ботать медленно. Промышленная версия должна быть эко- номна с ресурсами. Отладочная — может быть расточитель- ной. Промышленная версия не должна позволять пользователю делать опасные действия. Отладочная — может предоставлять дополнительные возможности без риска нарушить безопасность.
Одна моя программа интенсивно использовала четырехсвязный список. Код это- го списка содержал ошибки, и иногда список повреждался. Я добавил пункт меню для проверки целостности этого связного списка.
В отладочном режиме Microsoft Word содержит код, который в момент простоя каждые несколько секунд проверяет целостность объекта
Document. Это помогает быстро обнаруживать повреждение данных, что упрощает диагностику ошибок.
Будьте готовы поступиться скоростью и ресурсоемкостью во время раз- работки в обмен на встроенные средства, позволяющие процессу разра- ботки двигаться более гладко.
Внедрите поддержку отладки как можно раньше
Чем раньше вы добавите отладочные средства, тем больше они помогут. Обычно вы не добавляете отладочную информацию, пока несколько раз не столкнетесь с про- блемой. Если же вы внедрите поддержку отладки после первого раза или перенесете ее из предыдущего проекта, она будет помогать вам на протяжении всей работы.
Дополнительные сведения Об использовании отладочного ко- да в защитном программирова- нии см. «Writing Solid Code»
(Maguire, 1993).
1 ... 22 23 24 25 26 27 28 29 ... 104
ГЛАВА 8 Защитное программирование
201
Используйте наступательное программирование
Исключительные случаи должны обрабатываться так, что- бы во время разработки они были очевидны, а в промыш- ленном коде — позволяли продолжить работу. Майкл Ховард и Дэвид Леблан назвали этот подход «наступательным про- граммированием» (Howard and LeBlanc, 2003).
Допустим, у вас есть оператор
case, который, как вы ожида- ете, будет обрабатывать только 5 видов событий. Во время разработки вариант по умолчанию нужно использовать для генерации предупреждения «Эй! Здесь еще один вариант! Исправьте программу!». Однако в промышленной версии реакция в этом случае должна быть более вежливой. Можно, например, делать запись в журнал ошибок.
Вот некоторые приемы наступательного программирования.
쐽
Убедитесь, что утверждения завершают работу програм- мы. Нельзя, чтобы у программистов вошло в привычку просто нажимать клавишу Enter для пропуска уже изве- стной проблемы. Сделайте проблему достаточно мучи- тельной, чтобы ее исправили.
쐽
Заполняйте любую выделенную память, чтобы можно было обнаружить ошибки выделения памяти.
쐽
Заполняйте все файлы и потоки, чтобы выявить ошибки файловых форматов.
쐽
Убедитесь, что при попадании в ветвь
default или else всех операторов case
программа прекращает работу или еще как-то заставляет обратить на это вни- мание.
쐽
Заполняйте объекты мусором перед самым их удалением.
쐽
Настройте программу на отправку вам журналов ошибок по электронной по- чте, чтобы видеть, какие ошибки происходят в рабочем ПО, если, конечно, это можно сделать в ваших программах.
Иногда нападение — лучшая защита. Чем жестче требования во время разработ- ки, тем проще эксплуатация программы.
Запланируйте удаление отладочных средств
Если вы пишете код для себя, возможно, было бы хорошо оставить всю отладоч- ную информацию в программе. Но в коде для коммерческого использования тре- бования к скорости и размеру скорее всего этого не допустят. Решите заранее, как избежать постоянного перетаскивания отладочного кода в программу и из нее.
Вот несколько способов сделать это:
Для контроля версий и сборки программы используй-
те инструменты ant и make Средства контроля версий позволяют создавать варианты программы из одних и тех же исходных файлов. А инструменты для сборки позволяют вам настроить вклю- чение отладочного кода в режиме разработки и его исключение в коммерческой версии.