Ïèòåð Ãóäëèô
Ïèòåð Ãóòëèô
«Есть книги, которые нужно прочесть, но есть книги, которые нель-
зя не прочесть.
Книга Пита относится ко второй категории: она полезна и интерес-
на, и, прочтя ее, вы станете лучше как программист.» РЕМЕСЛО
— Джез Хиггинс, глава ACCU
ПРОГРАММИСТА
// п р а к т и к а написания хорошего кода
РЕМЕСЛО ПРОГРАММИСТА
Ïèòåð Ãóòëèô Вы умеете писать код, который работает, но как нужно писать код,
чтобы его было легко понять? И как добиться его надежности и
(Pete Goodliffe) – îïûòíû
é ðàçðàáîò÷èê ïðîãðàìì отсутствия ошибок? Если ваш труд станут читать другие програм-
íîãî îáåñïå÷åíèÿ, ïîñòî мисты, смогут ли они выяснить логику и цель кода? Выдающиеся
ÿííî ìåíÿþùèé ñâîþ ðîë программисты не просто обладают техническими знаниями: у них
ü â öåïè ïðîãðàììíûõ ðà еще есть правильный подход и отношение к программированию.
çðàáîòîê; îí çàíèìàëñÿ Если вы хотите стать настоящим профессионалом в программиро-
ðàçðàáîòêàìè íà ìíîãèõ вании или повысить свой нынешний уровень мастерства, эта книга
ÿçûêàõ â ðàçëè÷íûõ ïðîå покажет вам, как писать код, который больше чем “просто рабо-
êòàõ. Êðîìå òîãî, ó íåãî тает”.
áîëüøîé îïûò îáó÷åíèÿ Здесь вы найдете не связанные с конкретными языками рекомен-
è ïîâûøåíèÿ êâàëèôèêà дации, полезные всем разработчикам и касающиеся таких про-
öèè ïðîãðàììèñòîâ. Ïèò блем, как стиль представления, выбор имен переменных, обра-
âåäåò ðåãóëÿðíóþ êîëîí ботка ошибок и безопасность. Рассматриваются и более широкие
êó «Professionalism in Prog практические темы, такие как эффективность групповой работы,
ramming» â æóðíàëå C Vu технологии разработки и составление документации. В конце
, èçäàâàåìîì ACCU (www каждой главы есть раздел контрольных вопросов, заставляющий
.accu.org). Îí ëþáèò ïèñà
повторить основные идеи и выступить в качестве эксперта, что де-
òü ïðåâîñõîäíûé êîä, â ê
лает книгу особенно ценным пособием для молодых программи-
îòîðîì íåò îøèáîê, áëà
стов, желающих стать профессионалами и эффективно трудиться
ãîäàðÿ ÷åìó îí ìîæåò áî
ëüøå âðåìåíè ïðîâîäèòü в команде.
ñî ñâîèìè äåòüìè. Это руководство по выживанию в условиях промышленного произ-
водства программного обеспечения научит тому, как:
* Писать хороший код, когда условия тому не благоприятствуют
* Избегать катастроф и отвлечений во время работы
* Точно оценить свои возможности и выбрать пути совершенство-
вания
* Занять продуктивную позицию и следовать лучшим примерам
Ничто не сравнится по ценности с советами настоящего
программиста-профессионала. Эта книга написана ясно, практично
и занимательно. Она поможет вам перейти на более высокий уро-
вень в мастерстве программирования и вашей личной карьере.
Pete Goodliffe
Ремесло
программиста
Практика написания
хорошего кода
Питер Гудлиф
СанктПетербург–Москва
2009
Серия «Профессионально»
Питер Гудлиф
Ремесло программиста.
Практика написания хорошего кода
Перевод С. Маккавеева
Главный редактор А. Галунов
Зав. редакцией Н. Макарова
Научный редактор Б. Попов
Редактор А. Петухов
Корректор О. Макарова
Верстка Д. Орлова
Художник О. Макарова
Гудлиф П.
Ремесло программиста. Практика написания хорошего кода. – Пер. с англ. –
СПб.: Символ%Плюс, 2009. – 704 с., ил.
ISBN 978%5%93286%127%1
Ничто не сравнится по ценности с советами настоящего программиста%профес%
сионала. Книга Питера Гудлифа «Ремесло программиста» написана ясно,
практично и занимательно. Она поможет вам перейти на более высокий уро%
вень мастерства программирования и покажет, как писать код, который боль%
ше чем «просто работает». Да, вы умеете писать работающий код, но как напи%
сать понятный код? Как добиться его надежности и отсутствия ошибок? Смо%
гут ли другие программисты выяснить логику и цель вашего кода? Выдающи%
еся программисты не просто обладают техническими знаниями – у них есть
правильный подход и отношение к программированию.
Перед вами руководство по выживанию в условиях промышленного производ%
ства ПО. Эта книга посвящена тому, чему вас никто не учил: как правильно
программировать в реальной жизни. Здесь вы найдете не связанные с конкрет%
ными языками рекомендации, полезные всем разработчикам и касающиеся
таких проблем, как стиль представления, выбор имен переменных, обработка
ошибок, безопасность, эффективность групповой работы, технологии разра%
ботки и составление документации.
Читатель должен обладать опытом программирования, ибо книга не учит про%
граммированию – она учит правильно программировать. Издание будет полез%
но и студентам старших курсов, знакомым с принципами программирования.
ISBN 9785932861271
ISBN 978 1593271190 (англ)
© Издательство Символ%Плюс, 2009
Authorized translation of the English edition © 2007 No Starch Press, Inc. This trans%
lation is published and sold by permission of No Starch Press, Inc., the owner of all
rights to publish and sell the same.
Все права на данное издание защищены Законодательством РФ, включая право на полное или час%
тичное воспроизведение в любой форме. Все товарные знаки или зарегистрированные товарные зна%
ки, упоминаемые в настоящем издании, являются собственностью соответствующих фирм.
Псалом 150
Оглавление
Об авторе . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Благодарности . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Предисловие . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
I. Перед лицом кода . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
1. Держим оборону . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
На пути к хорошему коду . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Готовьтесь к худшему. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Что такое защитное программирование? . . . . . . . . . . . . . . . . . . . . . . . . . 32
Этот страшный, ужасный мир . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
Технологии защитного программирования . . . . . . . . . . . . . . . . . . . . . . . 36
Ограничения . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
Резюме . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
Контрольные вопросы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
2. Тонкий расчет . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
Да в чем проблема? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
Знайте своих клиентов . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
Что такое хорошее представление? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
Размещение скобок . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
Единственно верный стиль . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
Внутрифирменные стили (и когда их придерживаться) . . . . . . . . . . . . 63
Установка стандарта . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
Религиозные войны? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Резюме . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
Контрольные вопросы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
4. Литературоведение . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
Самодокументируемый код . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
Техника написания самодокументируемого кода . . . . . . . . . . . . . . . . . . 98
Практические методологии самодокументирования . . . . . . . . . . . . . . 103
Резюме . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
Контрольные вопросы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
Резюме . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502
Контрольный список . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 504
Контрольные вопросы . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 504
Библиография . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 688
Алфавитный указатель . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 693
Отзывы на книгу «Ремесло программиста»
Эта книга создавалась в течение ряда лет. Говорят, что терпение воз#
награждается. Множество людей оказывало мне поддержку на протя%
жении этого пути…
Больше всего благодарности и сочувствия заслуживает моя жена
Бриони, которая терпела меня вместе с этим проектом на протяжении
столь долгого времени. Послание к Филиппийцам 1,3.
Мой друг, отличный программист и выдающийся иллюстратор Дэй%
вид Брукс (David Brookes), превратил мои ужасные комиксы с обезь%
янками и дурацкими шутками в прелестные вещицы. Спасибо, Дэйв!
Неуклюжие шутки остаются на моей совести.
Ряд людей прочел первые наброски этой книги в том или ином виде.
Особая благодарность ACCU (www.accu.org), которая успешно помогла
мне проверить свои писательские навыки. Спасибо специалистам
cthree.org Энди Берроузу (Andy Burrows), Эндрю Беннету (Andrew
Bennett) и Крису Риду (Chris Reed), давшим ценные отзывы, а также
Стиву Лаву (Steve Love) и ребятам с #ant.org. Джон Джеггер (Jon Jag%
ger) написал взвешенную техническую рецензию и поделился расска%
зами о собственных сражениях и полученных в них шрамах, благода%
ря чему книга стала намного лучше.
По большей части эта книга отражает мой собственный опыт и огорче%
ние жалким состоянием разработки программного обеспечения в реаль%
ном мире, а также желание помочь людям совершенствоваться в своей
специальности. Поэтому «благодарности» заслуживают также различ%
ные проблемные компании, в которых я когда%то работал, и ужасные
программисты, с которыми я там встречался. Они дали мне столько
материала для возмущения, что его хватит едва ли не на всю жизнь!
Я не сразу понял, насколько мне повезло.
Наконец, спасибо всем сотрудникам No Starch Press, принявшим мою
рукопись в малоприятном формате XML и превратившим ее в отлич%
ную книгу. Спасибо за вашу веру в проект и за ваш труд.
Предисловие
Есть много вещей, о которых умный человек
предпочел бы не знать.
Ральф Уолдо Эмерсон
говорится и кое о чем еще: о том, как писать правильный код правиль#
ным образом.
Что это значит? Есть много аспектов написания хорошего кода в ре%
альном мире:
• Разработка технически элегантного кода
• Создание кода, доступного для сопровождения, т. е. понятного
другим
• Способность разобраться в чужом запутанном коде и переделать его
• Умение работать вместе с другими программистами
Все эти навыки (и многие другие) необходимы, чтобы стать настоя%
щим кодером. Вы должны знать скрытую жизнь своего кода: что про%
исходит с ним после того, как вы его набрали. У вас должно быть раз%
вито эстетическое чувство: красивый код отличается от уродливого.
И нужно обладать практицизмом: решать, когда оправданы упроще%
ния, когда требуется поработать над архитектурой кода, а когда нуж%
но все бросить и двигаться дальше (прагматический принцип «не тро#
гай то, что уже работает»). Эта книга поможет вам решать такие за%
дачи. Вы узнаете, как выжить в условиях промышленного производ%
ства программ, как вести разведку и выяснять замыслы противника,
какой тактики придерживаться, чтобы не угодить в расставленные
противником ловушки, и как, несмотря на все препятствия, все%таки
создавать прекрасные программы.
Разработка программ – интересная профессия. Она динамична, в ней
множество преходящих модных поветрий, схем быстрого обогащения
и проповедников новых идеологий. Она еще не достигла зрелости. Я не
претендую на изобретение чудодейственных средств, но у меня есть не%
которые практичные и полезные рекомендации, которыми я хочу по%
делиться. Это не теория башни из слоновой кости, а реальный опыт
и добросовестная практика.
К тому моменту, когда вы переварите этот материал, вы не просто на%
учитесь лучше программировать. Вы сможете успешнее выживать в ус%
ловиях этой отрасли. Станете настоящим бойцом. Вы освоите ремесло
кодировщика. Если такая перспектива вас не вдохновляет, вам, воз%
можно, стоит подумать о военной карьере.
Стремление к совершенству
Так чем же хорошие программисты отличаются от плохих? Или лучше –
чем отличные программисты разнятся от удовлетворительных? Сек%
рет заключается не только в технической компетенции – я встречал
толковых программистов, способных энергично и впечатляюще пи%
сать на C++, знающих этот язык на зубок, но код их просто ужасал.
Знавал я и более скромных программистов, которые старались писать
очень простой код, но их программы были чрезвычайно элегантны и хо%
рошо продуманы.
Предисловие 21
Структура книги
Я постарался максимально облегчить чтение книги. Здравый смысл
подсказывает, что книгу нужно читать последовательно с начала до
конца. К данной книге это не относится. Можете открыть любую гла%
ву, которая вас заинтересовала, и начать чтение с нее. Каждая глава
самостоятельна, но содержит полезные перекрестные ссылки, благо%
даря которым видна общая взаимосвязь. Конечно, если вы предпочи%
таете традиционное чтение, ничто не мешает начать с самого начала.
Структура всех глав одинакова и не сулит неприятных сюрпризов.
Главы состоят из следующих разделов:
В этой главе
Сначала перечисляются основные темы главы. В нескольких стро%
ках дается обзор содержания. Можете прочесть их и выяснить, о чем
будет рассказано.
Глава
Тот захватывающий материал, за возможность прочесть который
вы заплатили приличные деньги.
По всей главе встречаются «золотые правила». Таким образом я вы%
деляю важные советы, проблемы и позиции, поэтому обратите на
них внимание. Выглядят они так:
Резюме
Этот маленький раздел в конце каждой главы подытоживает изло%
жение. В нем дан общий обзор материала. Если вас действительно
поджимает время, можете прочесть только «золотые правила» и этот
завершающий раздел. Только никому не говорите, что я вам это по%
советовал.
После этого я сравниваю подходы, применяемые хорошими и пло%
хими программистами, чтобы вывести из них правильные установ%
ки, которые вам необходимо в себе выработать. При достаточной
24 Предисловие
Содержание глав
Каждая глава посвящена одной теме – одной из современных проблем
разработки программного обеспечения. Это обычные причины, по ко%
торым программисты пишут плохой код или плохо пишут код. В каж%
дой главе описаны правильные подходы и установки, делающие более
сносной жизнь бойца на передовой.
Главы распределены по шести частям. В начале каждой части приво%
дится оглавление и краткое описание содержимого каждой главы.
Части организованы так, что сначала рассматривается, какой код мы
пишем, а в конце – как мы его пишем.
Наше исследование начинается с внешнего вида кода, сосредоточива%
ясь на микроуровне написания исходного кода. Я умышленно начинаю
с этих вопросов – программисты редко обращают на них внимание:
Часть I. Перед лицом кода
В этой части рассматриваются основные элементы разработки ис%
ходного кода. Мы изучим методы защитного программирования
Предисловие 25
Готовьтесь к худшему
Во время написания кода невольно делаются какие%то предположения
о том, как он будет выполняться, как будет происходить его вызов, ка%
кие входные данные допустимы и т. д. Можно даже не заметить сде%
ланные допущения, потому что они кажутся очевидными. Проходят
месяцы беззаботного кодирования, и прежние предположения все бо%
лее стираются из памяти.
Иногда вы берете старый код и делаете в нем очень важное исправление
за пять минут до отправки готового продукта. Времени остается только
на то, чтобы бегло взглянуть на структуру кода и прикинуть, как он
будет работать. Полный критический разбор проводить некогда, и вам
остается только надеяться, что код действительно делает то, что сле%
дует, пока не представится возможность проверить это на практике.
Предположения служат причиной появления кода с ошибками. Очень
легко предположить следующее:
• Функцию никогда не станут вызывать таким способом. Ей всегда
будут передаваться только допустимые параметры.
• Этот фрагмент кода всегда будет работать, он никогда не сгенериру%
ет ошибку.
• Никто не станет пытаться обратиться к этой переменной, если
я напишу в документации, что она предназначена только для
внутреннего употребления.
В защитном программировании нельзя делать никаких допущений.
Нельзя предполагать, что некое событие никогда не случится. Нико%
гда нельзя предполагать, что все в мире будет происходить так, как
нам бы того хотелось.
Опыт показывает, что уверенным можно быть только в одном: в один
прекрасный день ваш код по каким%то причинам даст сбой. Кто%то обя#
зательно сделает глупость. Закон Мерфи гласит: «Если какая%нибудь
неприятность может произойти, она обязательно случится». Прислу%
шайтесь к автору – он говорит о том, что познал на собственном опыте.1
Защитное программирование ограждает от таких несчастных случаев,
поскольку предвидит их заранее или хотя бы пытается предугадать
путем выяснения неприятностей, способных произойти на каждом
этапе выполнения кода, и принятия защитных мер.
Вы считаете это паранойей? Возможно, вы правы. Но быть немного
параноиком не вредно. На самом деле, это весьма разумно. По мере
Чем больше спешки, тем меньше скорость. Всегда думайте, что вы собирае&
тесь ввести с клавиатуры.
Не верьте никому
Мама говорила вам, чтобы вы не вступали в разговор с незнакомыми
людьми? К сожалению, разработка хороших программ требует еще
больше цинизма и еще меньше доверия к человеческой натуре. Даже
добропорядочные пользователи могут вызвать проблемы у вашей про%
граммы; защищаясь, нельзя доверять никому.
У вас могут возникнуть проблемы по следующим причинам:
• Обычный пользователь случайно введет в программу неверные
данные или воспользуется ею некорректно.
38 Глава 1. Держим оборону
Не верьте никому. Кто угодно, включая вас самих, может сделать ошибки
в логике вашей программы. Ко всем входным и выходным данным относитесь
с подозрением, пока не проверите, что они допустимы.
Когда приступать?
Когда начинать программировать в защитном стиле? После того
как выявятся неполадки? Или когда попадется непонятный код,
написанный кем%то другим?
Нет, технологии защитного программирования нужно применять
постоянно. Они должны войти в привычку. Опытные программи%
сты знают это по своему опыту; они достаточно часто обжигались,
и теперь размещают в программах разумные средства защиты.
Защитные средства гораздо проще применять с самого начала
написания кода, нежели пытаться вмонтировать в уже сущест%
вующий. Если пытаться втиснуть их в код на поздних стадиях
работы с ним, трудно ничего не упустить и быть точным. Если
вы начинаете добавлять защитный код уже после того, как воз%
никли неполадки, то фактически занимаетесь отладкой, т. е. ре%
агируете на события, а не предупреждаете их.
Тем не менее в процессе отладки или добавления новых функций
обнаруживаются условия, которые желательно проверять. Вот
тогда и полезно добавить защитный код.
strcpy(buffer, source);
return buffer;
}
Если в источнике (source) больше 10 символов, то их копирование вый%
дет за пределы памяти, выделенной под буфер (buffer). Результат мо%
жет оказаться самым неожиданным. В лучшем случае произойдет раз%
рушение данных, если окажется измененным содержимое какой%то
другой структуры данных. В худшем – злоумышленник может вос%
пользоваться этой ошибкой в программе и поместить в стек програм%
мы исполняемый код, с помощью которого запустит собственную про%
грамму и фактически завладеет компьютером. Ошибками такого рода
систематически пользуются взломщики; это серьезная проблема.
Предотвратить появление таких уязвимостей нетрудно. Не пишите
скверный код! Пользуйтесь более надежными структурами данных,
которые не позволяют искажать программу, например управляемыми
буферами типа класса string в C++. Либо применяйте к небезопасным
типам данных только безопасные операции. Приведенный выше код
C++ можно защитить, заменив strcpy на strncpy, которая копирует
строку ограниченной длины:
char *safer_copy(const char *source)
{
char *buffer = new char[10];
strncpy(buffer, source, 10);
return buffer;
}
В Java и .NET есть сборщик мусора, который сделает вместо вас всю
эту скучную приборку, чтобы вы могли «забыть» об освобождении ре%
сурсов. Можно мусорить на пол, потому что на этапе исполнения за ва%
ми будут периодически подметать. Это замечательно удобно, но у вас
не должно возникнуть ложного чувства безопасности. Думать самому
все равно нужно. Вы должны явно уничтожить ссылки на объекты,
которые вам больше не нужны, иначе они не будут убраны; не делайте
ошибки, сохраняя ссылки на объекты. Кроме того, менее изощренные
сборщики мусора могут быть введены в заблуждение циклическими
ссылками (например, A ссылается на B, B ссылается на A, но тот и дру%
гой никому не нужны). В этом случае объекты могут никогда не по%
пасть в мусор, что ведет к скрытой утечке памяти.