Современная электронная библиотека ModernLib.Net

The Programmers' Stone (Программистский камень)

ModernLib.Net / Carter Alan / The Programmers' Stone (Программистский камень) - Чтение (стр. 8)
Автор: Carter Alan
Жанр:

 

 


      Во-вторых, выполняемые аудиторами сравнения, должны проводится между возможностями процесса и тем, что требует от пользователей бизнес. Одна из худших вещей, возникающая от враждебного аудита, -- это то, что разногласия при определенных обстоятельствах могут легко вылиться в оргвыводы. Например, большинство процессов содержат пункт, говорящий, что в записях у кадровиков должны делаться отметки о прохождении обучения. Это вполне соответствует полуквалифицированной работе, когда рабочие могут получать "билетики", позволяющие им выполнять специфические задачи. В транспортном отделе часто требуется получать сертификаты из-за законов, и там проблема немного другая. В программирующей команде формальные курсы обучения дают очень мало по сравнению с навыком, получаемым в команде, поскольку почти никогда не имеют отношения к делу. Повышение квалификации происходит либо на работе, либо за счет свободного времени работника. Многие программисты проводят по нескольку сотен часов в год за самообучением дома. Авторы основанных на "или иначе" процессах должны помнить, что наниматель даже не смеет потребовать от работника разглашения, что же он изучает дома, поэтому не стоит грубить ему, уходя от вопроса, поскольку менеджерам проектов действительно нужна эта информация и им приходится спрашивать вежливо. Так что проверка бессловесной покорности глобальному механизму бесполезна по сути и приведет только к спорам об интерперетации. Вместо этого аудитор должен оценить локальные потребности и удостовериться в применимости процесса в свете этих потребностей.
      В-третьих, аудитора нужно рассматривать как коллегу по бизнесу со своим положительным вкладом в работу. Эти люди видят сотни потребностей бизнеса и картотек, автоматизированных и ручных. Если бы глупейшая война между проверяющим и проверяемыми смогла превратиться в совместную критику процесса, проверяемые получили бы возможность открыто говорить о своих проблемах, вместо игры в молчанку, как это советует большинство менеджеров. Только тогда, когда они знают, в чем заключаются настоящие проблемы, аудиторы смогут покопаться в обширном опыте и предложить решения, которые, как они видели, срабатывали у других.
      Аудиторам качества не следует быть счетчиками зерна, единственный положительный вклад которых заключается во внедрении изысканных ритуалов для компенсации неестественной сложности процесса. Их роль должна идти гораздо дальше. Роберт Хайнлайн (Robert Heinlein) сказал, что цивилизация строится на библиотечной науке, а аудиторы качества -- это современные библиотечные ученые инженерной индустрии. Они могут сказать нам, как дешевле всего сохранить данные так, чтобы мы смогли найти их позднее, при условии, что мы знаем что ищем.


