Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
книги хакеры / Питер_Гудлиф_Ремесло_программиста_Практика_написания_хорошего_кода.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

 

 

 

 

 

w Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

.

 

 

 

 

 

.c

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

1

Держим оборону

Технологии защитного кодирования для создания надежного кода

В этой главе:

Что такое защитное программирование?

Стратегии создания безопасного кода

Ограничения и операторы контроля

Верить нельзя никому. Это наша единственная защита от предательства.

Теннесси Вильямс

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

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

 

 

 

 

hang

e

 

 

 

 

 

 

C

 

E

 

 

 

X

 

 

 

 

 

-

 

 

 

 

 

d

 

F

 

 

 

 

 

 

t

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

to

 

 

 

 

w Click

 

 

 

30m

 

 

 

 

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

 

 

 

 

 

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

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

.c

 

 

.

 

 

 

 

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

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

На пути к хорошему коду

Есть огромная разница между кодом, который на первый взгляд работа% ет, правильным кодом и хорошим кодом. М. Э. Джексон (M. A. Jackson) писал: «Всякий мудрый программист должен понимать, что есть раз% ница между тем, чтобы заставить программу работать, и тем, чтобы за% ставить ее делать это правильно». (Jackson 75) И вот в чем эта разница:

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

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

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

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

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

Как в таких условиях обеспечить профессиональное качество кода? На помощь приходит защитное программирование.

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

 

 

 

 

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

 

 

 

 

 

31Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

.c

 

 

.

 

 

 

 

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

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

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

Предположения служат причиной появления кода с ошибками. Очень легко предположить следующее:

Функцию никогда не станут вызывать таким способом. Ей всегда будут передаваться только допустимые параметры.

Этот фрагмент кода всегда будет работать, он никогда не сгенериру% ет ошибку.

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

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

Опыт показывает, что уверенным можно быть только в одном: в один прекрасный день ваш код по каким%то причинам даст сбой. Кто%то обя# зательно сделает глупость. Закон Мерфи гласит: «Если какая%нибудь неприятность может произойти, она обязательно случится». Прислу% шайтесь к автору – он говорит о том, что познал на собственном опыте.1 Защитное программирование ограждает от таких несчастных случаев, поскольку предвидит их заранее или хотя бы пытается предугадать путем выяснения неприятностей, способных произойти на каждом этапе выполнения кода, и принятия защитных мер.

Вы считаете это паранойей? Возможно, вы правы. Но быть немного параноиком не вредно. На самом деле, это весьма разумно. По мере

1Эдвард Мерфи, служивший инженером в ВВС США, сформулировал свой знаменитый закон, когда обнаружил, что некий техник регулярно подклю% чает целый ряд устройств наизнанку. Неправильное подключение было возможно из%за симметричности разъемов. Чтобы избежать этого, при%

шлось изменить их конструкцию.

 

 

 

 

hang

e

 

 

 

 

 

 

C

 

E

 

 

 

X

 

 

 

 

 

-

 

 

 

 

 

d

 

F

 

 

 

 

 

 

t

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

to

 

 

 

 

w Click

 

 

 

32m

 

 

 

 

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

 

 

 

 

 

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

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

.c

 

 

.

 

 

 

 

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

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

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

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

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

Что такое защитное программирование?

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

Защитное программирование позволяет обнаружить мелкие проблемы на ранней стадии, не дожидаясь момента, когда они приведут к серьез% ным катастрофам. Сплошь и рядом можно столкнуться с тем, как «профессиональные» разработчики спешат написать код, не дав себе труда задуматься – примерно так, как на верхнем рисунке на стр. 33.

Они постоянно спотыкаются на некорректных допущениях, прове% рить которые им недосуг. Не к чести современного программирования, но это происходит постоянно. Защитное программирование помогает

 

 

 

 

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

 

 

 

 

 

33Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

.c

 

 

.

 

 

 

 

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Поковырять код

Запустить его

Облом!

Поковырять код

Запустить его

Облом!

писать грамотные программы с самого начала и позволяет избавиться от цикла написать код, проверить, написать код, проверить… При защитном программировании картинка примерно такая:

Закодировал

Протестировал

Работает!

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

Защитное программирование – это метод профилактики, а не способ лечения. Этим оно отличается от отладки, т. е. устранения ошибок по% сле того, как они дали о себе знать. Отладка – это именно поиск спосо% ба лечения.

Действительно ли защитное программирование стоит этих хлопот? Приведем аргументы за и против:

Против

Защитное программирование требует дополнительных ресурсов – ваших и компьютера.

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

 

 

 

 

hang

e

 

 

 

 

 

 

C

 

E

 

 

 

X

 

 

 

 

 

-

 

 

 

 

 

d

 

F

 

 

 

 

 

 

t

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

to

 

 

 

 

w Click

 

 

 

34m

 

 

 

 

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

 

 

 

 

 

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

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

.c

 

 

.

 

 

 

 

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

С чем не нужно путать защитное программирование

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

Контроль ошибок

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

Тестирование

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

Отладка

Защитный код можно добавить в период отладки, но отладкой занимаются уже тогда, когда произошел отказ программы. За% щитное программирование нужно в первую очередь для того, чтобы предотвратить отказ (или обнаружить ошибки на ран% нем этапе, пока они не проявили себя недопустимым образом, который потребует от вас отлаживать программу всю ночь).

функции или класса, но если в системе 100000 функций, могут возникнуть проблемы.

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

За

Аргументы другой стороны убедительны.

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

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