WWW.PDF.KNIGI-X.RU
БЕСПЛАТНАЯ  ИНТЕРНЕТ  БИБЛИОТЕКА - Разные материалы
 

Pages:     | 1 || 3 | 4 |   ...   | 5 |

«ПРОГРАММИСТ ПРАГМАТИК Путь от подмастерья к мастеру г* Как бороться с недостатками программного обеспечения _ _ Как создать динамичную и адаптируемую программу Т • ч ...»

-- [ Страница 2 ] --

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

From X25LINE1 (Format=ABC123) { Put TELSTAR1 (Format=XYZ43B);

Store DB;

}

–  –  –

После того как вы написали приложение, пользователи предъявляют вам новое требование: сообщения с отрицательным балансом не должны сохраняться и должны отсылаться обратно по каналам связи Х.25 в первоначальном формате:

From X25LINE1 (Format=ABC123) { if (АВС123.balance 0) { Put X25LINE1 (Format=ABC123);

} else { Put TELSTAR1 (format=XYZ43B);

Store DB;

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

Подсказка 17 Программируйте ближе к предметной области вашей задачи

Мы полагаем, что вам следует рассмотреть способы перемещения вашего объекта ближе к предметной области проблемы — будь то простейший язык для конфигури­ рования и управления прикладной программой или ж е более сложный язык для обо­ значения правил или процедур. При составлении программы на более высоком уров­ не абстракции вам легко сосредоточиться на решении проблем предметной области и вы можете проигнорировать мелкие детали, связанные с реализацией.

Помните, что с приложением работают многие пользователи. Существует конеч­ ный пользователь, который понимает правила предметной области и то, что должно быть на выходе программы. Имеются также вторичные пользователи: обслуживаю­ щий персонал, менеджеры, занимающиеся конфигурированием и тестированием, программисты служб поддержки и сопровождения и будущие поколения разработчи­ ков. У каждого из этих пользователей есть собственная предметная область, и для всех них вы можете генерировать мини-среды и языки.

–  –  –

"АВ123" is not a format. Known formats are ABC123, XYZ43B, PDQB, and 42.

Реализация мини-языка В самом простейшем варианте мини-язык может реализовываться в строчно-ориентированном, легко анализируемом формате. Практически мы используем эту форму больше, чем любую другую. Ее просто проанализировать при помощи инструкций switch, или используя регулярные выражения в языках сценариев типа Perl. Ответ к упражнению 5 (Приложение В) показывает простую реализацию мини-языка на языке С.

Вы можете реализовать и более сложный язык, с более формальным синтаксисом.

Хитрость состоит в том, чтобы вначале определить синтаксис, используя систему обо­ значений, подобную нормальной форме Бэкуса-Наура. Как только вы определили грамматику, ее обычно легко преобразовать во входной синтаксис для генератора.

Программисты, работающие с языками С и С + +, давно используют уасс (или его бесплатную версию bison [URL 27]). Подробное описание этих программ приводится в книге "Lex and Yacc" [LMB92]. Программисты, работающие с языком Java, могут поработать с программой javaCC, которая находится на сайте [URL 26]. В ответе к уп­ ражнению 7 (Приложение В) показана программа грамматического разбора, напи­ санная с помощью bison. Пример показывает, что, как только вы изучите синтаксис, написание мини-языков не представляет сложности.

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

Например, можно могли интегрировать функциональные возможности на уровне приложения при помощи Python [URL 9] и написать нечто вроде :

–  –  –

Языки управления донными и процедурные языки Реализуемые вами языки могут использоваться двумя различными способами.

Языки управления данными создают некую форму структуры данных используе­ мой приложением. Эти языки часто используются для представления конфигурации.

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

Она обладает многими достоинствами и преиму­ ществами, которые управляются огромным файлом конфигурации, написанном на собственном языке конфигурирования программы sendmail:

Mlocal, P=/usг/bin/procmai1, F=lsDFMAw5:/|@qSPfhn9, S=10/30, R=20/40, T=DNS/RFC822/X-Unix, A=procmail -Y -a $h -d $u Очевидно, удобочитаемость не является сильной стороной sendmail.

Уже давно фирма Microsoft использует язык данных, который может описывать меню, реквизиты окон, диалоговые окна и другие ресурсы Windows. На рис. 2.2 пока­ зан фрагмент типичного файла ресурсов. Он читается намного легче, чем пример с программой sendmail, но используется точно так же — компилируется для генерации структуры данных.

Процедурные языки идут дальше. В этом случае язык является исполняемым и поэтому может содержать инструкции, конструкции управления и т.п. (подобные сце­ нарию на с. 50).

–  –  –

Вы также можете использовать собственные процедурные языки, чтобы облег­ чить сопровождение программы. Например, вас просят интегрировать информа­ цию из унаследованного приложения в вашу новую разработку графического ин­ терфейса. Обычно это осуществляется при помощи "экранного кармана"; ваше приложение связывается с основным (mainframe) приложением, так, как если бы это обычный пользователь-человек, генерируя нажатия клавиш и "считывая" при­ нимаемые отклики. Вы можете создать сценарий взаимодействия при помощи мини-языка.

locate prompt "SSN:"

type "%S" social_security_number type enter waitfor keyboardunlock if text_at(10,14) is "INVALID SSN" return bad_ssn if text_at(10,14) is "DUPLICATE SSN" return dup_ssn # etc...

Когда приложение определяет, что пора вводить номер SSN, то по этому сцена­ рию оно вызывает интерпретатор, который затем управляет транзакцией. Если ин­ терпретатор встроен в приложение, то они даже могут совместно использовать дан­ ные (например, при помощи механизма обратного вызова).

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

Автономные и встроенные языки Чтобы приносить пользу, мини-язык не должен использоваться приложением напря­ мую. Можно многократно использовать язык спецификации, чтобы создать искусст­ венные объекты (включая метаданные) которые компилируются, считываются или используются самой программой иным образом (см. "Метапрограммирование").

Например, в разделе "Обработка текста" описывается система, в которой мы ис­ пользовали Perl, чтобы генерировать большое количество выводов из первоначаль­ ной спецификации схемы. Мы изобрели общий язык, чтобы представить схему базы данных, и затем сгенерировали все его формы, которые нам необходимы, — SQL, С, интернет-страницы, XML и др. Приложение не использовало спецификацию напря­ мую, но оно полагалось на выходные данные, полученные из нее.

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

–  –  –

делается без компиляции. Это существенно упрощает сопровождение в области дина­ мической области приложения.

Несложная разработка или несложное сопровождение?

Мы рассмотрели несколько различных грамматик, начиная от простых строчно-ориентированных форматов до более сложных, которые выглядят как реальные языки.

Если для реализации требуются дополнительные усилия, тогда зачем выбирать более сложную грамматику?

Компромиссом являются расширяемость и сопровождение. В то время как про­ грамма грамматического разбора "реального" языка" может быть более сложной в написании, для пользователей она будет намного понятнее, и ее будет легче расши­ рить за счет добавления новых средств и функциональных возможностей. Слишком простые языки могут быть легкими для грамматического разбора, но они могут быть зашифрованными — подобно примеру с программой sendmail (см. "Языки управле­ ния данными и процедурные языки").

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

Другие разделы, относящиеся к данной теме: • Метапрограммирование

Вопросы д л я обсуждения

• Можно ли выразить некоторые из требований проекта, над которым вы ра­ ботаете в настоящее время, на языке, отражающем специфику предметной области? Возможно ли написать компилятор или транслятор, который мог бы сгенерировать большую часть требуемой программы?

• Если вы решили принять мини-язык как способ программирования, близкий к предметной области, то вы принимаете и то, что для реализации потребу­ ются некоторые усилия. Как выдумаете, есть ли способы, при которых "ске­ лет", разработанный для одного проекта, может многократно использовать­ ся в других?

–  –  –

N 1 # then north 1 Е 2 # then e a s t 2 S 1 n then back south U # pen up Составьте программу, которая анализирует этот язык. Она должна быть разрабо­ тано так, чтобы операция добавления новых команд была несложной.

6. Спроектируйте грамматику BNF (нормальной формы Бэкуса—Наура), чтобы провести грамматический разбор спецификаций времени. Все указанные при­ меры должны быть успешно проанализированы. (Ответ см. в Приложении В.) 4pm, 7:38pm, 23:42, 3:16, 3:16am

7. Реализуйте программу грамматического разбора для грамматики нормаль­ ной формы Бэкуса—Наура вупражнении 6, используя программы уасс, bison или аналогичный генератор грамматического разбора. (Ответ см. в Прило­ жении В.)

8. Реализуйте программу грамматического разбора времени, используя Perl.

(Подсказка: регулярные выражения позволяют написать хорошие программы грамматического разбора). (Ответ см. в Приложении В.) 13 Оценка Как скоро? Сколько времени потребуется для пересылки "Войны и мира" по модем­ ной линии 56 байт? Какое место займет на диске миллион имен и адресов? Сколько времени понадобится для прохождения 1000-байтового блока через маршрутизатор?

Сколько месяцев потребуется, чтобы завершить ваш проект?

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

Научившись оценивать и развивая этот навык до уровня, на котором у вас появля­ ется интуитивное ощущение величины тоге или иного предмета, вы сможете показать явно магическую способность к определению их выполнимости. Если кто-либо гово­ рит: "Мы вышлем вам резервную копию по каналу ISDN в центральный офис", вы сможете интуитивно осознать, имеет ли это смысл. Когда вы составляете программу, вы сможете понять, какие подсистемы нуждаются в оптимизации, а какие нужно оста­ вить в покое.

–  –  –

Насколько точной является "приемлемая точность"?

До некоторой степени все ответы представляют собой оценки. Просто некоторые из них точнее остальных. Так что первым вопросом, который вам придется задать самому себе, когда кто-либо просит вас об оценке, является вопрос о контексте, в котором будет приниматься данный вами ответ. Нужна ли здесь высокая точность, или речь идет о примерной цифре?

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

• Каково значение числа "пи"? Если вас интересует, какое количество бор­ дюрного камня понадобится для оформления цветочной клумбы, то цифра 3 вероятно будет приемлемой. На школьном уровне хорошим приближением является у-. Ну а если вы работаете в НАСА, то двенадцати цифр после запя­ той будет вполне достаточно.

Одной из интересных особенностей оценки является тот факт, что интерпретация ее результата зависит от используемых вами единиц измерения. Если выговорите, что для некоего действия потребуется 130 рабочих дней, то люди будут ожидать наступле­ ния этого события в достаточно узком интервале. Но если вы скажете "около шести месяцев", они будут знать, что этого события следует ожидать через 5—7 месяцев.

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

Мы рекомендуем следующую градацию оценок времени:

–  –  –

Так, если после всей необходимой работы, вы придете к решению, что проект зай­ мет 125 рабочих дней (25 недель), он может быть оценен как "примерно за шесть ме­ сяцев".

Для законодателей вполне достаточно значения 3. В законопроекте № 246 Законодатель­ ного собрания штата Индиана (1897) была сделана попытка установить, что отныне число "л" будет равно 3. Во втором чтении законопроект был отложен на неопределенное время, так как некий профессор математики указал, что власть законодателей не распространяет­ ся на законы природы.

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

Из чего исходят оценки?

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

Понимание сути заданного вопроса Первой частью любого упражнения в составлении оценки является понимание сути заданного вопроса. Как и в случае с вопросами точности, обсуждаемыми выше, вам необходимо осознать масштаб предметной области. Зачастую он неявно выражен в самом вопросе, но осознание масштаба, перед тем, как начать строить предположе­ ния, должно войти у вас в привычку. Зачастую выбранная вами предметная область частично формирует ответ, который выдаете: "Если предположить, что по дороге не будет аварий и машина заправлена, я буду там через 20 минут" Построение модели системы Эта часть процесса оценки — самая интересная. Исходя из вашего понимания задан­ ного вопроса, постройте в уме скелет действующей модели. Если вы оцениваете вре­ мя отклика, то в вашей модели может иметься узел обслуживания и некий входной по­ ток. При работе над проектом моделью могут послужить стадии, которые ваша организация использует в разработке, наряду с весьма грубым представлением того, как система может быть реализована.

Построение модели может быть творческим процессом и полезным в долгосрочной перспективе. Зачастую процесс построения модели приводит к открытию схем и про­ цессов, лежащих в основе чего-либо и не видимых невооруженным глазом. У вас даже может возникнуть желание повторно исследовать исходный вопрос: "Вы попросили дать оценку X. Однако, похоже, что Y, являющийся вариантом X, может быть выполнен примерно в два раза быстрее, и при этом вы теряете лишь одну характеристику".

Построение модели вносит погрешности в процесс оценки. Это и неизбежно, и по­ лезно. Вы жертвуете простотой модели ради точности. Удвоение усилий, прилагаемых к модели, может увеличить точность лишь незначительно. Ваш опыт подскажет вам, когда закончить процесс совершенствования.

–  –  –

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

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

Присвоение значения каждому параметру Как только в вашем распоряжении появились параметры, вы можете пойти напро­ лом и присвоить некое значение каждому из них. На этой стадии вы ожидаете вне­ сения некоторой ошибки. Хитрость состоит в том, чтобы понять, какие параметры оказывают максимальное воздействие на результат, и сосредоточиться на их точ­ ном получении. Обычно параметры, чьи значения добавляются к результату, явля­ ются менее значительными, чем те, что осуществляют умножение или деление. Уд­ воение скорости канала связи может увеличить вдвое объем данных, получаемых в течение часа, тогда как добавление транзитной задержки, равной 5 мс, не даст за­ метного эффекта.

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

Вычисление ответов Только в самом простом случае ваша оценка будет иметь один-единственный ответ.

Вы счастливый человек, если можете сказать: "Я могу пройти по городу пять квар­ талов за 15 минут". Но поскольку системы все усложняются, вам захочется подстра­ ховать ваши ответы. Проведите многократные вычисления, изменяя значения кри­ тических параметров, пока не выясните, какие из них действительно управляют моделью. Серьезную помощь в этом может оказать электронная таблица. Затем сформулируйте ваш ответ с точки зрения этих параметров. "Время отклика состав­ T ляет (грубо) три четверти секунды, если система имеет : i: 'iy.SCSI и объем памяти 64 Мбайт; и одну секунду при объеме памяти 48 Мбайт". (Заметьте, что "три чет­ верти секунды" дает иное ощущение точности, нежели 750 мс) Уже на стадии вычислений появляются ответы, которые могут показаться стран­ ными. Не спешите игнорировать их. Если ваша арифметика правильна, то, вероят­ но, ваше понимание проблемы или модель неверны. Это ценная информация.

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

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

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

• Проверить требования

• Проанализировать риск

• Осуществить проектирование, реализацию, интеграцию

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

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

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

Подсказка 19 Уточняйте график проекта на основе текста программы Это может не понравиться руководству, которому обычно нужно единственная на­ дежная цифра еще до начала проекта. Вам придется помочь им осознать, что команда, ее производительность и среда будут определять график выполнения. Формализуя эту про­ цедуру и уточняя график (что является частью итерационного процесса), вы сможете дать руководству самые точные оценки графика выполнения, какие только сможете.

Глава 2 Что сказать, если вас просят оценить что-либо Говорите: "Я вернусь к вам с этим позже".

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

Другие разделы, относящиеся к данной теме:

• Скорость алгоритма Вопросы д л я обсуждения

• Заведите журнал регистрации сделанных вами оценок. Для каждой оценки укажите, насколько точной она оказалась. Если отклонение превысило 5 0 %, постарайтесь выяснить, где была допущена ошибка.

Упражнения

9. Спрашивается: какой из двух каналов обладает более широкой полосой про­ пускания: линия связи со скоростью 1 Мбайт/сек, или человек, двигающийся от компьютера к компьютеру со стриммерной кассетой объемом 4 Гбайт в кар­ мане? Какие ограничения накладываются на ответ, чтобы гарантировать его корректность в определенной области? (Например, можно указать, что време­ нем доступа к ленте можно пренебречь). (Ответом, в Приложении В.)

10. Так какой же из двух каналов обладает более широкой полосой пропускания?

(Ответ см. в Приложении В.) Глава 3 Походный набор инструментов Каждый ремесленник отправляется на поиски заработка, имея при себе походный на­ бор инструментов. Столяру могут пригодиться линейки, шаблоны, пара ножовок, не­ сколько рубанков, тонкие стамески, сверла и зажимы, киянки и струбцины. Эти инст­ рументы он будет тщательно выбирать, настраивать, каждому из них будет уготована определенная работа, и, что наверное самое важное каждый из них, оказавшись в умелых руках столяра, найдет свое место под солнцем.

После этого придет черед обучению и притирке. Каждому инструменту будут присущи свои особенности (и хитрости), и каждый из них потребует, чтобы с ним об­ ращались особым образом. При работе столяр держит инструмент особым образом, и затачивает его под особым углом. Пройдет время, и от работы инструмент изно­ сится до того, что рукоятка превратится в слепок руки столяра, а режущая поверх­ ность сравнится с углом, под которым столяр держит инструмент относительно ра­ бочей плоскости. В этот момент инструменты станут проводниками идей от головы столяра к конечному продукту — они станут продолжением рук мастера. Пройдет время, и в арсенале столяра прибавятся новые орудия — резальные машины, ла­ зерные станки для резки под углом, направляющие шаблоны "ласточкин хвост" — всё это чудеса технологического прогресса. Но можно поспорить, что понастоящему он счастлив только тогда, когда держит в руках инструмент из старого походного набора и слышит, как рубанок поет свою песню, выстругивая деревянную заготовку.

Инструменты — средство усиления вашего таланта. Чем лучше они и чем лучше вы ими владеете, тем больше вы сможете сделать. Начните с походного универсаль­ ного набора инструментов. По мере того как вы приобретаете опыт и сталкиваетесь с специальными требованиями, ваш набор пополняется. Стоит уподобиться ремес­ леннику и пополнять набор регулярно. Старайтесь не прекращать поисков лучшего способа сделать что-либо. Оказавшись в ситуации, когда вы обнаруживаете, что ва­ ших инструментов недостаточно, поищите иное, возможно, более мощное средство Глава 3 для осуществления задуманного. Ваши приобретения должны исходить из сущест­ вующей необходимости.

Многие начинающие программисты делают ошибку, принимая на вооружение одно-единственное мощное инструментальное средство, в частности, конкретную ин­ тегрированную среду разработчика (ИСР), и никогда не выходят за пределы удобного для них интерфейса. Это действительно ошибочно. Необходимо осваиваться и вне пределов, установленных ИСР. Но это можно сделать лишь при условии, что инстру­ менты из походного набора должным образом заточены и готовы к работе.

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

Как и в любой хорошей дискуссии об инструментах, начнем ( в разделе "Преимущест­ ва простого текста") с рассмотрения сырья — материала, которому будет придана форма. Затем мы перейдем к верстаку — в нашем случае его роль играет компьютер.

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

Д а ж е для таких простых вещей, как личная адресная книжка, необходимо ис­ пользовать "Систему управления исходным текстом" как гарантию того, что даже самая малая часть вашей драгоценной работы не канет в небытие! И поскольку от­ крыватель законов Мерфи все ж е был оптимистом, то вы не можете считать себя великим программистом, пока не приобретете серьезных навыков в отладке (см.

"Отладка").

Чтобы как-то объединить большую часть элементов магии, необходимо некое свя­ зующее вещество (наподобие столярного клея). Некоторые средства, подобные awk, Perl и Python, рассмотрены в разделе "Обработка текста".

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

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

14 Преимущество простого текста Основной материал, с которым работают программисты-прагматики, — не де­ рево, и не металл, а человеческое знание. Оно является форматом при сборе требо­ ваний, а затем выражается в конструкциях, реализациях, тестах и документации. И мы уверены, что лучшим форматом для постоянного хранен./л знания является про­ стой текст. Простой текст позволяет нам обрабатывать знание, как вручную, так и с Походный набор инструментов помощью программных средств, используя практически все инструменты, имею­ щиеся у нас под рукой.

Что такое простой текст?

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

Field19=467abe Читатель и понятия не имеет, каков смысл значения 467abe.

Лучше сделать его понятным:

DrawingType=UMLActivityDrawing Простой текст вовсе не означает, что в нем отсутствует структура; яркими приме­ рами простого текста с четко определенной структурой являются форматы XML, SGML и HTML. С простым текстом можно проделывать все те ж е операции, что и с двоичным форматом, включая управление версиями.

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

Используя простей гскат, вы можете записать это следующим образом:

myprop.uses_menus=FALSE

А теперь сравните это с 0010010101110101.

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

Подсказка 20 Сохраняйте знания в формате простого текста

–  –  –

В зависимости от приложения неприемлемыми могут оказаться одна или обе вы­ шеописанные ситуации — например, при хранении данных СПУТНИКОВОЙ телеметрии или в случае внутреннего формата реляционной базы данных.

Но и в этих ситуациях допустимо сохранять метаданные, описывающие исходные данные, в формате простого текста (см. "Метапрограммирование").

Некоторые разработчики боятся помещать метаданные в формате простого тек­ ста, потому что таким образом они раскрывают его содержимое пользователям сис­ темы. Эти опасения не имеют достаточных оснований. Двоичные данные могут быть более расплывчатыми, чем простой текст, но от этого не становятся более защи­ щенными. Если вы не хотите, чтобы пользователи видели пароли, зашифруйте их.

Если вы не хотите, чтобы они изменяли параметры конфигурации, примените техно­ логию защищенного хеширования ко всем значениям параметров и используйте ре­ зультат в контрольной сумме файла.

Преимущества простого текста Поскольку "больше" и "медленнее" — не самые популярные требования, предьявгаемые пользователями, то зачем вообще нужен простой текст? Каковы его преиму­ щества?

• Гарантия того, что данные не устареют

• Более короткий путь к цели

• Более простое тестирование Гарантия того, что донные не устареют Форматы данных, которые может воспринять человек, и самодокументированные данные переживут все другие форматы данных и приложения, их породившие.

И точка.

На протяжении всего срока жизни данных вы сможете пользоваться ими и в пер­ спективе еще долго после того, как прикладная программа, их породившая, прекратит свое существование.

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

Рассмотрим файл данных из некой унаследованной системы. Вы обладаете скуд­ ной информацией о прикладной программе, которая создала этот файл; эта информа­ ция сводится к тому, что она поддерживала список номеров SSN (Social Security Number — номер социального страхования) клиентов, которые вам необходимо най­ ти и извлечь из архива.

Среди данных вы видите:

Для этой цели часто применяется M D 5. Великолепное введение в чудесный мир крипто­ графии — книга [Sch95].

Все программы становятся унаследованным, как только они написаны.

Походный набор инструментов "FIELD10"123-45-67897FIELD10" "FIELDIO'567-89-0123 "/FIELD10" "FIELD10"901-23-4567"/FIELD10"

–  –  –

"SSN0"123-45-67897SSN0" делает сие упражнение задачкой д л я детского сада и гарантирует, что данные пережи­ вут любой проект, их породивший.

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

–  –  –

Например, предположим, что вы развертываете крупномасштабное приложение со сложным конфигурационным файлом, характерным для конкретного местополо­ жения (на ум сразу приходит sendmail). Если этот файл представляет собой простой текст, то его можно подчинить системе управления исходными текстами (см. "Управ­ ление исходным текстом"), и вы автоматически сохраняете хронологию всех измене­ ний. Инструментальные средства сравнения файлов, такие как diff и fc, позволяют вам сразу увидеть, какие изменения были внесены, тогда как su*^ позволяет генерировать контрольную сумму для отслеживания файла на предмет случайных (или злонамерен­ ных) модификаций.

Более простое тестирование ЕСЛИ ВЫ используете простой текст при создании синтетических данных для запуска системных тестов, то добавление, обновление или модификация тестовых данных (без привлечения каких-либо специальных инструментальных средств) не представляет особого труда. Аналогично, результат регрессионного тестирования в виде простого текста может быть проанализирован тривиальным образом (например, с помощью программы diff) или более тщательно с помощью языков Perl, Python и некоторых других средств написания сценариев (скриптов).

Подводим итог Вездесущий текстовый файл никуда не денется и в будущем, когда интеллектуальные XML-базированные агенты, путешествующие по диким и опасным дебрям Интернета в автономном режиме, будут согласовывать обмен данными между собой. Действи­ тельно, в гетерогенных операционных средах преимущества простого текста могут перевесить все его недостатки. Необходимы гарантии того, что все стороны могут об­ мениваться информацией по общему стандарту. Таким стандартом и является простой текст.

Другие разделы, относящиеся к данной теме:

–  –  –

Вопросы для обсуждения

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

1. Преобразуйте этот формат в формат простого текста, используя XML.

2. Для каждой из версий добавьте новое поле переменной длины под на­ званием directions, в котором вы могли бы вводить указания, как подъе­ хать к дому каждого адресата.

Какие вопросы, связанные с управлением версиями и расширяемостью, мо­ гут возникнуть? Какую форму легче модифицировать? Как обстоит дело с преобразованием уже существующих данных?

15 Игры с оболочками Каждому столяру нужен хороший, солидный, надежный верстак — место, располо­ женное на удобной для столяра высоте, на котором он в ходе своей работы мог бы разместить предметы труда. Верстак становится центром мастерской, ремесленник возвращается к нему снова и снова, придавая форму материалу.

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

Для программистов, выросших на графических интерфейсах и ИСР, это может по­ казаться экстремизмом. В конце концов, разве нельзя проделать все операции с рав­ ным успехом, указывая на объект и щелкая кнопкой мыши?

Ответ прост: "Нет". Графические интерфейсы сами по себе прекрасны, и с их по­ мощью многие простые операции выполняются быстрее и с большим удобством. Пе­ ремещение файлов, чтение сообщений электронной почты с кодировкой MIME и на­ бор текстов писем — это все то, что вы хотели бы осуществлять в графической среде.

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

Глава 3 Графические среды обычно ограничены возможностями, заложенными в них разработчиками. Если вам необходимо выйти за пределы модели, созданной разра­ ботчиком, то обычно фортуна отворачивается от вас, однако чаще всего вам всетаки приходится выходить за пределы модели. Прагматики не просто "рубят" текст, разрабатывают объектные модели, пишут документацию или автоматизируют про­ цесс сборки — они делают все вышеперечисленное. Сфсрг: применения любого конкретного инструмента обычно ограничена задачами, решения которых от него ожидают. Предположим, что возникла необходимость в интеграции препроцессора исходного текста с И С Р (при реализации концепции "проектирования по контрак­ ту", многопроцессных директив и т.п.). Если разработчик И С Р явно не предусмот­ рел наличия в ней специальных средств, то вы не справитесь с решением задачи ин­ теграции.

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

Исповедуя прагматизм, вы постоянно будете испытывать потребность в осуще­ ствлении операций ad hoc {лат. для конкретного случая. — Прим. пер.) — это и есть те самые случаи, когда графический интерфейс может оказаться непримени­ мым. Командная строка может стать лучшим решением, если необходимо быстро скомбинировать несколько команд при выполнении запроса или иного задания.

Ниже приводится несколько примеров.

Найти все файлы типа *.с, модифицированные позже, чем ваш Makefile.

Командная строка find. --name ' *. с ' --newer Makefile --print

–  –  –

Ясно, что этот список может быть продолжен. Строчные команды могут быть не­ понятными и компактными, но они обладают мощностью и краткостью. И поскольку они могут сводиться в файлы сценариев (или командные файлы в системе Windows), то вы можете создавать последовательности команд для автоматизации часто выпол­ няемых процедур.

Подсказка 21 Используйте сильные стороны командных оболочек Освойте работу с оболочкой, и вы обнаружите, как выросла ваша производитель­ ность. Нужно создать перечень всех уникальных имен пакетов, которые явно импор­ тируются вашей программой на языке Java? Приведенная ниже программа сохраняет этот перечень в файле под названием "list".

grep '"import ' *.java | sed - - e ' s /. "import / / ' — e ' s / ;.. $ / / ' I s o r t —и l i s t Если вам еще не приходилось часами изучать возможности командной оболочки систем, с которыми вы работаете, то это занятие может показаться устрашающим.

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

Глава 3 Утилиты оболочек и системы Windows Хотя командные оболочки, поставляемые с системами Windows, постепенно улучша­ ются, утилиты командной строки Windows все еще уступают их двойникам в Unix. Од­ нако все не так плохо.

Фирма Cygnus Solutions разработала пакет под названием Cygwin [URL 31]. По­ мимо обеспечения слоя совместимости Unix для Windows, Cygwin поставляется вме­ сте с коллекцией более чем 120 утилит Unix, включая такие бестселлеры, как Is, grip и find. Утилиты и библиотеки могут загружаться и использоваться бесплатно, но обя­ зательно прочтите их лицензию. Программа Cygwin распространяется вместе с обо­ лочкой Bash.

–  –  –

В качестве альтернативы Дэвид. Корн (автор известной оболочки Копт) создал па­ кет под названием UWIN. Он предназначен для тех же целей, что и продукт Cygwin — Генеральная общая лицензия GNU [URL 57] является разновидностью легального вируса, который используется разработчиками программ с открытым текстом для защиты своих (и ваших) прав. Стоит уделить время ее изучению. В сущности, она говорит о том, что пользователь может использовать и модифицировать программы с генеральной общей ли­ цензией, но если он распространяет модифицированные программы, то они подлежат соот­ ветствующему лицензированию (и маркироваться как таковые), а исходный текст должен быть открыт. Это и есть часть вируса — если программа создается на основе лицензиро­ ванной программы, то первая также подлежит лицензированию. Тем не менее, пользова­ тель не ограничен никоим образом при использовании инструментальных средств — право собственности и лицензирование программ, разработанных при помощи указанных средств, находятся на усмотрении пользователя.

Походный набор инструментов это среда разработчика Unix, работающая в среде Windows. Пакет UWIN распро­ страняется с оболочкой Когп. Коммерческие версии поставляются фирмой Global Technologies, Ltd. [URL 30]. Кроме того, фирма AT&T допускает бесплатную загрузку пакета для оценки его работы и использования в академических учреждениях. Перед его использованием также необходимо прочесть лицензию.

И наконец, Том Кристиансен (во время написания книги) компонует Perl Power Tools, пытаясь в сжатом виде реализовать все известные утилиты Unix на языке Perl [URL 32].

Другие разделы, относящиеся к данной теме: • Вездесущая автоматизация

Вопросы д л я обсуждения:

• Существуют ли операции, которые в данное время вам приходится выпол­ нять вручную, работая в графической среде? Приходилось ли вам когда-либо сочинять для коллег по работе инструкции, состоящие из отдельных пунктов типа: "щелкните на этой кнопке", "выберите этот пункт"? Можно ли авто­ матизировать этот процесс?

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

• Изучите возможные альтернативы оболочке, используемой вами в настоя­ щее время. Если вы сталкиваетесь с проблемой, которую невозможно ре­ шить средствами имеющейся у вас оболочки, может быть, альтернативная оболочка проявит себя лучше?

16 Мощь редактирования Выше уже говорилось об инструментах, которые являются продолжением вашей руки. Это положение применимо к текстовым редакторам в большей степени, нежели к любому другому инструменту. Необходимо, чтобы вы затрачивали минимальные усилия на обработку текста, поскольку последний является основным "сырьем" при программировании. Рассмотрим некоторые общие характеристики и функции, кото­ рые помогают использовать ваш текстовый редактор с максимальным КПД.

Один-единственный редактор Мы полагаем, что лучше овладеть одним-единственным редактором, но в совершенст­ ве, и использовать его для всех задач, связанных с редактированием: работа с текстом программ, документацией, записками, системное администрирование и т.

д. Не имея под рукой хотя бы одного редактора, можно оказаться в ситуации, аналогичной вави­ лонскому смешению языков, но уже на современный манер. При написании текстов программ может понадобиться встроенный редактор И С Р (для каждого языка), для 72 Глава 3 создания документации — универсальный офисный редактор, а может быть, и еще один встроенный редактор для отправки сообщений по электронной почте. Различаться могут даже клавиатурные команды, используемые вами для редактирования командных строк в оболочке. Трудно быть экспертом в любой из этих программных сред, если в каждой из них имеется свой набор команд и соглашений при редактировании.

Но экспертом быть необходимо. Мало набирать символы построчно и использо­ вать мышь для вырезания и вставки фрагментов. Работая подобным образом, вы не достигнете того уровня производительности, который возможен при наличии мощно­ го текстового редактора. Десятикратное нажатие клавиши - или Backspace для пе­ ремещения курсора влево к началу строки не столь эффективно, как простая клавиа­ турная команда, например Ctrl+A, Home или 0.

Подсказка 22 Используйте один текстовый редактор, но по максимуму Выберите какой-либо редактор, тщательно изучите его и используйте во всех за­ дачах, связанных с редактированием текста. Если вы пользуетесь одним редактором (или набором функциональных клавиш) для всех работ, связанным с редактировани­ ем текста, то вам не придется останавливаться и обдумывать, как осуществить ту или иную обработку текста: нажатие нужных клавиатурных команд становится рефлек­ сом, редактор — продолжением вашей руки: клавиши поют свою песню, перемеща­ ясь по тексту сквозь череду мыслей. Это и есть цель!

Убедитесь, что выбранный вами редактор поддерживается всеми платформами, с которыми вы работаете. Редакторы Emacs, vi, CRISP, Brief и ряддругих поддержива­ ются несколькими платформами, часто в двух версиях — в графической и неграфиче­ ской (текстовый режим).

Средства редактирования Помимо тех средств, которые вы считаете особенно полезными и удобными, имеется ряд основных возможностей, которыми, по нашему мнению, должен обладать любой приличный редактор. Если в вашем редакторе отсутствует любая из этих возможно­ стей, то, вероятно, настало время поразмыслить о переходе к более продвинутому ре­ дактору.

• Настраиваемость. Все свойства редактора должны настраиваться по ва­ шему пожеланию, включая шрифты, цвета, размеры окон и клавиатурные привязки (команды, исполняемые при нажатии той или иной клавиши).

Применение только клавиатурных сочетаний в ходе обычных операций ре­ дактирования является более эффективным по сравнению с мышью или командами в меню, поскольку руки не отрываются от клавиатуры.

–  –  –

• Расширяемость. Редактор не должен устаревать только потому, что появля­ ется новый язык программирования. Он должен обладать способностью ин­ тегрироваться в любую компиляторную среду, используемую вами в данный момент. Вы должны "обучить" его нюансам любого нового языка програм­ мирования или текстового формата (XML, HTML версии 9 и т. д.).

• Программируемое™. Вы должны располагать возможностью программи­ рования редактора для осуществления сложных многоступенчатых опера­ ций. Это может осуществляться при помощи макросов или встроенного язы­ ка программирования сценариев (к примеру, редактор Emacs использует вариант языка Lisp).

В дополнение к этому многие редакторы поддерживают средства, которые свойст­ венны конкретному языку программирования:

• Выделение синтаксических конструкций

• Автоматическое завершение

• Автоматический отступ

• Библиотека исходных стандартных текстов или документов

• Принудительная справка, выдаваемая системой

• Средства, подобные И С Р (компиляция, отладка и т. д.).

–  –  –

Рис. 3.1. Упорядочение строк в редакторе Такое средство, как выделение синтаксических конструкций, может показаться необязательной фривольностью, но в реальности оно весьма полезно и улучшит вашу производительность. Вы быстро привыкнете к тому, что ключевые слова отобража­ ются разным цветом или шрифтом, а неправильно набранное ключевое слово, кото­ рое отображается по-другому, бежит на вас как зверь на ловца, задолго до того, как вы запустите компилятор.

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

–  –  –

лопаты — примитивный набор текста и использование основных команд ("вырезатьвставить"), активизируемых с помощью мыши, явно недостаточны.

Какие же процедуры могут потребовать от вас большего, чем примитивные дейст­ вия, описанные выше?

Начнем с перемещения курсора. Некоторые клавиатурные команды, перемещаю­ щие вас по словам, строкам, блокам или функциям, являются более эффективными по сравнению с повторным нажатием одной и той ж е клавиши, перемещающей кур­ сор от символа к символу или от строки к строке.

Или предположим, что вы пишете программу на языке Java. Вам нравится поддер­ живать ваши операторы import в алфавитном порядке, но кто-то чужой зарегистриро­ вал несколько файлов, не отвечающих этому стандарту. Вы хотели бы пробежать по нескольким файлам и упорядочить некоторую их часть. Это легко сделать при работе с редакторами типа vi и Emacs (см. рис. 3.1). А попробуйте сделать то ж е самое в ре­ дакторе notepad!

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

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

Он может включать в себя:

• Наименование создаваемого класса или модуля (определенного из имени файла)

• Ваше имя и/или знак авторского права • "Скелеты" конструкций на данном языке (например, описания конструктора или деструктора) Другим полезным средством является автоматический отступ. В нужный момент редактор автоматически делает отступы (например, при вводе открывающей скобки), и не нужно проделывать ту ж е операцию вручную (используя пробел или табуляцию).

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

Куда же направиться?

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

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

Подобным образом разработано ядро Linux. В данном случае имеются разработчики, раз­ бросанные географически, многие из которых работают над одними и теми же фрагмента­ ми текста. Опубликован перечень установочных параметров (в данном случае для редакто­ ра Emacs), содержащий описание требуемого стиля отступов.

Походный набор инструментов

–  –  –

Какой же редактор выбрать?

Советуем освоить приличный редактор, но какой же именно? Уклонимся от ответа на этот вопрос: выбор редактора является личным делом каждого (некоторые даже скажут, что выбор редактора связан с вероисповеданием!). В приложении А приведен список популярных редакторов и мест, откуда их можно загрузить.

Вопросы для обсуждения

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

Разработайте набор макросов (или эквивалентных им средств) для всех опе­ раций, которые вам приходится осуществлять повторно.

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

–  –  –

наши ошибки. Еще лучше, если графическая среда поддерживает многоуровневый от­ кат и повтор так, что можно вернуться назад и восстановить статус-кво, существовав­ ший за несколько минут до этого. Но как быть, если ошибка произошла на прошлой неделе и за прошедшее время компьютер включался и выключался раз десять? Это и является одним из многих преимуществ системы управления исходным текстом про­ грамм: она является своего рода гигантской клавишей U N D O — машиной времени, работающей в масштабах проекта, которая способна вернуть вас к безмятежным дням на прошлой неделе, когда программа реально компилировалась и запускалась.

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

Но система управления исходным текстом (английская аббревиатура S C C S ) дает много больше, чем просто отмену ошибочных действий. Хорошая система позволяет отслеживать изменения и дает ответы на характерные вопросы: "Кто внес изменения в данную строку текста? В чем состоит разница между версией, существующей на данный момент, и версией, существовавшей на прошлой неделе? Сколько строк тек­ ста программы были изменены в данной версии? Какие файлы изменяются чаще все­ го?" Подобная информация бесценна при отслеживании ошибок, аудите, оценке про­ изводительности и качества.

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

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

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

Системы управления могут сохранять поддерживаемые ими файлы в централизо­ ванной БД проекта — лучшем кандидате на архивирование.

В книге используется английская аббревиатура S C C S (заглавные буквы), которая обозна­ чает системы управления исходным текстом вообще. Помимо этого, существует также осо­ бая система управления, обозначаемая sees (строчные буквы), изначально выпущенная фирмой AT&T вместе с Unix System V.

Походный набор инструментов 77 И наконец, некоторые программные продукты позволяют двум и более пользова­ телям работать одновременно с одним и тем же набором файлов и даже вносить изме­ нения в один и тот же файл одновременно. Затем система управляет слиянием этих изменений при возвращении этих файлов в централизованную Б Д проекта. При всей кажущейся рискованности, на практике подобные системы полезны в работе с проек­ тами различного масштаба.

Подсказка 23 Всегда используйте управление исходным текстом программы

Всегда. Даже если ваша команда состоит из одного человека и продолжительность проекта составляет одну неделю. Д а ж е если это есть прототип на выброс. Даже если материал, с которым вы работаете, не является исходным текстом программы. Убеди­ тесь, что все находится под контролем — документация, номера телефонов, записки поставщикам, сборочные файлы, процедуры сборки и выпуска, крохотный сценарий (в оболочке), прожигающий эталонный компакт-диск, словом — все. Обычно мы ис­ пользуем управление исходным текстом в отношении всего того, что мы набираем (включая текст данной книги). И даже если мы не работаем над проектом, каждоднев­ ная работа надежно сохраняется в централизованной БД.

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

Механизм сборки проекта может автоматически извлекать последнюю версию ис­ ходного текста из централизованной БД. Этот механизм может запускаться среди ночи, после того как все сотрудники (будем надеяться на это) уйдут домой. Вы можете автоматически прогонять регрессионные тесты для гарантии того, что исходные тек­ сты, созданные в течение рабочего дня, ничего не нарушили. Автоматизация сборки обеспечивает согласованность — отсутствуют ручные процедуры, и вам не нужно, чтобы разработчики помнили о копировании созданного ими текста в специальную сборочную область.

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

Команда, в которой я работаю, не использует систему управления исходным текстом Как же им не стыдно! Звучит как перспектива провести очередную Реформацию! Од­ нако, пока вы дождетесь, когда они увидят свет во тьме, стоит попробовать внедрить свою, частную систему управления. Воспользуйтесь одним из бесплатных инструмен­ тальных средств, указанных в приложении А, и обратите особое внимание на то, что­ бы результаты вашей личной работы были надежно сохранены в централизованной Глава 3 БД. Хоть это и может показаться двойной работой, мы с уверенностью можем ска­ зать, что эта процедура сбережет ваши нервы (и сэкономит деньги, отпущенные на проект) в тот момент, когда вам впервые придется ответить на вопросы типа "Что ты натворил с модулем xyz?" и "Кто разрушил сборку?" Подобный подход поможет вам убедить руководство в том, что система управления исходным текстом действительно работает.

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

Программы управления исходным текстом В приложении А приведены интернет-ссылки (URL) на типичные системы управле­ ния исходным текстом — некоторые из них являются коммерческим продуктами, дру­ гие же распространяются бесплатно. Имеются и другие программные продукты — обратите внимание на ссылки на часто задаваемые вопросы (FAQ) по управлению конфигурацией.

Другие разделы, относящиеся к данной теме:

• Ортогональность

• Преимущество простого текста

• Все эти сочинения Вопросы для обсуждения

• Д а ж е если у вас нет возможности использовать систему управления исход­ ным текстом на работе, установите RCS или CVS на личный компьютер.

Воспользуйтесь ей для управления вашими домашними проектами, докумен­ тами, которые вы составляете, и (возможно) изменениями в конфигурации самой компьютерной системы.

• Обратите внимание на некоторые из проектов с открытыми исходными текста­ ми, архивы которых доступны в сети Интернет (например, Mozilla [URL 51], KDE [URL 54] и Gimp [URL 55]). Каким образом вы получаете обновления ис­ ходного текста? Как вы вносите изменения — сам проект регулирует доступ, или же разрешает внесение изменений?

–  –  –

Английское слово bug (ошибка) используется для описания "объекта, вызываю­ щего ужас" уже начиная с XIV в. Контр-адмирал д-р Грэйс Хоппер (создатель языка COBOL) оказался первым, кто наблюдал компьютерного "жучка", буквально — Походный набор инструментов моли, попавшей в одно из электромеханических реле, из которых состояли первые вычислительные системы. Когда техника просили объяснить, почему машина ведет себя не так, как надо, он сообщал, что в системе "завелся жучок", и в соответствии со своими должностными обязанностями приклеивал его клейкой лентой вместе с кры­ лышками и всем остальным в рабочий журнал.

К сожалению, мы до сих пор встречаемся с "жучкамг." в системе, хотя и не из рода перепончатокрылых. Но значение этого слова, принятое в XIV в., — привидение — возможно, более применимо сейчас, нежели тогда. Изъяны в программном обеспече­ нии проявляют себя по-разному — от превратно истолкованных требований до оши­ бок в написании исходных текстов. К сожалению, возможности современных компью­ терных систем все еще ограничены исполнением только того, что мы им прикажем, не обязательно тем, что мы хотим, чтобы они сделали.

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

Рассмотрим некоторые аспекты, вовлеченные в процесс отладки, и некоторые уни­ версальные стратегии поиска неуловимых ошибок.

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

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

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

Подсказка 24 Занимайтесь устранением проблемы, а не снятием обвинений На самом деле, не важно, кто виноват в ошибке — вы или кто-то другой. Это все равно остается вашей проблемой.

–  –  –

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

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

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

С чего начать?

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

Необходимо сосредоточиться на более сложных насущных проблемах.

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

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

Однажды один из авторов книги (Энди Хант) работал над большим графическим приложением. Дело уже шло к выпуску готовой версии, когда тестировщики сообщи­ ли о том, что приложение "падало" всякий раз, когда они проводили черту при помо­ щи конкретной кисти. Программист начал оспаривать это утверждение, говоря о том, что все в порядке: он сам пытался выполнять аналогичную прорисовку, и все работа­ ло превосходно. Обмен "любезностями" продолжался в течение нескольких дней, по­ сле чего напряженность резко возросла.

В конце концов все собрались в одной комнате. Тестировщик выбрал нужный ин­ струмент (кисть) и провел черту из ПРАВОГО ВЕРХНЕГО угла к Н И Ж Н Е М У Л Е В О М У углу. Приложение "упало"! Программист тихонько охнул, а затем виновато Походный набор инструментов проблеял, что при тестировании он проводил черту только из Н И Ж Н Е Г О Л Е В О Г О угла к ВЕРХНЕМУ ПРАВОМУ углу, и при этом ошибка никак не выявлялась.

В этой истории есть два момента, заслуживающих внимания:

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

• Искусственные тесты (такие, как одна-единственная черта, проведенная "кистью" снизу вверх) недостаточны для испытания приложения. Необходи­ мо осуществлять тестирование обоих граничных условий и реалистических шаблонов действия конечного пользователя. Это нужно делать систематиче­ ски (см. "Безжалостное тестирование").

Стратегии отладки Если вы уверены, что знаете, в чем дело, пора выяснить, как сама программа относит­ ся к происходящему.

–  –  –

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

Простейшим примером этого является прямолинейный подход типа "переменная = значение", который может быть реализован в виде печатного текста или в виде полей диалогового окна (списка) графического интерфейса.

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

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

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

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

В отладчике DDD имеются некоторые средства визуализации и распространяется бесплатно (см. [URL 19]). Интересно заметить, что отладчик DDD работает со многи­ ми языками, включая Ada, С, С + +, Fortran, Java, Modula, Pascal, Perl и Python (явно ортогональная конструкция).

–  –  –

Трассировка Отладчики обычно сосредоточены на состоянии программ в данный момент. В ряде случаев вам необходимо нечто большее — отследить состояние программы или структуры данных через какое-то время. Если посмотреть на трассировку стека, то можно лишь сделать вывод, как попасть в эту точку напрямую. Это не дает информа­ ции о том, что вы делали до этой последовательности обращений, что особенно важно для систем, основанных на событиях.

Операторы трассировки представляют собой небольшие диагностическими сооб­ щениями, которые выводятся на экран или в файл и говорят о том, что "это здесь" и "х = 2". Это примитивная методика, сравнимая с отладчиками в стиле И С Р, но она особенно эффективна при диагностировании некоторых классов ошибок, с которыми отладчики справиться не могут. Трассировка имеет большое значение в любой систе­ ме, где время само по себе является фактором: в одновременных процессах, системах реального времени и приложениях, основанных на событиях.

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

Походный набор инструментов Трассировочные сообщения должны быть представлены в регулярном, согласо­ ванном формате; возможно, вам захочется провести их синтаксический анализ в авто­ матическом режиме. Например, если вам необходимо отследить утечку ресурсов (не­ сбалансированные операции открытия и закрытия файлов), вы можете трассировать каждый из операторов open и close в файле журнала. Обрабатывая файл журнала с помощью программы на языке Perl, вы легко обнаружите, где встречался операторнарушитель open.

–  –  –

Рассказ о резиновом утенке Очень простая, но весьма полезная методика поиска причины проблемы, состоит в том, чтобы разъяснить ее кому-либо. Ваш собеседник должен заглянуть через ваше плечо на экран монитора, и время от времени утвердительно кивать головой (подобно резиновому утенку, ныряющем и выныривающему в ванне). Ему не нужно говорить ни слова; простое, последовательное объяснение того, что же должна делать ваша программа, часто приводит к тому, что проблема выпрыгивает из монитора и объяв­ ляет во всеуслышанье: "А вот и я!".

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

Почему "резиновый утенок"? Один из авторов книги, Дэйв Хант, учился в лондонском Империал-колледже, и много работал совместно с аспирантом, которого звали Грег Паг и которого Д. Хант считает одним из лучших известных ему разработчиков. На протяжении нескольких месяцев Грег носил при себе крохотного желтого резинового утенка, которого он ставил на край дисплея во время работы. Прошло некоторое время, пока Дэйв отважил­ ся спросить...

84 Глава 3 Процесс исключения В большинстве проектов отлаживаемая вами программа может представлять собой смесь прикладных программ, написанных лично вами и другими сотрудниками вашей проектной команды, а также программные продукты, созданные независимыми про­ изводителями (база данных, обеспечение связи, графические библиотеки, специали­ зированные протоколы связи или алгоритмы и т.д.) и платформенное окружение (опе­ рационная система, системные библиотеки и компиляторы).

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

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

Однажды мы работали над проектом, и старший инженер был уверен, что в систе­ ме Solaris имелось нарушение системного вызова select. Никакие убеждения или ло­ гические построения не могли изменить сложившегося у него мнения (тот факт, что все другие сетевые приложения работали прекрасно, не принимался во внимание).

Неделями он составлял программы обхода этого вызова, которые, по какой-то стран­ ной причине, не способствовали решению проблемы. И когда, в конце концов он был вынужден сесть за стол и прочесть документацию по вызову select, он обнаружил, в чем заключалась проблема, и исправил ее за несколько минут. Теперь мы используем выражение "вызов select нарушен" как деликатное напоминание, когда один из нас начинает обвинять систему в наличии ошибки, которая, скорее всего, является его собственной.

Подсказка 26 Ищите ошибки вне пределов операционной системы Помните: увидев следы копыт, думайте о лошадях, а не о зебрах. Скорее всего, операционная система не нарушена. Да и база данных находится в прекрасном со­ стоянии.

Если вы "внесли всего одно изменение" и система перестала работать, то скорее всего именно оно, прямо или косвенно, несет ответственность за случившееся, каким бы притянутым за уши ни казалось это утверждение. Иногда то, что изменяется, нахо­ дится вне вашего управления: новые версии операционной системы, компилятора, базы данных или программы независимых производителей могут вызывать проблемы и с изначально корректной программой. В ней могут обнаружиться новые ошибки.

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

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

Элемент удивления Если ошибка вызвала у вас удивление (до того, что вы еле слышно бормочете "это­ го не может быть"), стоит провести переоценку истин, дорогих вашему сердцу.

А все ли граничные условия вы протестировали в подпрограмме связанного спи­ ска — той, которую вы считали непробиваемой и которая, по всей вероятности, не могла стать причиной этой ошибки? А другой фрагмент текста программы, кото­ рый вы использовали в течение нескольких лет, — не мог ли он все еще таить в себе ошибку?

Конечно, мог. То удивление, которое вы испытываете, когда что-то идет не так как надо, прямо пропорционально уровню доверия и веры в правильность прогоняе­ мой программы. Поэтому, столкнувшись с "удивительным" отказом в работе про­ граммы, вы должны осознать, что одно или более ваших предположений неверны.

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

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

Кроме того, если ошибка является результатом неправильных данных, которые распространились по нескольким уровням, перед тем как вызвать взрыв, посмотрите, может быть, более усовершенствованная процедура проверки параметров в этих под­ программах смогла бы помешать ее распространению (см. обсуждение процедур дос­ рочного сбоя и утверждений в разделе "Мертвые программы не лгут").

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

86 Глава 3 Если устранение этой ошибки заняло много времени, спросите себя, а почему?

Можете ли вы сделать что-нибудь, чтобы облегчить устранение этой ошибки в сле­ дующий раз? Возможно, вы можете встроить усовершенствованные обработчики прерываний (для тестирования) или написать программу-анализатор файла журнала?

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

Проделайте все это, и наверняка в следующий раз вы будете избавлены от подоб­ ных сюрпризов.

Контрольные вопросы при отладке

• Является ли проблема прямым результатом фундаментальной ошибки или просто ее признаком?

• Ошибка действительно "сидит" в компиляторе? В операционной системе?

Или в вашей собственной программе?

• Если бы вам пришлось подробно объяснить вашему коллеге, в чем состоит проблема, то что бы вы ему сказали?

• Если подозрительная программа проходит модульное тестирование, то явля­ ется ли оно достаточно полным? Что произойдет, если вы прогоняете мо­ дульный тест с реальными данными?

• Существуют ли условия, вызвавшие данную ошибку, где-либо еще в сис­ теме?

Другие разделы, относящиеся к данной теме:

• Программирование на основе утверждений

• Программирование в расчете на совпадение

• Вездесущая автоматизация

• Безжалостное тестирование Вопросы для обсуждения

• Отладка сама по себе является вопросом.

19 Обработка текста Прагматики обрабатывают тексты программ подобно тому, как столяры придают форму деревянным заготовкам. В предыдущих разделах обсуждались некоторые специфические инструментальные средства — оболочки, редакторы, отладчи­ ки — те, что мы используем в работе. Они подобны столярным долотам, ножовкам и рубанкам — инструментам, которые предназначены для выполнения одной или двух конкретных работ. Однако время от времени нам приходится выполнять неко­ торые преобразования, которые не могут быть осуществлены с помощью походноПоходный набор инструментов го инструментария. Нам необходим универсальный инструмент для обработки тек­ ста.

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

Число хороших языков обработки текста постоянно увеличивается. Разработчики программ для Unix часто любят использовать мощь их командных оболочек, усилен­ ных инструментальными средствами типа awk и sed. Тем, кто предпочитает более структурированные средства, по душе объектно-ориентированный характер языка Python [URL 9]. У некоторые выбор падает на Tel [URL 23]. Случается и мы предпо­ читаем язык Perl [URL 8] для написания коротких сценариев.

Эти языки являются важными узаконивающими технологиями. Используя их, вы можете быстро решить все проблемы с утилитами и создать прототипы идей — при работе с обычными языками на это потребовалось бы раз в пять-десять раз больше времени. И этот умножающий коэффициент кардинально важен для экспериментов, которые мы проводим. Потратить 30 минут на воплощение сумасшедшей идеи намно­ го лучше, чем потратить на то ж е пять часов. Потратить один день на автоматизацию важных составляющих проекта — нормально, потратить неделю — может быть, и нет. В книге "The Practice of Programming" [KP99] Керниган и Пайк реализовали одну и ту же программу на пяти различных языках. Самой короткой оказалась версия на языке Perl (17 строк по сравнению со 150 строками на языке С). Работая с языком Perl, вы можете обрабатывать текст, взаимодействовать с другими программами, пе­ редавать данные по сетям, управлять web-страницами, производить арифметические действия с произвольной точностью и писать программы, которые выглядят наподо­ бие клятвы Снупи.

Подсказка 28 Изучите язык обработки текстов Чтобы продемонстрировать широту области применения языков обработки тек­ ста, в качестве примера мы приводим некоторые приложения, разработанные нами на протяжении последних нескольких лет:

• Сопровождение схемы базы данных. Набор сценариев на языке Perl обра­ батывал файл с простым текстом, содержащий определение схемы базы дан­ ных и генерировал из него:

В оригинале router обозначает не маршрутизатор Л В С, а фрезерный станок.

Глава 3 Инструкции SQL для создания Б Д — — Плоские файлы данных для заполнения словаря данных Библиотеки программ на языке С для доступа к Б Д — — Сценарии для проверки целостности Б Д — Web-страницы, содержащие спецификации и диаграммы Б Д — XML версию схемы Доступ к свойству Java. Хорошим тоном в объектно-ориентированном про­ граммировании является ограничение доступа к свойствам объекта, вынуж­ дая внешние классы получать и устанавливать их через методы. Однако в об­ щем случае, когда свойство представлено внутри класса при помощи простого поля, создание метода get и set для каждой переменной представля­ ет собой утомительную механическую процедуру. У нас имеется сценарий Perl, который изменяет исходные файлы и вставляет правильные определе­ ния метода для всех переменных, помеченных соответствующим образом.

Генерирование тестовых данных. Унас имелись десятки тысяч записей, со­ держащих тестовые данные, рассеянных по нескольким различных файлов различного формата, которые нуждались в соединении и преобразовании в некую форму, пригодную для загрузки в реляционную БД. Программа на Perl справилась с этим за пару часов (и в процессе обнаружила пару ошибок изза несовместимости в исходных данных).

Написание книг. Мы придаем важность тому факту, что любая программа, представленная в книге, вначале должна быть протестирована. Большин­ ство программ, приведенных в этой книге, были протестированы. Однако, используя принцип DRY (см. "Пороки дублирования"), мы не хотели копи­ ровать и вставлять строки текста из протестированных программ в книгу.

Это означало бы, что текст дублируется, поэтому велика вероятность, что мы забудем обновить пример, когда соответствующая программа изменит­ ся. В некоторых примерах нам также не хотелось утомлять вас наличием "скелета" программы, необходимым для компиляции и прогона нашего примера. Мы обратились к языку Perl. При форматировании книги вызы­ вался относительно простой сценарий — он извлекал именованный сег­ мент исходного файла, выделял синтаксические конструкции и преобразо­ вывал результат в язык, который мы используем для подготовки типографского макета книг.

Интерфейс между языками С и Object Pascal. У заказчика имеется команда разработчиков, пишущих программы на языке Object Pascal, реализованном на персональных компьютерах. Требуется осуществить сопряжение их про­ граммы с телом программы, написанной на языке С. Был разработан корот­ кий сценарий на языке Perl, который проводил синтаксический анализ фай­ лов заголовков С, выделяя определения всех экспортированных функций и используемых ими структур данных. Затем были сгенерированы модули Object Pascal с записями Pascal для всех структур С и произведен импорт оп­ ределений процедур для всех функций С. Этот процесс генерирования стал Походный набор инструментов частью сборки, так что при любых изменениях заголовка С происходит авто­ матическое конструирование нового модуля Object Pascal.

• Генерирование интернет-документации. Многие команды разработчиков публикуют свою документацию на внутренних интернет-сайтах. Авторами написано много программ на языке Perl, которые анализируют схемы баз данных, исходные файлы на С и С + +, сборочные файлы и другие исходные тексты проекта для производства требуемой HTML-документации. Авторы также использовали язык Perl для верстки документов со стандартными верхними и нижними колонтитулами и передачи их на интернет-сайт.

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

Другие разделы, относящиеся к данной теме: • Пороки дублирования

Упражнения

11. В вашей программе на языке С для представления одного из 100 состояний используется перечислимый тип данных. В целях отладки вам хотелось бы иметь возможность вывода состояния на печать » виде строки (в отличие от числа). Напишите сценарий, который осуществляет считывание со стан­ дартного устройства файла следующего содержания (Ответ см. в Приложе­ нии В):

–  –  –

12. Дописав эту книгу до середины, авторы обнаружили, что не поместили дирек­ тиву use s t r i c t во многие из примеров на языке Perl. Напишите сценарий, который просматривает все файлы типа *. pi в некотором каталоге и добав­ ляет директиву use s t r i c t в конец начального блока комментариев ко всем файлам, в которых это не было сделано ранее. Не забудьте сохранить резерв­ ную копию всех файлов, в которые внесены изменения. (Ответ см. в Прило­ жении В.) 20 Генераторы текстов программ Если столярам приходится снова и снова изготавливать одну и ту же деталь, они идут на хитрость. Они делают для себя шаблон. Если они сделают шаблон один раз, то вре­ мя от времени они могут воссоздавать некоторый фрагмент работы. Шаблон избавля­ ет столяров от излишней сложности и снижает вероятность ошибки, позволяя ремес­ леннику сосредоточиться на качестве работы.

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

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

Подсказка 29 Пишите текст программы, которая пишет текст программы

Существует два основных типа генераторов текста:

1. Пассивные генераторы текста запускаются один раз для достижения результа­ та. Начиная с этого момента результат становится независимым — он отделя­ ется от генератора текста. Мастера, обсуждаемые в разделе "Злые волшебни­ ки", вместе с некоторыми средствами CASE являются примерами пассивных генераторов текста.

2. Активные генераторы текста используются всякий раз, когда возникает не­ обходимость в результатах их работы. Этот результат создается по принци­ пу "выбросить и забыть" — он всегда может быть воспроизведен с помо­ щью генератора текста. Зачастую активные генераторы считывают некоторую форму сценария или управляющего файла для получения конеч­ ного результата.

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

Пассивные генераторы текста применяются во многих случаях:

• Создание новых исходных файлов. Пассивный генератор текста может создавать шаблоны, директивы управления исходным текстом, сведения об авторских правах и стандартные блоки комментариев для каждого нового файла в некотором проекте. Мы настроили наши редакторы на выполнение этого действия всякий раз при создании нового файла: при редактировании новой программы на языке Java в новом буфере редактора автоматически окажутся уже заполненные блок комментариев, директива пакета и описа­ ние структурного класса.

• Осуществление двоичных преобразований в языках программирова­ ния. Мы начали писать эту книгу, используя систему troff, но после пятна­ дцатого раздела перешли на LATgX. Мы написали генератор текста, который считывал исходный текст из troff, и преобразовывали его в формат LAT^X.

Точность составила 9 0 % ; остальное мы делали вручную. Это является инте­ ресной особенностью пассивных генераторов текста: они не должны отли­ чаться абсолютной точностью. Вы выбираете, какое усилие необходимо вло­ жить в генератор, в сравнении с энергией, которую вы тратите на устранение ошибок в выходной информации.

• Создание таблиц поиска и других ресурсов, вычисление которых явля­ ется дорогой операцией. Вместо того, чтобы вычислять тригонометриче­ ские функции, во многих старых графических системах использовались таб­ лицы синусов и косинусов. Обычно эти таблицы создавались пассивным генератором текста и затем копировались в исходный текст программы.

Активные генераторы текста Пассивные генераторы текста являются не более чем удобством, но их активные род­ ственники являются необходимостью, если вы хотите следовать принципу DRY. С по­ мощью активного генератора текста вы можете использовать представление некото­ рого фрагмента знания и преобразовать его во все формы, которые необходимы вашему приложению. Это не является дублированием, поскольку эти формы являют­ ся расходным материалом и создаются по мере необходимости генератором текста (отсюда термин "активный").

Когда бы вам ни приходилось организовывать совместную работу двух совершенно разных сред, стоит подумать об использовании активных генераторов текста.

Допустим, вы разрабатываете приложение БД. В этом случае вы имеете дело с двумя средами — базой данных и языком программирования, который используется для доступа к БД. У вас имеется схема, и вам необходимо определить низкоуровневые конструкции, отражающие компоновку определенных таблиц БД. Вы могли бы про­ сто запрограммировать их напрямую, но при этом нарушается принцип DRY: знание схемы было бы выражено дважды. Если схема меняется, вам необходимо помнить и о 92 Глава 3 соответствующем изменении текста программы. Если из таблицы удаляется столбец, а база текста программы не меняется, то может статься, что ошибка не проявится даже при компиляции. Первый раз вы узнаете об этом во время тестирования, когда начнутся сбои (или же от пользователя).

–  –  –

Альтернативой этому является использование активного генератора текста — берется схема и используется для генерации исходного текста конструкций, как по­ казано на рис. 3.3. Теперь при любом изменении схемы будет происходить и автома­ тическое изменение программы, используемой для доступа к ней. При удалении столбца исчезает и соответствующее поле в конструкции, и любая высокоуровневая программа, использующая этот столбец, не пройдет компиляцию. Ошибку удалось заметить во время компиляции, а не в процессе сборки. Конечно, эта схема работает только в том случае, если вы сделаете генерацию текста частью самого процесса сборки.

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

Хотя зачастую легче выразить ее более простым, независимым от языка представ­ лением и сгенерировать программу для обоих языков, как показано на рис. 3.4. Так­ же можно посмотреть ответ к упражнению 13 (см. Приложение В) в качестве при­ мера того, как отделить синтаксический анализ представления плоского файла от генерации текста.

Как насчет создания текста из схемы БД? Существует несколько способов. Если схема содержится в плоском файле (например, операторы create statements), то синтаксиче­ ский анализ и генерацию исходного текста можно провести при помощи относительно не­ сложного сценария. В качестве альтернативного способа предлагается следующий: при использовании инструментального средства для создания схемы непосредственно в са­ мой БД необходимо иметь возможность извлечения нужной информации непосредствен­ но из словаря БД. В языке Perl имеются библиотеки, обеспечивающие доступ к большин­ ству основных БД.

Походный набор инструментов Генераторы текста не должны быть слишком сложными Весь этот разговор об активном "этом" и пассивном "том" может создать у вас впе­ чатление, что генераторы текста — сложные звери. Им не надо быть сложными.

Обычно самой сложной частью является синтаксический анализатор, который обра­ батывает входной файл. Не усложняйте входной формат, и генератор текста станет простым. Обратите внимание на ответ к упражнению 13 (см. Приложение В): в ре­ альности генерация текста представляет собой в основном операторы print.

–  –  –

Генераторы текста не всегда генерируют тексты программ Хотя многие из примеров, приведенных в этом разделе, демонстрируют генерато­ ры текстов программ, которые производят исходные тексты программ, это не всегда так. Вы может применять генераторы текстов для создания выходного файла в любом формате: HTML, XML, простой текст — любого текста, который является входной информацией в какой-либо части вашего проекта.

–  –  –

Упражнения

13. Напишите генератор текста, который обрабатывает входной файл, изобра­ женный на рис. 3.4, и генерирует выходной файл на двух языках по вашему выбору. Попытайтесь упростить добавление новых языков. (Ответ см. в При­ ложении В) Глава 4 Прагматическая паранойя Подсказка 30 Невозможно написать совершенную программу Ваши чувства задеты? Не стоит принимать эту подсказку близко к сердцу. Примите ее как жизненную аксиому. Заключите ее в объятья. Восславьте ее. Поскольку совер­ шенных программ в природе не существует. За всю краткую историю информатики никому не удалось написать ни одного совершенного фрагмента программы. Малове­ роятно, что вы станете первым. И когда вы примете это как существующий факт, то перестанете тратить время и энергию впустую в погоне за призрачной мечтой.

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

Каждый из этих людей знает, что лично он — лучший водитель на планете Земля.

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

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

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

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

Глава 4 Но прагматики идут дальше. Они не доверяют даже самим себе. Зная, что никто не пишет совершенных программ, включая их самих, прагматики пишут программы, за­ щищаясь от собственных ошибок. Первый рубеж обороны описан в разделе "Проек­ тирование по контракту": клиенты и поставщики должны договориться о правах и обязанностях.

В разделе "Мертвые программы не лгут" говорится о гарантиях того, что процеду­ ра устранения ошибок не нанесет вреда. Поэтому мы попытаемся чаще проверять нашу программу и завершать ее аварийно, если она работает не так, как надо.

В разделе "Программирование с применением утверждений" описан простой метод проверки "на ходу" — программа, которая активно проверяет ваши предположения.

Исключения, как и любая другая методика, может причинить больше вреда, чем пользы, если ее применять неправильно. Мы обсудим эти аспекты в разделе "Случаи, когда необходимо использовать исключения".

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

В разделе "Балансировка ресурсов" предлагаются способы того, как не ронять те предметы, которыми вы жонглируете.

Поэтому будем осторожными в этом мире несовершенных систем, устаревших вре­ менных масштабов, смешных инструментальных средств и невыполнимых требований.

Если все общество отклоняется от нормы, чтобы понять вас, скорее всего, это паранойя.

Вуди Аллен

–  –  –

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

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

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

Допустим, у вас имеется контракт на работу, в котором определены количество от­ рабатываемых часов и правила поведения, которым вы обязаны следовать. В ответ фирма платит вам заработную плату и предоставляет другие льготы. Каждая из сто­ рон выполняет свои обязательства, а в результате выигрывают все.

Прагматическая паранойя 97 Эта идея используется во всем мире — и формальным, и неформальным обра­ зом — для того, чтобы помочь людям во взаимодействии. Можем ли мы применить этот же принцип, чтобы способствовать взаимодействию программных модулей? От­ вет на этот вопрос положительный.

Проектирование по контракту Бертран Мейер [Меу97Ь] разработал концепцию проектирования по контракту для языка Eiffel. Это простая, но мощная методика, сосредоточенная на документирова­ нии (и согласовании) прав и обязанностей программных модулей в целях обеспечения корректности программы. Так что же означает "корректная программа"? Это та про­ грамма, которая делает не более и не менее того, на что она претендует. Документи­ рование и подтверждение указанных претензий лежит в основе принципа проектиро­ вания по контракту (в дальнейшем, для краткости, будем называть его ППК).

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

Мейер описывает эти виды и претензии следующим образом:

• Предусловия. Требования подпрограммы — то, что обязано быть истинным для того, чтобы подпрограмма могла вызываться. Если предусловия наруше­ ны, программа не должна вызываться ни в коем случае. Ответственность за передачу качественных данных лежит на вызывающей программе (см. врез­ ку ниже "Кто несет ответственность?").

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

• Инварианты класса. Класс гарантирует, что данное условие всегда истинно с точки зрения вызывающей программы. Во время внутренней обработки подпрограммы инвариант может и не выполняться, но к моменту выхода из подпрограммы и передачи управления обратно к вызывающей программе, инвариант обязан быть истинным. (Следует заметить, что класс не может да­ вать неограниченное право доступа для записи к любому элементу данных, участвующему в инварианте.) Рассмотрим контракт на создание программы, которая осуществляет вставку зна­ чения данных в упорядоченный список уникальных данных. При работе с iContract (препроцессором для языка Java, который можно загрузить с [URL 17]), этот кон­ тракт может быть реализован следующим образом:

–  –  –

Следовательно, в контракте между подпрограммой и любой потенциально вызы­ вающей ее программой может быть записано следующее:

"Если вызывающая программа выполняет все предусловия подпрограм­ мы, то подпрограмма гарантирует, что по завершении ее работы все постусловия и инварианты будут истинными".

Прагматическая паранойя Если одна из сторон нарушает условия контракта, то применяется предварительно согласованная мера — например, возбуждается исключение или происходит завер­ шение работы программы. Что бы ни происходило, вы не ошибетесь, утверждая, что нарушение условий контракта есть ошибка. Это происходит далеко не всегда, и по­ этому предусловия не должны использоваться для осуществления таких процедур, как проверка правильности данных, вводимых пользователем.

Подсказка 31 Проектируйте в соответствии с контрактами

В разделе "Ортогональность" рекомендуется создавать "скромные" программы.

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

Наследование и полиморфизм являются краеугольными камнями объектно-ориен­ тированных языков программирования и представляют собой область, в которой прин­ цип программирования по контракту может проявиться особенно ярко. Предположим, что вы используете наследование при создании связи типа "это-схоже-с тем", где один класс "схож-с-тем" (другим) классом.

Вероятно, вы действуете в соответствии с прин­ ципом замещения, изложенным в книге [Lis88]:

"Использование подклассов должно осуществляться через интерфейс базового класса, но при этом пользователь не обязан знать, в чем со­ стоит различие между ними".

Другими словами, вы хотите убедиться в том, что вновь созданный подтип действи­ тельно "схож-с-тем" (базовым) типом — что он поддерживает те же самые методы и эти методы имеют тот же смысл. Этого можно добиться при помощи контрактов. Кон­ тракт необходимо определить единожды (в базовом классе) с тем, чтобы он применялся к вновь создаваемым подклассам автоматически. Подкласс может (необязательно) ис­ пользовать более широкий диапазон входных значений или же предоставлять более же­ сткие гарантии. Но, по крайней мере, подкласс должен использовать тот же интервал и предоставлять те же гарантии, что и родительский класс.

Рассмотрим базовый класс Java, именуемый Java. AWT. Component. Вы можете об­ рабатывать любой визуальный элемент в AWT или Swing как тип Component и не знать того, чем является подкласс в действительности — кнопкой, подложкой, меню или чем-то другим. Каждый отдельный элемент может предоставлять дополнительные, специфические функциональные возможности, но, по крайней мере, он должен пре­ доставлять базовые средства, определенные типом Component. Но ничто не может по­ мешать вам создать для типа Component подтип, который предоставляет методы с пра­ вильными названиями, приводящие к неправильным результатам. Вы легко может создать метод paint, который ничего не закрашивает, или же метод setFont, который не устанавливает шрифт. AWT не обладает контрактами, которые способны обнару­ жить факт нарушения вами соглашения.

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

Например, вы составляете контракт для метода setFont (подобный указанному ниже), который гарантирует, что вы получите именно тот шрифт, который установили:

I ** * @pre f ! == null * ©post getFont() == f */ public void s e t F o n t ( f i n a l Font f) { / /...

Реализация принципа ППК Самая большая польза от использования принципа П П К состоит в том, что он ста­ вит вопросы требований и гарантий во главу угла. В период работы над проектом простое перечисление факторов — каков диапазон входных значений, каковы гра­ ничные условия, что можно ожидать от работы подпрограммы (или, что важнее, чего от нее ожидать нельзя), — является громадным шагом вперед в написании лучших программ. Не обозначив эти позиции, вы скатываетесь к программирова­ нию в расчете на совпадение (см. раздел "Программирование в расчете на сте­ чение обстоятельств"), на чем многие проекты начинаются, заканчиваются и тер­ пят крах.

В языках программирования, которые не поддерживают в программах принцип П П К, на этом можно было бы и остановиться — и это неплохо. В конце концов, принцип П П К относится к методикам проектирования. Д а ж е без автоматической проверки вы можете помещать контракт в текст программы (как комментарий) и все равно получить от этого реальную выгоду. По меньшей мере, закомментиро­ ванные контракты дают вам отправную точку для поиска в случае возникновения неприятностей.

Утверждения Документирование этих предположений уже само по себе неплохо, но вы можете из­ влечь из этого еще большую пользу, если заставите компилятор проверять имеющий­ ся контракт. Отчасти вы можете эмулировать эту проверку на некоторых языках про­ граммирования, применяя так называемые утверждения (см. "Программирование утверждений"). Но почему лишь отчасти? Разве вы не можете использовать утвер­ ждения для всего того, на что способен принцип ППК?

К сожалению, ответ на этот вопрос отрицательный. Для начала, не существует средств, поддерживающих распространение действия утверждений вниз по иерархии наследования. Это означает, что если вы отменяете метод базового класса, у которого имеется свой контракт, то утверждения, реализующие этот контракт, не будут вызы­ ваться корректно (если только вы не продублируете их вручную во вновь написанной программе). Не забывайте, что, прежде чем выйти из любого метода, необходимо Прагматическая паранойя 101 вручную вызвать инвариант класса (и все инварианты базового класса). Основная проблема состоит в том, что контракт не соблюдается автоматически.

Кроме того, отсутствует встроенный механизм "старых" значений; то есть значе­ ний, которые существовали на момент входа в метод. При использовании утвержде­ ний, обеспечивающих соблюдение условий контрактов, к предусловию необходимо добавить программу, позволяющую сохранить любую информацию, которую вы на­ мерены использовать в постусловии. Сравним это с iContract, где постусловие может просто ссылаться на "variable@pre", или с языком Eiffel, который поддерживает принцип "old expression".

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

Поддержка ППК в языках программирования Языки программирования, в которых имеется встроенная поддержка ППК (напри­ мер, Eiffel и Sather[URL 12]) осуществляют автоматическую проверку предусловий и постусловий в компиляторе и исполняющей системе. В этом случае вы оказываетесь в самом выгодном положении, поскольку все базовые элементы программы (включая библиотеки) должны выполнять условия соответствующих контрактов.

Но как быть, если вы работаете с более популярными языками типа С, С + + и Java? Для этих языков существуют препроцессоры, которые обрабатывают контрак­ ты, инкапсулированные в первоначальный исходный текст как особые комментарии.

Препроцессор разворачивает эти комментарии, преобразуя их в программу, которая контролирует утверждения.

Если вы работаете с языками С и С + +, то попробуйте изучить Nana [URL 18].

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

Для языка Java существует средство iContract [URL 17]. Оно обрабатывает ком­ ментарии (в формате JavaDoc) и генерирует новый исходный файл, содержащий логи­ ку утверждений.

Препроцессоры уступают встроенным средствам. Они довольно муторно интегри­ руются в проект, а другие используемые вами библиотеки останутся без контрактов.

И тем не менее, они могут принести большую пользу; когда проблема обнаруживает­ ся подобным способом, — в особенности та, которую по-другому найти просто невоз­ можно, — и это уже сродни работе волшебника.

–  –  –

числяющий квадратные корни (подобный классу DOUBLE в языке Eiffel). Этот метод требует наличия предусловия, которое ограничивает область действия положитель­ ными числами. Предусловие в языке Eiffel объявляется с помощью ключевого слова require, а постусловие объявляется с помощью ключевого слова ensure, так что мож­ но записать:

Sqrt: DOUBLE is

-- Подпрограмма вычисления квадратного корня require

–  –  –

ЕСЛИ ваш алгоритм извлечения квадратного корня не работает (или выходит за пределы погрешности), вы получите сообщение об ошибке и трассировку стека, ука­ зывающую на цепочку вызовов.

Если вы передаете sqrt отрицательный параметр, рабочая среда Eiffel выводит на печать ошибку "sqrt _arg_must_be_positive" (аргумент функции sqrt должен быть положительным) наряду с трассировкой стека. Этот вариант реализован лучше, чем его аналогия в рабочих средах типа Java, С, и С + +, где при передаче отрицательного числа в sqrt выдается специальное значение NaN (Not a Number — не число). Далее по ходу программы, когда вы попытаетесь произвести со значением NaN некие мате­ матические действия, результаты этого будут поистине удивительными.

Проблему намного проще найти и диагностировать "не сходя с места", при ава­ рийном завершении работы программы.

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

Инварианты цикла Понимание граничных условий для нетривиального цикла может оказаться проблема­ тичным. Циклы испытывают воздействие "проблемы банана" (я знаю, как записать по буквам слово "банан", но не знаю, в какой момент нужно остановиться), ошибки "постов охраны" (путаница в том, что подсчитывать: сами посты, или интервалы меж­ ду ними) и вездесущая ошибка завышения (занижения) [URL 52].

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

int m = arr[0]; // пример предполагает, что длина массива 0 i n t i = 1;

// Инвариант цикла: m = max(arr[0:i-1]) while ( i arr.length) { m = Math.max(m, a r r [ i ] ) ;

i = i + 1;

}

–  –  –

мя выполнения цикла. Таким образом, нам известно, что инвариант истинен после выполнения цикла, и, следовательно, наш результат является достоверным. Инвари­ анты цикла могут быть запрограммированы в явном виде (как утверждения); они так­ же полезны при проектировании и документировании.

Семантические инварианты Вы можете использовать семантические инварианты для выражения неизменных тре­ бований при составлении своего рода "философского контракта".

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

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

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

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

Например, в случае с дебетовой картой можно было бы записать:

–  –  –

Это и есть четкая, сжатая, однозначная формулировка, которая применима к раз­ личным областям системы. Это наш контракт со всеми пользователями системы, наша гарантия ее поведения.

Динамические контракты и агенты До сих пор мы говорили о контрактах как о неких фиксированных, раз и навсегда уста­ новленных спецификациях. Но случае с автономными агентами этого быть не должно.

Из определения "автономных" агентов следует, что они могут отвергать запросы, ко­ торые они не хотят выполнять. Они могут обговаривать условия контракта — "я не могу предоставить то-то и то-то, но если вы дадите мне вот это, тогда я смогу предос­ тавить что-то другое".

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

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

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

–  –  –

22 Мертвые программы не лгут Приходилось ли вам замечать, что иногда, еще до того, как вы осознаете проблему, ее признаки обнаруживают посторонние люди? То же самое применимо и к про­ граммам других разработчиков. Если в одной из наших программ что-то начинает идти не так, как надо, в ряде случаев первой это "заметит" библиотечная подпро­ грамма. Возможно, паразитный указатель заставил нас записать в дескриптор фай­ ла какие-то бессмысленные символы. При следующем обращении к read это будет обнаружено. Возможно, что переполнение буфера привело к уничтожению счетчи­ ка, который мы собирались использовать для определения объема назначаемой па­ мяти. Возможно, причиной сбоя окажется raalloc. Логическая ошибка в одном из нескольких миллионов операторов, находящихся в тексте перед оператором выбора, означает, что его селектор больше не примет значение 1, 2, или 3. Мы берем случай d e f a u l t (который является одной из причин того, что почему любой оператор выбо­ ра должен иметь значение по умолчанию — мы хотим знать, в какой момент про­ изошло невозможное).

Легко стать приверженцем ментальности "этого не может быть, потому что не мо­ жет быть никогда". Большинство из нас создавало программы, которые не проверя­ ют, успешно ли завершилась операция закрытия файла и правильно ли записан опе­ ратор трассировки. И все сводилось к одному (к тому, что мы и так знали) — рассматриваемая программа не откажет, если будет работать в нормальных условиях.

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

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

Подсказка 32 Пусть аварийное завершение работы программы произойдет как можно раньше

–  –  –

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

Эта философия воплощена в языке и библиотеках Java. Когда в системе выпол­ нения происходит что-то непредвиденное, происходит возбуждение исключения RuntimeException. Если это исключение не перехвачено, оно будет двигаться на верхний уровень программы и заставит ее прекратить работу, отобразив трасси­ ровку стека.

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

В языке С для этого весьма полезны макрокоманды:

#define СНЕСК(LINE, EXPECTED) \ int гс = LINE; \ if (гс != EXPECTED) \ ut_abort( FILE__, LINE #LINE, гс, EXPECTED); } void ut_abort(char *file, i n t In, char *line, i n t rc, int exp) { fprintf(stderr, "%s line %d\n'%s': expected %d, got %d\n", file, In, line, exp, rc);

exit(1);

} Тогда вы можете инкапсулировать вызовы, которые никогда подведут, с помощью строки:

CHECK(statC'/tmp", &stat_buff), 0);

Если бы это не удалось, то вы получаете сообщение, записанное в stderr:

source.с line 19 'stat("/tmp", &stat_buff)': expected 0, got -1 Ясно, что в ряде случаев выход из выполняющейся программы просто не уме­ стен. Возможно, вы претендуете на ресурсы, которые не освобождены, или же вам необходимо записать сообщения в журнал, завершить открытые транзакции или взаимодействовать с другими процессами. Здесь будут полезны методики, которые обсуждаются в разделе "Случаи, когда необходимо использовать исключения". Од­ нако основной принцип остается тем же — если ваша программа обнаруживает, что произошло событие, которое считалось невозможным, программа теряет жизнеспо­ собность. Начиная с этого момента, все действия, которые она совершает, попадают под подозрение, так что выполнение программы необходимо прервать как можно быстрее. В большинстве случаев мертвая программа приносит намного меньше вре­ да, чем испорченная.

–  –  –

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

Она звучит так:

"Это никогда не может случиться..."

И далее: "Через 30 лет эта программа использоваться не будет, так что для обо­ значения года хватит и двух разрядов". "Нужна ли интернационализация, если это приложение не будет использоваться за рубежом?" "Счетчик не может принимать от­ рицательное значение". "Этот оператор printf не дает сбоев".

Не стоит заниматься подобного рода самообманом, особенно при написании про­ грамм.

Подсказка 33 Если что-либо не может произойти, воспользуйтесь утверждениями, которые гарантируют, что это не произойдет вовсе Всякий раз, когда вы начинаете думать: "Ну конечно, такого просто не может про­ изойти", проверяйте это высказывание с помощью программы. Самый простой спо­ соб осуществить это — использовать утверждения. В большинстве реализаций язы­ ков С и С + + имеется некоторая разновидность макроса a s s e r t или _ a s s e r t, который осуществляет проверку логического условия. Эти макрокоманды могут представлять огромную ценность.

Если указатель, передаваемый к вашей процедуре, ни в коем слу­ чае не должен принимать значение NULL, то проверьте выполнение этого условия:

void w r i t e S t r i n g ( c h a r * s t r i n g ) { a s s e r t ( s t r i n g ! = NULL);

–  –  –

Конечно, условие, переданное утверждению, не должно оказывать побочного воз­ действия (см. врезку "Утверждения и побочные эффекты"). Необходимо также пом­ нить, что утверждения могут отключаться во время компиляции — не помещайте в макрос a s s e r t программу, которая должна быть выполнена. Утверждения не должны использоваться вместо реальной обработки ошибок.

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

p r i n t f ( " E n t e r 'Y' or ' N ' : " ) ;

ch = g e t c h a r ( ) a s s e r t ( ( c h == "Y") | | (ch == ' N ' ) ) ; /* дурной тон! */ И поскольку имеющаяся макрокоманда a s s e r t вызывает e x i t, если утверждение ложно, нет никаких оснований для того, чтобы этого не могли сделать создаваемые вами версии программы.. Если вам приходится освобождать ресурсы, сделайте так, чтобы невыполнение утверждения возбуждало исключение или осуществляло пере­ ход long jump к точке выхода, или же вызывало обработчик ошибки. Убедитесь в том, что программа, исполняемая в течение миллисекунд, не использует информацию, ко­ торая привела к невыполнению утверждений.

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

Оно формулируется примерное так:

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

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

В действительности маловероятно, что процедура тестирования любой сложной про­ граммы всегда будет происходить по единому сценарию, даже при минимальном про­ центе перестановок в тексте программы (см. "Безжалостное тестирование"). Вто­ рое — эти оптимисты забывают, что ваша программа выполняется в опасном мире.

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

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

Сумма страховки велика, но получить ее в случае падения довольно сложно.

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

–  –  –

В языках программирования, имеющих своей основой язык С, можно использовать пре­ процессор или конструкцию с условными операторами с тем, чтобы указанные утвержде­ ния не носили обязательный характер. Во многих разработках происходит отключение ге­ нерации текста программы для макроса assert при установленном (или сброшенном) флажке этапа компиляции. Можно также поместить текст программы в пределах условно­ го оператора i f с постоянным условием, которое многие компиляторы (включая наиболее распространенные Java-системы) отбросят в ходе оптимизации.

112 Глава 4

–  –  –

24 Случаи, в которых используются исключения В разделе "Мертвые программы не лгут" высказано предложение считать хорошим тоном проверку всех возможных ошибок, в особенности тех, которые возникают не­ ожиданно. Однако на практике это может привести к тому, что программа станет уродливой; нормальная логика вашей программы может сойти на нет из-за ее затума­ нивания процедурами обработки ошибок, особенно если вы являетесь приверженцем школы программирования, девиз которой звучит так: "В подпрограмме должен иметься один-единственный оператор return" (авторы не являются приверженцами указанной школы). Нам приходилось видеть текст программы, выглядевший следую­ щим образом:

retcode = OK;

if ( s o c k e t. r e a d ( n a m e ) != OK) { retcode = BAD_READ;

} else processName(name);

i f (socket.read(address) != OK) { retcode = BAD_READ;

} else { processAddress(address);

i f (socket.read(telNo) ! = 0 K ) { retcode= BAD_READ Прагматическая паранойя

–  –  –

Теперь схема управления отличается ясностью — вся обработка ошибок сосредо­ точена в одном-единственном месте.

Что является исключительным?

Одна из проблем, связанных с исключениями, заключается в том, что необходимо знать, когда их можно использовать. Мы полагаем, что не стоит злоупотреблять ис­ ключениями для нормального хода выполнения программы; они должны быть заре­ зервированы для нештатных ситуаций. Предположим, что неперехваченное исключе­ ние прекратит работу вашей программы и спросите себя: "А будет ли эта программа исполняться, если удалить все обработчики исключений?" Если ответ отрицатель­ ный, то вполне возможно, что исключения используются в обстоятельствах, их не требующих.

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

Мы отвечаем следующим образом: "Это зависит от конкретного случая". Если файл должен был там находиться, то исключение гарантировано. Произошло что-то непредвиденное — файл, который вы считали существующим, похоже, исчез. С дру­ гой стороны, если вы понятия не имеете, должен ли этот файл существовать или нет, то его отсутствие уже не кажется столь исключительным и возвращение ошибки вполне уместно.

Рассмотрим пример с первой ситуацией. Представленная ниже программа откры­ вает файл /etc/passwd, который обязан существовать во всех системах Unix. Если Глава 4

–  –  –

Подсказка 34 Пользуйтесь исключениями только в исключительных случаях Почему мы предлагаем подобный подход к исключениям? Конечно, исключение представляет собой мгновенную нелокальную передачу управления — своего рода многоуровневый оператор goto. Программы, использующие исключения в своей обычной работе, испытывают те же проблемы с удобочитаемостью и сопровожде­ нием, которые свойственны классическим неструктурированным программам. Эти программы нарушают инкапсуляцию: подпрограммы и их вызывающие программы отличаются более сильной связанностью за счет обработки исключений.

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

Возникают ситуации, когда вам захочется применить обработчики ошибок вместо исключений или же одновременно с ними. Понятно, что, если вы работаете с язы­ ком С, не поддерживающим исключения, это один из нескольких возможных вариан­ тов (см. Вопросы для обсуждения ниже). Но иногда обработчики ошибки могут ис­ пользоваться даже в языках (типа Java), обладающих хорошей встроенной схемой обработки исключений.

Рассмотрим реализацию приложения "клиент—сервер" с использованием средства RMI (удаленный вызов метода) в языке Java. Поскольку RMI реализован определен­ ным способом, то каждое обращение к удаленной подпрограмме должно быть подго­ товлено, с тем чтобы обработать ситуацию RemoteException. Добавление программы обработки этих исключений может представлять собой утомительную процедуру и оз­ начает сложность написания программы, которая могла бы работать как с локальными, так и с удаленными подпрограммами. Обойти эту трудность возможно путем инкапсу­ лирования удаленных объектов в класс, не являющийся удаленным. Тогда этот класс сможет реализовать интерфейс обработчика ошибок, позволяя программе клиента ре­ гистрировать подпрограмму, обращение к которой происходит при обнаружении уда­ ленной исключительной ситуации.

Другие разделы, относящиеся к данной теме: • Мертвые программы не лгут

Вопросы для обсуждения

• В языках программирования, не поддерживающих исключительные ситуа­ ции, часто используется иной (нелокальный) способ передачи механизма управления (например, в языке С существует средство longjmp/setjmp).

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

–  –  –

При написании программ всем нам приходится управлять ресурсами: памятью, транзакциями, потоками, файлами, таймерами — словом, всеми типами объектов, доступность которых ограничена. Большую часть времени использование ресурса следует предсказуемой схеме: ресурс назначается, используется, а затем освобож­ дается.

Однако многие разработчики не имеют сколько-нибудь завершенного плана, ка­ сающегося распределения и освобождения ресурсов.

Поэтому предлагается простая подсказка:

–  –  –

Доводите до конца то, что начинаете В большинстве случаев эту подсказку применить несложно. Она всего лишь озна­ чает, что подпрограмма или объект, который назначает ресурс, должен нести ответст­ венность за освобождение этого ресурса. Использование указанной подсказки можно рассмотреть на примере неудачной программы — приложения, открывающего файл, считывающего из него информацию о заказчике, обновляющего поле и записываю­ щего результат обратно в файл. Чтобы сделать пример более наглядным, мы опусти­ ли процедуру обработки ошибок.

–  –  –

На первый взгляд, подпрограмма updateCustomer выглядит довольно прилично.

Похоже, что она реализует нужную нам логику — считывает запись, обновляет баПрагматическая паранойя ланс и осуществляет запись обратно в файл. Однако за внешним приличием и скры­ вается главная проблема. Подпрограммы readCustomer и writeCustomer тесно связа­ ны между собой — они совместно используют глобальную переменную c F i l e.

Подпрограмма readCustomer открывает файл и сохраняет указатель файла в перемен­ ной c F i l e, a noflnporpaMMf'wrTteCustomer использует сохраненный указатель, для за­ крытия файла по окончании работы. Эта глобальная переменная даже не появляется в подпрограмме updateCustomer.

Почему эту программу можно считать неудачной? Представим несчастную дамупрограммистку из службы сопровождения, которой сказали, что спецификация изме­ нилась — баланс должен обновляться только в том случае, если его новое значение неотрицательное.

Дама открывает файл с исходным текстом и изменяет подпрограм­ му updateCustomer:

void updateCustomer(const char *fName, double newBalance) { Customer cRec;

readCustomer(fName, ScRec);

i f (newBalance = 0. 0 ) { cRec.balance = newBalance;

writeCustomer(&cRec);

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

У этой проблемы есть весьма неудачное решение — использовать специальный оператор выбора в подпрограмме updateCustomer:

void updateCustomer(const char *fName, double newBalance) { Customer cRec;

readCustomer(fName, &cRec);

i f (newBalance = 0. 0 ) { cRec.balance = newBalance;

writeCustomer(ScRec);

} else fclose(cFile);

} Это устраняет проблему — теперь файл закроется независимо от нового значения баланса, но эта процедура означает, что теперь связанными оказываются три под­ программы (через глобальную переменную c F i l e ). Мы попадаем в ловушку, и если мы продолжаем действовать в том же духе, то все полетит под откос.

–  –  –

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

Вложенное распределение ресурса Базовая схема распределения ресурсов может быть распространена на подпрограм­ мы, которым одновременно необходимо более одного ресурса.

Поэтому есть еще два предложения:

1. Освобождайте ресурсы в последовательности, обратной той, в которой проис­ ходило их распределение. При этом можно избежать появления "осиротев­ ших" ресурсов, если один из них содержит ссылки на другой.

2. При распределении одного и того же набора ресурсов в различных местах про­ граммы необходимо осуществлять эту операцию в одном и том же порядке. Это уменьшает вероятность взаимоблокировки. (Если процесс А требует resourcel и собирается затребовать resource2, тогда как процесс В затребо­ вал resource2 и пытается заполучить resourcel, то два процесса окажутся в состоянии вечного ожидания.) Неважно, какой тип ресурсов используется — транзакции, память, файлы, пото­ ки, окна, к ним применима общая схема: кто бы ни назначал ресурс, он обязан нести Прагматическая паранойя ответственность за его освобождение. Однако эта концепция может получить даль­ нейшее развитие при работе с рядом языком программирования.



Pages:     | 1 || 3 | 4 |   ...   | 5 |
Похожие работы:

«Министерство образования Республики Беларусь учреждение образования «Белорусский государственный университет информатики и радиоэлектроники» ТЕЛЕКОММУНИКАЦИОННЫЕ СИСТЕМЫ И СЕТИ МАТЕРИАЛЫ 51-Й НАУЧНОЙ КОНФЕРЕНЦИИ АСПИРАНТОВ, МАГИСТРАНТОВ И...»

«I. ИНФОРМАТИКА УДК 519.68: 681.513.7 КАК ОЦЕНИТЬ НАДЕЖНОСТЬ АЛГОРИТМА КЛАССИФИКАЦИИ. II. ИНТЕРВАЛЬНЫЕ ОЦЕНКИ С.И. Гуров факультет ВМиК МГУ им. Ломоносова, г.Москва, Россия e-mail: sgur@cs.msu.su, gurov@c...»

«ПРИКЛАДНАЯ ДИСКРЕТНАЯ МАТЕМАТИКА 2008 Математические основы компьютерной безопасности № 1(1) УДК 681.322 РЕАЛИЗАЦИЯ ПОЛИТИК БЕЗОПАСНОСТИ В КОМПЬЮТЕРНЫХ СИСТЕМАХ С ПОМОЩЬЮ АСПЕКТНО-ОРИЕНТИРОВАННОГО ПРОГРАММИРОВАНИЯ Д.А. Стефанцов Томский государственный университет E-mail: d.a.stephantsov@gmail.com Рассматрива...»

«230 УПРАВЛЕНИЕ, ВЫЧИСЛИТЕЛЬНАЯ ТЕХНИКА И ИНФОРМАТИКА УДК 37.018.46:339.138 И.И. Веберова Исследование рынка потребителей как основа позиционирования и продвижения программы дополнительного профессионального о...»

«ФЕДЕРАЛЬНОЕ АГЕНСТВО ЖЕЛЕЗНОДОРОЖНОГО ТРАНСПОРТА ГОСУДАРСТВЕННОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ ВЫСШЕГО ОБРАЗОВАНИЯ МОСКОВСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ ПУТЕЙ СООБЩЕНИЯ ИМПЕРАТОРА НИКОЛАЯ II МГУПС (МИИТ) _ Кафедра «Геодезия, геоинформатика и навигация» У.Д. Ниязгул...»

«МИНИСТЕРСТВО СЕЛЬСКОГО ХОЗЯЙСТВА РФ Федеральное государственное бюджетное образовательное учреждение высшего профессионального образования КУБАНСКИЙ ГОСУДАРСТВЕННЫЙ АГРАРНЫЙ УНИВЕРСИТЕТ ФАКУЛЬТЕТ ПРИКЛАДНОЙ ИНФОРМАТИКИ УТВЕРЖДАЮ Декан факультета прикладной информатики _ профессор С. А. Курно...»

«Московский государственный университет имени М. В. Ломоносова Факультет вычислительной математики и кибернетики Кафедра математических методов прогнозирования ПОДОПРИХИН Дмитрий Александрович Распознавание паттернов в сигналах головного мозга ДИП...»

«ГОСУДАРСТВЕННЫЙ КОМИТЕТ СВЯЗИ, ИНФОРМАТИЗАЦИИ И ТЕЛЕКОММУНИКАЦИОННЫХ ТЕХНОЛОГИЙ РЕСПУБЛИКИ УЗБЕКИСТАН ТАШКЕНТСКИЙ УНИВЕРСИТЕТ ИНФОРМАЦИОННЫХ ТЕХНОЛОГИЙ Кафедра Информационная безопа...»

«Второй (заключительный) этап академического соревнования Олимпиады школьников «Шаг в будущее» по общеобразовательному предмету «Информатика» 9 класс, февраль, 2016 г. Вариант № 2. Задание 1 (...»

«УДК 519.8 ОПРЕДЕЛЕНИЕ ПОКАЗАТЕЛЕЙ ЛЯПУНОВА НА ПРИМЕРЕ МОДЕЛИ СЕЛЬКОВА В ПРИСУТСТВИИ ВНЕШНЕЙ ПЕРИОДИЧЕСКОЙ СИЛЫ © 2013 А. Ю. Верисокин аспирант каф. общей физики e-mail: ffalconn@mail.ru Курский государственный университе...»

«Санкт-Петербургский государственный университет Прикладная математика и информатика Вычислительная стохастика и статистические модели Григорьева Ирина Владимировна Канонический анализ категориальных данных с приложением в мар...»

«БЛ.СОВЕТОВ САЖОВЛЕВ Моделирование систем Издание третье, переработанное и дополненное Рекомендовано Министерством образования Российской Федерации в качестве учебника для студентов высших учебных...»

«И.Н. Блинов, В.С. Романчик Java 2 ПРАКТИЧЕСКОЕ РУКОВОДСТВО Минск УП «УниверсалПресс» УДК 004.432.45 ББК 32.973.26-018.1 Б69 Рекомендовано к изданию Ученым советом факультета прикладной математики и информатики БГУ Авторы: доцент кафедры ИПМОАП БГУ, кандидат физико-математичес...»

«Специальность «Транспортная логистика». Дисциплина «Информатика» Лабораторная работа 4. Инструменты анализа прикладных данных в MS Excel Цель работы:  1. Научиться устанавливать контроль ввода данных в MS Excel.  2. Научиться выполнять поиск нужной информации с помощью фильтра.  3. Научиться вычислять промежуточные итоги в списках.  4. О...»

«ВЕСТНИК ТОМСКОГО ГОСУДАРСТВЕННОГО УНИВЕРСИТЕТА 2012 Управление, вычислительная техника и информатика № 4(21) УДК.519.24 Б.С. Добронец, О.А. Попова ЧИСЛЕННЫЙ ВЕРОЯТНОСТНЫЙ АНАЛИЗ ДЛЯ ИССЛЕДОВ...»

«Маслобоев А.В. и др. Мультиагентная информационная технология. УДК 004.94 : 004.89 : 378.1 : 338.2 Мультиагентная информационная технология поддержки управления качеством высшего образования А.В. Маслобоев, В.В. Быстров, А.В....»

«Учреждение образования «Белорусский государственный университет информатики и радиоэлектроники» УТВЕРЖДАЮ Проректор по учебной работе и менеджменту качества 24 дека...»

«TNC 620 Руководствопользователя Программированиециклов Программное обеспечение с ЧПУ 817600-02 817601-02 817605-02 Русский (ru) 5/2015 Основные положения Основные положения О данном руководстве О...»

«Отчет по внешнему аудиту НКАОКО-IQAA СОСТАВ ВНЕШНЕЙ ЭКСПЕРТНОЙ ГРУППЫ Срсенбі бдіжаан Манапович Руководитель группы Заведующий кафедрой «Математические методы и моделирование» Южно-Казахстанского государственного университета им. М.Ауэзова, д.ф.-м.н., профессор Питер Эндрас Международны...»

«Московский Государственный Университет им. М.В. Ломоносова Факультет Вычислительной Математики и Кибернетики Кафедра Математических Методов Прогнозирования Метод статистической верификации регрессионных моделей, основанный на перестановочных тестах. Дипломная работа выполнил Дзыба Дмитрий Серг...»

«АННОТАЦИЯ ПРОГРАММЫ УЧЕБНОЙ ПРАКТИКИ ( по получению первичных профессиональных умений и навыков, в том числе первичных умений и навыков научно-исследовательской деятельности) Место учебной практики в структур...»

«Математическое моделирование субъективных суждений в теории измерительно-вычислительных систем Д. А. Балакин, Б. И. Волков, Т. Г. Еленина, А. С. Кузнецов, Ю. П. Пытьев Рассмотрены методы моделирования неполного...»

«Сметанин Ю.Г.1, Ульянов М.В.2 Вычислительный центр им. А.А. Дородницына, Российская академия наук, г. Москва, д.ф.-м.н., главный научный сотрудник, smetanin.iury2011@yandex.ru Институт проблем управления им. В.А. Тра...»

«Инновационные образовательные технологии в современной школе. «Нет ничего сильнее идеи, время которой пришло». А. Горячев..Можно смело сказать, что пришло время технологии деятельностного метода как средства реализации современных целей образова...»

«САНКТ-ПЕТЕРБУРГСКИЙ ГОСУДАРСТВЕННЫЙ УНИВЕРСИТЕТ КАФЕДРА КОМПЬЮТЕРНОГО МОДЕЛИРОВАНИЯ И МНОГОПРОЦЕССОРНЫХ СИСТЕМ Христенко Евгений Александрович Выпускная квалификационная работа бакалавра Сравнение эффективности методов многомерной виз...»

«Министерство образования Республики Беларусь Учреждение образования Белорусский государственный университет информатики и радиоэлектроники «Утверждаю» Проректор по учебной работе и социальным вопросам _ А.А. Хмыль «_»2013 г. ПРОГРАММА дополнительного экзамена в магистратуру по специальности 1-45 81 01 «Инфокоммуникационные си...»

«ИНФОРМАЦИОННЫЕ ТЕХНОЛОГИИ УДК 004.272:004.942 ББК 32.973-018.2; 32.81; 28.071 АСПЕКТЫ ИСПОЛЬЗОВАНИЯ ПАКЕТА MATLAB НА ВЫЧИСЛИТЕЛЬНОМ КЛАСТЕРЕ ДЛЯ РЕШЕНИЯ БИОМЕТРИЧЕСКИХ ЗАДАЧ А.В. Карпов, О.В. Комогорцев В статье рассматриваются особенности использования пакета MATLAB для проведения матема...»

«МИНИСТЕРСТВО ОБЩЕГО И ПРОФЕССИОНАЛЬНОГО ОБРАЗОВАНИЯ РОСТОВСКОЙ ОБЛАСТИ ГОСУДАРСТВЕННОЕ БЮДЖЕТНОЕ ПРОФЕССИОНАЛЬНОЕ ОБРАЗОВАТЕЛЬНОЕ УЧРЕЖДЕНИЕ РОСТОВСКОЙ ОБЛАСТИ «РОСТОВСКИЙ-НА-ДОНУ КОЛЛЕДЖ СВЯЗИ И ИНФОРМАТИКИ» Методика расчета нагрузки и состава оборудова...»





















 
2017 www.pdf.knigi-x.ru - «Бесплатная электронная библиотека - разные матриалы»

Материалы этого сайта размещены для ознакомления, все права принадлежат их авторам.
Если Вы не согласны с тем, что Ваш материал размещён на этом сайте, пожалуйста, напишите нам, мы в течении 1-2 рабочих дней удалим его.