Простая и надежная среда

      Средства разработки состоят из всех тех инструментов (включая текстовые процессоры), которые используют программисты, а также машин и сетевой инфраструктуры, на которой они работают. Хорошие средства разработки должны быть как можно проще. Как и в программах, которые вы разрабатываете: чем больше сложности, тем больше места для проблем, тем выше стоимость эксплуатации.
      Хорошее общее правило -- держать всю свою работу, включая средства конфигурации (в файлах скриптов, если нужно) в виде простого текста. Имейте возможность при необходимости стереть все, кроме исходного текста, и автоматически перекомпилировать.
      Самая главная задача репозитария и системы управления конфигурацией -- обеспечить вашу безопасность. Каждый добавляемый элемент сложности увеличивает опасность падения системы. Команда, перекладывающая контроль на систему управления конфигурацией с клиент-серверной архитектурой, рискует потерять проект из-за потери ссылочной целостности в системе. Получающийся хаос, когда вся работа останавливается, понуждает найти способ обеспечения безопасности в создании резервной копии, люди начинают выяснять, что необходимо переделать заново, и дух падает. Не так сложно построить хорошую систему управления конфигурацией на основе скриптов с использованием списков рассылки, на основе чего-то простого, типа SCCS или RCS, для обеспечения контроля версий.
      Та же самая логика, что целью является безопасность, и поэтому простота увеличивает надежность и безопасность, когда система и пользователи в стрессе, применима и к созданию резервных копий. Когда что-то не так, главное -- это знать, как откатить систему в нормальное состояние, в котором она была некоторое время назад. Инкрементное резервирование с гибкой стратегией отслеживания изменений дерева каталогов может уменьшить доверие к тому, что все работает нормально, даже когда это так. Или, скорее, уменьшит. Команда, перешедшая к простому тексту, записывающая все на ленту и меняющая ленту каждую ночь, знает, где все это находится, и на этом надежном фундаменте способная достигать прогресса в выполнении настоящей работы, действительно умнее, чем любящая сложности команда, проводящая месяц только в попытках организовать работу.
      Делай проще. Никогда не латай ничего -- ты никогда не знаешь, нашел ли ты все проблемы. Сотри и загрузи заново. Всегда будь способен переформатировать свой диск, переинсталлировать свои инструменты, восстановить тексты из репозитария, переконфигурировать и перекомпилировать. Это дает полную безопасность и сохраняет все то время, которое ушло бы на суету по поводу вирусов. Кого это волнует?

