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

 

 

 

 

 

367Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

.c

 

 

.

 

 

 

 

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

ве побочного эффекта вносит несколько новых ошибок. Брукс считает, что 40% исправлений приводят к новым ошибкам (Brooks 95). «За% стольная песнь программиста» (напеваемая на мотив «99 Bottles of Beer on the Wall»), написанная неизвестным менестрелем, замечатель% но подытоживает этот результат:

99 маленьких багов в коде, 99 багов в коде, Исправь один баг, снова скомпилируй, 101 маленький баг в коде.

(Повторять до BUGS == 0)

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

Помните о том, как легко деградирует модифицируемый код. Избегайте мо& дификаций, после которых система оказывается в худшем состоянии.

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

Тревожные симптомы

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

Вот что должно восприниматься как мигающие красные огни и вой клаксонов:

Код перегружен многочисленными крупными классами и запутан% ными функциями.

Имена функций загадочны или обманчивы. У функций проявляются неожиданные побочные эффекты, не предусмотренные их именами.

Отсутствует структура: непонятно, где искать нужную функцию.

Есть дублирование: оказывается, что отдельные фрагменты кода

занимаются одним и тем же.

 

 

 

 

hang

e

 

 

 

 

 

 

C

 

E

 

 

 

X

 

 

 

 

 

-

 

 

 

 

 

d

 

F

 

 

 

 

 

 

t

 

D

 

 

 

 

 

 

 

i

 

 

 

 

 

 

 

 

r

P

 

 

 

 

 

NOW!

o

 

 

 

 

 

 

 

 

 

 

 

BUY

 

 

 

 

 

to

 

 

 

 

w Click

 

 

 

368m

 

 

 

 

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

 

 

 

 

 

Глава 15. Программное обеспечение – эволюция или революция?Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

.c

 

 

.

 

 

 

 

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

Высока степень связанности: сложные соединения и взаимозависи% мости модулей приводят к тому, что небольшие изменения в одном месте вызывают широкие отголоски во всем коде – иногда в моду% лях, которые, как казалось, независимы. (См. раздел «Модуль% ность» на стр. 325).

Проходящие через систему данные многократно преобразуются из одного представления в другое (например, отображаемые данные передаются между std::string, char*, Unicode, UTF%8 и обратно).

API становятся расплывчатыми; некогда четкие интерфейсы стали слишком широкими по охвату в результате непродуманного добав% ления новых функций.

API резко меняются от одной версии кода к другой.

Элементы закрытой реализации просачиваются в открытые API ра% ди изготовления быстрых заплаток.

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

Есть функции с громадными списками параметров. Многие из них не используют эти параметры, а передают во вспомогательные функции.

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

Новые функции добавляются без соответствующей документации; имеющаяся документация устарела.

При компиляции кода выводится множество предупредительных сообщений.

Встречаются комментарии, гласящие здесь ничего не трогать

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

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

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

 

 

 

 

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

 

 

 

 

 

369Click

 

 

 

 

 

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

 

 

 

370m

 

 

 

 

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

 

 

 

 

 

Глава 15. Программное обеспечение – эволюция или революция?Click

 

 

 

 

 

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

 

 

 

Как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

 

 

 

 

 

371Click

 

 

 

 

 

m

 

 

 

 

 

 

w

 

 

 

 

 

 

 

o

 

 

w

 

 

 

 

 

 

 

 

w

 

 

 

 

 

 

.c

 

 

.

 

 

 

 

 

 

 

 

p

 

 

 

 

g

 

 

 

 

 

df

 

 

n

e

 

 

 

 

 

-x cha

 

 

 

 

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

Рефакторинг – причудливое название для ряда конкретных типов мо% дификации кода. Мартин Фаулер формализовал их, описав несколь% ко небольших и понятных усовершенствований кода (Fowler 99).1

Теория хаоса

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

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

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

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

1Мартин Фаулер «Рефакторинг: улучшение существующего кода». – Пер.

с англ. – СПб: Символ%Плюс, 2002.