Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
книги хакеры / Питер_Гудлиф_Ремесло_программиста_Практика_написания_хорошего_кода.pdf
Скачиваний:
16
Добавлен:
19.04.2024
Размер:
9.23 Mб
Скачать

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

Подымаемm

скандал

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

151Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

.c

 

 

.

 

 

 

 

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

исключений: когда выполнение функции прерывается досрочно и ге% нерируется исключительная ситуация, ресурсы высвобождаются ав% томатически. Такие приемы позволяют избежать множества проблем, с которыми мы сталкивались выше, перемещая сложность в отдель% ный поток управления.

Тот же пример с использованием исключительных ситуаций, в пред% положении, что все вызываемые функции не возвращают код ошибки, а генерируют исключительные ситуации, выглядел бы так (в C++, Java и C#):

void exceptionalHandling()

{

try

{

operationOne();

... какие то действия ...

operationTwo();

... еще какие то действия ...

operationThree();

... еще действия ...

}

catch (...)

{

... уборка после ошибок ...

}

}

Это элементарный пример, но он показывает, насколько удачно могут действовать исключительные ситуации. В хорошей конструкции кода блок try/catch может вообще не потребоваться, если гарантируется от% сутствие утечки ресурсов, а обработка ошибок передается на более вы% сокий уровень. К сожалению, написание хорошего кода с использова% нием исключительных ситуаций требует усвоения принципов, выхо% дящих за рамки этой главы.

Подымаем скандал

Мы уже довольно долго занимаемся ошибками, которыми нас снабжа% ют другие. Пора поменяться местами и самим выступить в роли нехо% роших мальчиков: будем генерировать ошибки. Если вы пишете функ% цию, в ней могут происходить неправильные вещи, о которых нужно сообщать вызывающему. И делать это необходимо – не проходите мимо сбоев. Даже если вы уверены, что вызвавший не будет знать, что делать с проблемой, вы должны проинформировать его. Не пишите лживый код, который якобы делает то, чего на самом деле он не выполняет.

Каким механизмом сообщения об ошибке воспользоваться? Это в зна% чительной мере определяется архитектурой: соблюдайте установлен% ные в проекте правила и употребляйте стандартные идиомы языка.

 

 

 

 

hang

e

 

 

 

 

 

 

C

 

E

 

 

 

X

 

 

 

 

 

-

 

 

 

 

 

d

 

F

 

 

 

 

 

 

t

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

to

 

 

 

 

w Click

 

 

 

152m

 

 

 

 

w

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

.

 

 

 

 

 

.c

 

 

p

 

 

 

 

g

 

 

 

 

df

 

 

n

e

 

 

 

 

-xcha

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

Глава 6. Людям свойственно ошибатьсяClick

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

.c

 

 

.

 

 

 

 

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

В языках, где такая возможность есть, обычно отдают предпочтение исключительным ситуациям, но применяйте их только тогда, когда это делается во всем проекте. Java и C# на самом деле не оставляют вам выбора; исключительные ситуации лежат глубоко в основе их сре% ды выполнения. В архитектуре C++ иногда отказываются от этого средства ради переносимости на платформы, не поддерживающие ис% ключений, или для совместимости со старым кодом C.

Мы уже рассмотрели стратегии передачи ошибок, генерируемых вы% зываемыми функциями. Теперь наша забота состоит в том, чтобы со% общить о новых проблемах, возникающих во время выполнения. Как вы станете обнаруживать ошибки – дело ваше, но при генерации сооб% щений о них нужно руководствоваться рядом правил:

Во%первых, тщательно ли вы произвели уборку? В надежном коде не происходит утечка ресурсов и не возникают противоречивые со% стояния даже в случае ошибок, если только не оказывается невоз# можным корректно прибрать за собой. Такие случаи должны быть тщательно задокументированы. Подумайте, как поведет себя ваш код при новом вызове после возникновения такой ошибки. Необхо% димо гарантировать его работоспособность.

Не помещайте лишнюю информацию в свои сообщения об ошибке. Выведите только полезные данные, которые сможет понять и ис% пользовать вызвавший.

Корректно пользуйтесь исключительными ситуациями. Не генери% руйте исключений, если возвращаемые значения необычны – они редки, но не ошибочны. Исключения должны сигнализировать только о тех случаях, когда функция не смогла выполнить свой контракт. Не применяйте их с целями, для которых они не предна% значены (например, для управления потоком выполнения).

Для перехвата ошибок, которые никогда не могут произойти в ходе обычного выполнения программы, т. е. подлинных ошибок програм% мирования, можно воспользоваться оператором контроля assert (см. раздел «Ограничения» на стр. 45). Применение исключитель% ных ситуаций в таких случаях тоже допустимо: некоторые меха% низмы контроля можно настраивать так, что они будут генериро% вать исключения в случае ошибок.

Если какие%то тесты можно заранее провести на этапе компиляции, не нужно этим пренебрегать. Чем раньше будет обнаружена и ис% правлена ошибка, тем меньше неприятностей она принесет.

Постарайтесь, чтобы ваши ошибки было сложно игнорировать. При первой же возможности вашим кодом воспользуются не так, как вы планировали. Исключения здесь полезны: нужно прило% жить специальные усилия, чтобы подавить исключение.

Какого рода ошибки нужно отслеживать? Очевидно, это зависит от то% го, какую задачу решает функция. Вот контрольный список общих ти% пов ошибок, которые нужно проверять в каждой функции:

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

 

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

w Click

 

 

 

Управлениеm

ошибками

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-xcha

 

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

153Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

.c

 

 

.

 

 

 

 

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Проверка всех параметров функции. Убедитесь, что вам передали корректные и допустимые данные. Для этого можно воспользовать% ся операторами проверки в зависимости от того, насколько строго был составлен контракт. (Не является ли его нарушением передача недопустимых параметров?)

Проверка выполнения инвариантов в важных точках кода.

Проверка допустимости всех данных из внешних источников перед их использованием. Содержимое файлов и данных интерактивного ввода должно быть разумным и не иметь пропусков.

Проверка состояния возврата всех системных вызовов и вызовов подчиненных функций.

Исключение из правила

Исключительные ситуации – мощный механизм сообщения об ошибках. При правильном применении они могут значительно упростить код и способствовать его стабильности. Однако в не% умелых руках они становятся опасным оружием.

Однажды я работал над проектом, где среди программистов бы% ло принято прерывать цикл while или завершать рекурсию пу% тем генерации исключительной ситуации, используемой как оператор дальнего перехода. Идея кажется интересной, когда встречаешься с ней впервые. Но такая технология – издеватель% ство над исключительными ситуациями: идиоматически они применяются для других целей. Множество критических оши% бок появилось в результате того, что сопровождавший код про% граммист не понимал последовательности передачи управления в сложном и нестандартно завершаемом цикле.

Следуйте идиомам своего языка и не пытайтесь сочинять остро% умный код ради собственного развлечения.

Управление ошибками

Стандартный принцип для генерации и обработки ошибок требует нали% чия единообразной стратегии обработки сбоев, в каком бы месте они ни проявились. Вот общие административные соображения, относящие% ся к возникновению, обнаружению и обработке программных ошибок:

Избегайте ситуаций, чреватых ошибками. Лучше напишите код, который гарантированно будет работать. Например, чтобы не воз% никало ошибок выделения памяти, заранее позаботьтесь о резерви% ровании достаточных ресурсов. Когда у вас обеспечен пул памяти,

программа не может испытывать недостатка в памяти. Конечно,

 

 

 

 

hang

e

 

 

 

 

 

 

C

 

E

 

 

 

X

 

 

 

 

 

-

 

 

 

 

 

d

 

F

 

 

 

 

 

 

t

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

to

 

 

 

 

w Click

 

 

 

154m

 

 

 

 

w

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

.

 

 

 

 

 

.c

 

 

p

 

 

 

 

g

 

 

 

 

df

 

 

n

e

 

 

 

 

-xcha

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

Глава 6. Людям свойственно ошибатьсяClick

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

.c

 

 

.

 

 

 

 

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

это осуществимо, только если заранее известно, сколько нужно ре% сурсов, но часто именно так и бывает.

Определите возможное поведение вашей программы или функции в необычных обстоятельствах. Исходя из этого установите, на% сколько устойчивым должен быть код и насколько тщательной – обработка ошибок. Не получится ли так, что функция, не привле% кая внимания, сгенерирует скверные выходные данные согласно классическому принципу GIGO?1

Четко распределите между компонентами обязанности по обработ% ке тех или иных ошибок. Объявите это в интерфейсе модуля. Пусть ваш клиент знает, что будет работать всегда, а что может в один прекрасный день отказать.

Проконтролируйте свой стиль программирования: в какой момент вы пишете код обработки ошибок? Не откладывайте его на буду% щее; обязательно что%нибудь пропустите. Не откладывайте написа% ние обработчиков до того времени, когда тестирование выявит про% блемы – это не инженерный подход.

Если вы пишете код, который может отказать, одновременно с ним пишите код для обнаружения и обработки ошибок. Не откладывайте это на буду& щее. Если вы все же вынуждены отложить обработку, по крайней мере, на& пишите оснастку для обнаружения ошибок.

Если вы перехватили ошибку, что это – симптом или причина? Вы можете обнаружить источник проблемы, которую нужно исправ% лять тут же, или признак более застарелой проблемы. В последнем случае не нужно писать много кода в этом месте; лучше поместить его там, где он более уместен – в более раннем обработчике ошибки.

Резюме

Ошибка – от человека, раскаяние – от Бога, упрямство – от дьявола.

Бенджамин Франклин

Людям свойственно ошибаться (но у компьютеров это тоже хорошо по% лучается). Исправление ошибок – святое дело.

На всякую написанную вами строчку кода должен приходиться надле% жащий объем кода для доскональной проверки и обработки ошибок. Программа без строгой обработки ошибок не может быть надежной. В один прекрасный день случится какая%то неясная ошибка, и в ре% зультате программа рухнет.

1Garbage In, Garbage Out – если мусор на входе, то и результатом станет

мусор.

 

 

 

 

hang

e

 

 

 

 

 

 

C

 

E

 

 

 

X

 

 

 

 

 

-

 

 

 

 

 

d

 

F

 

 

 

 

 

 

t

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

to

 

 

 

 

w Click

 

 

 

Резюмеm

 

 

 

 

w

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

.

 

 

 

 

 

.c

 

 

p

 

 

 

 

g

 

 

 

 

df

 

 

n

e

 

 

 

 

-xcha

 

 

 

 

 

 

 

hang

e

 

 

 

 

 

 

 

C

 

E

 

 

 

 

X

 

 

 

 

 

 

-

 

 

 

 

 

d

 

 

F

 

 

 

 

 

 

t

 

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

 

to

 

 

 

 

 

155Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

.c

 

 

.

 

 

 

 

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Обработка ошибок и ситуаций отказа – тяжелый труд. Здесь програм% мирование сталкивается с реалиями окружающего мира. Однако это абсолютно необходимая работа. По некоторым подсчетам 90% созда% ваемого кода занимается обработкой исключительных обстоятельств (Bentley 82). Это неприятная статистика, поэтому при написании кода будьте готовы к тому, что гораздо больше труда придется вложить в то, что может пойти вкривь и вкось, чем в то, что будет правильно работать.

Хорошие программисты…

Плохие программисты…

Сочетают хорошие замыс% Пишут код беспорядочно, не обду%

лы с хорошей практикой кодирования

Пишут код обработки оши% бок одновременно с основ% ным кодом

Досконально рассматрива% ют в своем коде все воз% можные случаи сбоев

мывая его заранее и не пересматри% вая потом

Игнорируют ошибки, возникающие во время написания кода

Проводят длительные сеансы отлад% ки после аварий программы, потому что никогда не задумывались, что может стать причиной ошибки

См. также

Глава 1. Держим оборону

Обработка ошибок в контексте – один из многих приемов защитно% го программирования.

Глава 4. Литературоведение

Самодокументирование кода гарантирует, что обработка ошибок становится неотъемлемой частью написания кода.