Типы систем

      Один из самых важных вопросов, который нужно задать о новой системе, или даже о старой системе, с которой пришлось столкнуться, - какого вида эта система? Никто не станет даже пытаться расположить колонию хиппи вокруг плаца или военный лагерь в виде хаотически расположенных вигвамов, соединенных тропинками! Любая система может обладать более чем одним признаком из перечисленных ниже, хотя некоторые взаимно исключают друг друга. Эти, вероятно полезные, признаки -- грубые категории, встречающиеся на практике. Они не выведены из какой-то лежащей в основе теории. Примеры типов систем такие:
       Монолитная.Централизованная обработка, а также оффлайновые соединения с пользователями либо примитивные терминалы -- вот что такое монолит. Пользователи находятся либо в том же здании либо подключены по постоянной выделенной линии. Это прекрасный способ проделать обработку громадного количества коммерческих данных, когда к системе подключены промышленные средства ввода документов, огромные хранилища данных на дисках и лазерные принтеры, выплевывающие страницы на несколько футов в воздух. У монолитов свои проблемы -- пользователи, например, часто лишены возможности доступа к резервной копии! Но это сглаживается высокой степенью контроля над системой. Устойчивое к сбоям оборудование (включающее RAID и резервирование питания) и сложные технологии серверов баз данных -- вот где имеют преимущество такие системы.
       Клиент-Сервер.Распределяет обработку среди пользователей для работы, которая должна быть сосредоточена в одном месте. Предоставляет хорошее разбиение на уровни, разделяя хранение, обработку и взаимодействие с пользователем (HCI - Human-Computer Interface, человеко-машинный интерфейс). Допускает локальную специализацию по функциям и нескольких поставщиков, и, следовательно, сохраняет инвестиции при последующем расширении. Требует (и, следовательно, использует) более сложную сеть, чем монолит, но лучше приспособлена к интерактивной работе, давая максимально возможному числу пользователей выполнять обработку локально, используя средства, наиболее соответствующие потребностям пользователей. Все клиент-серверные архитектуры внутри себя прячут монолит.
       Интерактивная.Позволяет пользователю вступать в диалог с системой. Незаменима, когда требуется быстрый отклик, либо при работе с широкой публикой. В наше время обычно построены на графическом интерфейсе. Состояние системы постоянно изменяется, поэтому требуется журналирование, либо периодически будет возникать риск потери данных. Проблема распределения нагрузки -- важный момент, поскольку число пользователей меняется, и нагрузка регулярно скачет от нормальной до пиковой.
       Пакетная.Хотя их часто считают устаревшей стратегией с использованием перфокарт, пакетные системы просты, надежны, великолепно себя ведут при неустойчивой связи и масштабируются лучше, чем интерактивные системы.
       Управляемая событиями.Реагирует на события из внешнего мира. Приложения с графическим интерфейсом большую часть времени находятся в ожидании, пока пользователь что-нибудь нажмет, чтобы отреагировать на событие. "Однорукие бандиты" (игровые автоматы) - тоже управляемые событиями системы, как и сигнализация. У управляемых событиями систем сложное пространство состояний и большая чувствительность к вводимым данным. Часто для них ограничивается время ответа на событие. Если проблему можно представить как не управляемую событиями систему, то лучше так и сделать.
       Управляемая данными.Аналогична управляемой событиями и пакетной, но с ясными потоками данных через каждую подсистему, где наличие входных данных -- это событие-триггер, заставляющее каждую подсистему выполнять свой цикл обработки. Управляемые данными системы гибче пакетных, поскольку размер пакета может изменяться динамически, но они обладают надежностью пакетных систем, поскольку мы всегда можем узнать, как далеко мы зашли в обработке каждого пакета. Каждая подсистема может организовывать элементарную операцию, выдающую информацию и удаляющую входные данные, так что даже при восстановлении после сбоя питания система сразу готова к работе, поскольку состояние системы всегда устойчиво. Системы электронной почты - пример систем, управляемых данными.
       Оппортунистическая.(рассчитывающая на благоприятную возможность)Этот тип систем никогда не пострадает от ошибок при передаче, поскольку они используют каналы только тогда, когда это возможно. В действительности, большинство офисов оппортунистические, поскольку они построены на основе Ethernet. Данные хранятся в буфере до тех пор, пока локальный передатчик не передаст их без конфликтов (collision).
       Штурманская(Dead reckoning). Пытается проследить каждый шаг пользователя. Часто представляется желательной, поскольку позволяет обеспечить сильную проверку данных пользователя, но может оказаться очень хрупкой, что приводит к шуткам типа знаменитой: "Бесполезно нажимать на это, компьютер говорит, что этого тут нет!"
       Сходящаяся в одну точку(Convergent). Тут не интересно отслеживать каждый шаг происходящего в реальном мире процесса, здесь внимание направлено на регистрацию изменений состояний в ключевых точках и интегрировании данных в виде аккуратной картины реального мира в некоторый момент в прошлом, и прогрессивно ухудшающейся аппроксимацией по мере приближения к настоящему. Мобильные пользователи, которые закачивают данные со своих ноутбуков в корпоративную сеть -- хороший пример. Мы очень точно знаем, как много мы продали на прошлой неделе, очень приблизительно знаем о вчерашнем дне, но мы не получили еще данные от Джона и Джилла, поэтому не знаем почти ничего о сегодняшнем дне.
       На гребне волны(Wavefront). Системы, работающие по мере появления событий. Управление освещением или телефонная коммутация -- вот примеры. При сбоях мы больше заинтересованы в быстром восстановлении работы, чем обеспокоены потерей данных.
       Ретроспективная(Retrospective). Связанная с поддержанием аккуратной записи прошлого. Избежание потерь данных обычно очень важно. Пример - бухгалтерские системы.

Обработка ошибок - лимфатическая система программы

      Часто говорят, что следует перехватывать сообщения об ошибках, но в этом мало пользы, если неизвестно, что же с ними собираются делать. Обработка ошибок составляет такую же часть структуры программы, как и логика обработки нормальных ситуаций, но не настолько же почитаема. Эта связь скорее похожа на взаимодействие кровеносной и лимфатической систем в теле. Вам необходимо предусматривать обработку ошибок на каждой стадии проектирования. Например, нет никакого смысла регистрировать сообщения об ошибках записи в журнал ошибок в процедуре обработки ошибок!
      Концептуальная целостность требует, чтобы вы определили общий подход к обработке ошибок, используемый по всему проекту. Как вы будете сообщать об ошибках? Какие идиомы будут применять программисты для элегантной проверки ошибок без нарушения главного потока управления? Совершенно необязательно делать второй вызов, чтобы проверить, возникла ли ошибка или что там было -- иначе код будет распухать. В идеале ошибки должны проверяться при выходе из функции, чтобы использовать краткие идиомы, что приводит к получению понятного кода и, как следствие, плато качества:
      if((fp = fopen(...)) == NULL) { // Error }
      или
      if(!DoTheBusiness()) { // Error }
      Можно слышать множество жалоб на распухание логики обработки ошибок до состояния, когда строго написанный вызов функции может занимать столько строк, что уже невозможно увидеть всю картину в целом. Как картостроители, мы знаем, что обрастание таким количеством Достойных стандартов кодирования, что невозможно написать замечательную программу -- ошибочный путь.
      В процедурном (и, в некоторой степени, объектном) подходе к коду ведется дискуссия по поводу стратегии обработки ошибок, где их следует обрабатывать. В дискуссии рассматриваются два подхода. Первый говорит, что вызываемая подпрограмма не должна возвращать управление, пока она не выполнила то, что ее просили, и это можно назвать "полным делегированием". Другой говорит, что вызываемая подпрограмма должна выполняться, пока не столкнется с проблемой, и тогда аккуратно возвращать наверх весь мусор, который она получила и сообщать вызывавшему, что произошла ошибка - это мы назовем "ложная готовность".
      Привлекательность полного делегированиясостоит в том, что при этом получается очень чистый код с вызывающей стороны, и он может быть очень эффективным, и он препоручает ответственность за поддержание состояния более низких уровней самим уровням. Недостаток заключается в том, что он работает только при условии, что вызывающей стороне на самом деле не требуется обработка последствий ошибки в ее собственном контексте. Это ограничивает его системами для типовых ситуаций, где приложение действительно может не знать о том, что происходит на нижних уровнях, и если нижний уровень реально не сможет устранить проблему, то попытка возврата будет недопустима, поскольку приведет либо к зависанию процесса либо к фатальной ошибке.
       Ложная готовностьвсегда позволяет вызывающей стороне отреагировать на проблемы, и вложенные вызовы смогут откатить стек, пока не будет достигнут уровень, способный разобраться с проблемой. Логика обработки ошибок пронизывает каждый уровень, но может быть минимизирована аккуратным кодированием, если автор и сопровождающий знают, как работать в этой схеме. Кроме того, можно обеспечить трассировку вызовов, показывающую, как процесс напоролся на проблему, так что всегда можно воспроизвести ситуацию.
      Мы не думаем, что нужно дискутировать на эту тему, поскольку когда полное делегирование применимо, оно оказывается на самом нижнем уровне. Смешивание этих подходов приводит к кошмару в коде, поскольку нарушает концептуальную целостность.
      Некоторые объектные языки предоставляют исключения, которые позволяют автоматически схлопывать стек до уровня, ответственного за обработку данной ошибки. Это замечательный способ освободить основную логику от деталей обработки ошибок. Важный момент, который нужно помнить, заключается в том, что иногда исключение может быть передано гораздо выше, если ты хочешь не просто обработать его, а хочешь знать, из-за чего оно произошло. Сообщение об ошибке с нижнего уровня, говорящее
      Could not write() datafile ftell() = 246810
      если за ним не следует другое, говорящее
      Could not Save World
      при отладке просто бесполезно. Ты можешь передавать исключения на более высокий уровень без нарушения главной логики управления, и следует подумать, как это сделать.
      Не злоупотребляй в рабочее время исключениями для создания замысловатого потока управления. В особенности не скрывай longjmp() в макросах и не вызывай их из обработчиков. Если ты желаешь поэкспериментировать с Силами Тьмы, делай это дома. Нам всем приходится это делать, но что печальнее всего, и наши коллеги могут ухватиться за неправильную идею и начнут ее рационализировать. Не кажется ли странным, что мы производим сегодня языки, которые страдают запорами по этому поводу, когда заняло годы просто правильно переопределить с помощью const описания прототипов функций, но при этом нам позволено фокусничать с потоком управления так, как мы не пытались делать даже на ассемблере?
      По возможности избегайте расставлять захламляющие ваш код assert()и условную компиляцию отладочных макросов. Вам не достичь необходимой достаточности плато качества, если все вокруг завалено мусором.

Увлечение формой (а не содержанием) и комбинаторный взрыв

      По некоторым причинам существует мнение, что для того, чтобы системы были робастными (устойчивыми к ошибкам), им требуются нормальные режимы, режимы сбоя, в которые они попадают при сбое, и режимы восстановления, в которые они переходят после попадания в режим сбоя для возврата в нормальный режим. Частично это провоцируется потерявшими ориентировку пользователями, которые пытаются описать цели в случае сбоя, но делают это рассуждая о "режимах" системы. Это деликатная область, поскольку при обсуждении сбоя пользователи должны думать о составляющих реальной системы, которые могут давать сбой, и они должны обсуждать сбои заранее, раз они вынуждены подписывать Требования Пользователя, которые потом могут быть использованы как палка, которой их будут бить. Это значит, что они должны пытаться изучить финальную реализацию лучше, чем ее знают сами разработчики, чтобы суметь описать, что нужно делать при сбое компонентов.
      Подчеркивая важность диалога, необходимо также отметить часто упускаемый момент. Действительно ли пользователь хочет, чтобы вы реализовали режим сбоя, детально описанный в Требованиях Пользователя? Может будет достаточно системы, которая просто работает? Конечно, скорее всего так и есть, но многие команды сломя голову бегут и реализуют эти сбои, как и сказано в Требованиях Пользователя.
      Современная легенда в ICL гласит, что когда они покупали первую партию плат от Fujitsu, то сделали оценку, что надежность будет составлять 1% отказов. Поэтому прямо перед отправкой первой сотни один из директоров Fujitsu взял сверху из ящика плату и, перед тем как положить ее обратно, стукнул по ней молотком.
      Помимо необходимости управлять переключениями состояний и исполнением редко когда нужного кода, в системах такого рода есть более глубокая проблема.
      Сначала мы находимся в нормальном режиме. Затем попадаем в режим сбоя. Затем в режим восстановления. Что случится, если опять произойдет сбой? Что, у нас приключился сбой во время восстановления из режима сбоя? Восстановления из сбоя во время восстановления из сбоя? Тут очень легко появляется необходимость бесконечного расползания системы режимов, а не просто распознавание сбоев. Конечно, если дизайн всех уровней одинаков, то ничего страшного -- вам остается лишь доказать, что это именно тот случай.
      Если вы смогли остановить бесконечное расползание, то, вероятно, сделали и следующий шаг -- устранили нормальный режим и режим восстановления и оставили только режим сбоя! (Или устранили нормальный режим и режим сбоя и оставили режим восстановления, если вам так больше нравится.) При этом отпадает необходимость управлении скоординированными переключениями на многих платформах в моменты, когда гремлины шевелят контакты в сетях питания. Системе даже не нужно знать, что она находится под непрерывной атакой реального мира, и что это уже четвертый раз, как она пытается обработать пучок транзакций. При этом, если вы достаточно аккуратно определили Правильные Вещи, для выполнения Правильных Вещей не нужно знать их контекста.
      Наличие множества режимов для обработки сбоев на самом деле гораздо менее нужно, чем думает большинство людей, а избавление от них очень сильно улучшает управляемость сложностью. Если мы желаем сохранить контроль и понимание наших проектных решений, мы должны минимизировать сложность всего, что мы можем. На стороне победителя в этом уравнении находится плато качества. На стороне проигравшего -- взаимодействие одной сложности с другой сложностью, дающее невообразимый рост пространства состояний системы, называемый " комбинаторным взрывом".

Избегайте избыточности представления

      Каждый проектировщик баз данных знает о нормальных формах. Дело становится очень сложным, если пытаться проводить полный анализ предмета в условиях реального мира, но базовая идея очень проста. Избегайте избыточности в представлении. Если вам понадобилась запись заказа и запись счета, каждая из которых требует названия заказчика, храните запись о заказчике в одной таблице, и используйте уникальную ссылку на таблицу заказчиков в записи заказа. Затем вставьте ссылку на таблицу заказов в записи счета. Тогда вещи никогда не встанут наперекосяк, когда вам придется не забывать удостовериться в загрузке разных мелочей каждый раз, когда вы хотите изменить данные.
      Дело в том, что концепция нормализации базы данных из тех же соображений применима везде. Никогда не храните одну вещь и, отдельно, еще одну неподсоединенную вещь, которая полагается на то, что первая существует. Пусть данные управляют своей собственной структурой, и не появится никаких перекосов. Ритуальное использование структур данных часто включает претензию на управление держа зубную щетку и палочки для еды в одной руке. Если мы создаем структуру финансовых отчетов в Ящике А, а сложное описание Ящика А в Ящике В, то мы можем провести много времени копаясь в Ящике В, и ни разу не обратим внимания на тот факт, что на самом деле мы вообще не понимаем того, что происходит в Ящике А!
      Не попадайте в эту ловушку. Пусть данные представляют сами себя, или, как сказал Лори Андерсон (Laurie Anderson) в книге "Большая наука" ( Big Science)
      Let X = X

Посмотри на состояние всего этого!

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

Реальность системы как объекта

      Этот раздел прежде всего обращен к проектировщикам объектных систем, поскольку проблема, которой он касается, прежде всего проявляется в объектном подходе. Это происходит из-за строгой инкапсуляции, которую предоставляет объектная модель. Мы уже обсуждали два подхода к проектированию объектных систем, которые предпочитают картостроители и паковщики. Подход картостроителя включает понимание природы желания, а затем в процессе итераций выявляется адекватная динамика системы и создается оптимальное взаимное соответствие (карта) между динамикой проблемы и семантикой системы.
      Объектные проекты нацелены на создание формализованного Хода Конем ("вилки") с использованием подхода, который явно связывает объекты реального мира с жизнеспособной семантикой системы посредством объектных языков программирования (будь это Eiffel или генератор кода UML). При разработке этих проектов их создатели стремятся представить все, что есть сейчас в реальном мире, а не будет завтра, когда система будет использоваться. Главное отличие в том, что завтра система будет существовать в мире пользователя -- сегодня это не так. Поэтому аналитики регулярно создают маленькие картинки мира пользователя в будущем, которые содержат все, кроме самой компьютерной системы, которая является центром всего сценария.
      Тем не менее, внутренний проект системы также испытывает затруднения из-за недостаточного представления самой системы. Кто-то мог бы сказать, что система реального мира и внутренняя система -- это одно и то же как в реальном, так и в абстрактном мирах, и, следовательно, эта идентичность в объектных проектах формирует появление "Хода Конем" в наиболее фундаментальной форме.
      Вот два глубоких вопроса, возникающих при поиске объектов и попытках найти их взаимосвязи:
      Кто кого порождает? Кто чьи методы использует?
      Имея в проекте чистый класс System (Система), гораздо легче построить иерархию наследования и увидеть, откуда приходят такие вещи как GUI и ввод/вывод на ленту, не говоря уже о событиях, запускаемых по таймеру! Это не означает, что на более поздней стадии проектирования функциональность нельзя поместить в специализированные классы, но в проекте это дает реальности мира пользователя равный вес с реальностью системы, поэтому результат будет удовлетворять обоим критериям.
      Битва с абстрактным набором беспорядочно летающих вокруг классов без способа сверки с реальностью так же безнадежна, как и любая другая деятельность, если вы не знаете, что же вы делаете.
      Конечно, потребность в классе System (Система) исчезает, если кому-то нужно просто моделирование, а не автоматизация бизнес-процессов, в которых не представлены управляющие системы. Каким мог бы быть подход в таком случае? Здесь мы напираем на то, что проектирование, формируемое стратегией картостроения, включает в себя больше, чем просто набор процедурных действий. Это означает очерчивание проблемы, прояснение желаний, нахождение точки оптимального приложения сил между динамикой проблемы и семантикой системы. Если ваш проект не получает преимуществ от класса System (Система), не используйте его!

Детекторы утечки памяти

      На рынке имеется ряд продуктов, которые на основе различных стратегий обнаруживают утечки памяти в приложениях. Утечка памяти -- это то, что случается, когда программа запрашивает блок памяти из кучи (используя, например, malloc() в Cи под UNIX или DOS , или оператор new в C++), а затем забывает вернуть его обратно по своему завершению. Иногда это приводит к нарушению работы других процессов, работающих на той же платформе, поскольку некоторые операционные системы позволяют одному процессу захватить всю имеющуюся память системы и файл подкачки.
      Даже если операционная система достаточно разумна, чтобы ограничить выделяемую отдельному процессу память, приложение может вскоре исчерпать свою квоту, что обычно заканчивается сбоем с точки зрения пользователя.
      Поэтому утечки памяти -- это Плохая Вещь.
      Это причина того, почему люди продают, и покупают, детекторы утечки памяти. Неприятность в том, что утечки -- это симптом проблемы, а не причина проблемы. Не так трудно вызвать free() или удалить объекты, которые больше не нужны. Применение delete к коллекции указателей на активные объекты -- вот опасное занятие, как и перезаписывание их адресов. Что если эти объекты, скажем, зарегистрировали функции обратного вызова (callbacks) в GUI ? Как же их освободить? Деструкторы вне контроля -- это программист вне контроля. Если программист не может продемонстрировать контроль над объектами, которого достаточно для избежания утечек памяти, то как мы можем думать, что правильно еще хоть что-нибудь?
      Концептуальная целостность -- это одна из самых сильных подпорок в сохранении контроля над объектами. Полезное общее правило (хотя, как и все правила, не всегда применимое) должно говорить, что уровень, который конструирует модуль, должен отвечать за его уничтожение. Это, по крайней мере, фокусирует внимание на жизненном цикле объектов, а не на всего лишь нескольких аспектах их поведения, которые можно проследить на диаграммах использования.

Таймауты

      Один из наиболее эффективных способов получения "затравки" для генератора случайных чисел -- посмотреть на системные часы. Аналогично, если два процесса работают на одном и том же процессоре, то мы никогда не сможем предсказать, сколько времени пройдет от запуска программы до достижения заданной точки в программе. Мы не можем даже точно предсказать, сколько процессорного времени будет выделено каждому процессу.
      Таким образом, таймауты -- это Плохая Вещь. Есть мнение, что они значительно увеличивают пространство состояний системы, что делает поведение системы гораздо хуже предсказуемым для проектировщика. При отладке они могут привести к условиям, при которых невозможно будет воспроизвести сбой. Не используйте их, кроме тех случаев, когда они абсолютно необходимы.
      Коммуникационные уровни часто обязаны использовать таймауты, поскольку когда приходит время их работы, единственный способ обнаружить, что удаленное устройство готово к взаимодействию -- послать ему сообщение и дождаться, посылает ли оно ответ. Как долго нужно ждать? "Проблема Византийских полководцев" (`Byzantine Generals' Problem') это иллюстрирует. Поэтому в большинстве современных систем есть таймауты на уровне коммуникации, но нет никакого оправдания использовать их повсюду, и там, где они должны использоваться, они должны быть сокрыты внутри инкапсулирующего объекта, который для целей отладки может быть заменен на детерминированный генератор событий (например, нажатие клавиши).

  • Страницы:
    1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12