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


Большущая выборка из МакКоннела, частью как справочник, частью - шпаргалка.

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
"Пишите код так, как будто сопровождать его будет склонный к насилию психопат, который знает, где вы живете"© Джон Вудс.

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

Проблема не в том, что клиенты - более низкая форма жизни.

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

В архитектуре должны быть обоснованы важнейшие принятые решения. С подозрением относитесь к обоснованиям из разряда «мы всегда так делали». Здесь уместно вспомнить одну поучительную историю. Бет хотела приготовить тушеное мясо по прославленному рецепту, передававшемуся из поколения в поколение в семье ее мужа Абдула. Абдул сказал, что его мать солила кусок мяса, перчила, обрезала его края, укладывала в горшок, закрывала и ставила в духовку. На вопрос
Бет «Зачем обрезать оба края?» Абдул ответил: «Не знаю, я всегда так делал. Спрошу у мамы». Он позвонил ей и услышал: «Не знаю, просто я так всегда делала. Спрошу у твоей бабушки». А бабушка заявила: «Понятия не имею, почему вы так делаете.
Я делала так потому, что мой горшок был маловат».

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

есть два способа разработки
проекта приложения: сделать
его настолько простым, чтобы
было очевидно, что в нем нет
недостатков, или сделать его
таким сложным, чтобы в нем не
было очевидных недостатков.
Ч. Э. Р. Хоар (C. A. R. Hoare)

Изучая естественные языки, лингвисты Сапир и Уорф (Sapir and Whorf) высказали предположение, что способность к размышлению над определенными идеями связана с выразительной силой языка. Согласно гипотезе Сапира Уорфа способность человека к обдумыванию определенных мыслей зависит от знания слов, при помощи которых можно выразить эту мысль. Если вы не знаете слов, то не сможете выразить мысль и, возможно, даже сформулировать ее (Whorf, 1956)

Управление сложностью — самый важный технический аспект разработки ПО. Помоему, управление сложностью настолько важно, что оно долж но быть Главным Техническим Императивом Разработки ПО.

Сложность — не новинка в мире разработки ПО. Один из пионеров информатики Эдсгер Дейкстра обращал внимание на то, что компьютерные технологии — единственная отрасль, заставляющая человеческий разум охватывать диапазон, простирающийся от отдельных битов до нескольких сотен мегабайт информации, что соответствует отношению 1 к 109, или разнице в девять порядков (Dijkstra, 1989). Такое гигантское отношение просто ошеломляет. Дейкстра выразил это так: «По сравнению с числом семантических уровней средняя математическая теория кажется почти плоской. Создавая потребность в глубоких концептуальных иерархиях, компьютерные технологии бросают нам абсолютно новый интеллектуальный вызов, не имеющий прецедентов в истории». Разумеется, за прошедшее с 1989 г. время сложность ПО только выросла, и сегодня отношение Дейкстры вполне может характеризоваться 15 порядками.

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

Работая над проблемой, я никогда не думаю о красоте. Я думаю только о решении проблемы. Но если полученное решение некрасиво, я знаю, что оно неверно. Р. Бакминстер Фуллер (R. Buckminster Fuller)

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

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

Иными словами — и это неизменный принцип, на котором основан всегалактический успех всей корпорации, — фундаментальные изъяны конструкции ее товаров камуфлируются их внешними изъянами. Дуглас Адамс (Douglas Adams)

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

С точки зрения сложности, главное достоинство абстракции в том, что она позволяет игнорировать нерелевантные детали. Большинство объектов реального мира уже является абстракциями некоторого рода. Как я только что сказал, дом — это абстракция окон, дверей, обшивки, электропроводки, водопроводных труб, изоляционных материалов и конкретного способа их организации. Дверь же — это абстракция особого вида организации прямоугольного фрагмента некоторого материала, петель и ручки. А дверную ручку можно считать абстракцией конкретного способа упорядочения медных, никелевых или стальных деталей. Мы используем абстракции на каждом шагу. Если б, открывая или закрывая дверь, вы должны были иметь дело с отдельными волокнами древесины, молекулами лака и стали, вы вряд ли смогли бы войти в дом или выйти из него. Абстракция — один из главных способов борьбы со сложностью реального мира.

Инкапсуляция помогает управлять сложностью, блокируя доступ к ней.

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

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

Интерфейсы классов должны быть полными и минимальными.

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

Я никогда не встречал человека,
желающего читать 17 000 стра-
ниц документации, а если бы
встретил, то убил бы его, чтобы
он не портил генофонд.
Джозеф Костелло
(Joseph Costello)

Плохие новости заключаются в
том, что мы, как нам кажется,
никогда не найдем философско-
го камня. Мы никогда не най-
дем процесса, позволяющий
проектировать ПО абсолютно
рациональным образом. Но есть
и хорошая новость: мы можем
его подделать.
Дэвид Парнас
и Пол Клементс (David Parnas
and Paul Clements)

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

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

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

Изучая возможные варианты проектирования и пробуя разные подходы,
вы будете рассматривать и высокоуровневые, и низкоуровневые аспекты. Общая
картина, которую вы получаете при работе над высокоуровневыми вопросами,
поможет вам лучше понять низкоуровневые детали. Детали, которые вы узнаете
при работе над низкоуровневыми вопросами, помогут вам создать прочный фун-
дамент для принятия высокоуровневых решений. Некоторые конфликты между
высокоуровневыми и низкоуровневыми соображениями — вполне здоровое яв-
ление; это напряжение способствует созданию структуры, более стабильной, чем
структура, полностью созданная «сверху вниз» или «снизу вверх».

Многим программистам — и вообще многим людям — трудно переключаться между
высокоуровневыми и низкоуровневыми точками зрения, но эта способность —
важное условие эффективного проектирования. Занимательные упражнения, по-
зволяющие развить гибкость ума, можно найти в книге «Conceptual Blockbusting»
(Adams, 2001), описанной в разделе «Дополнительные ресурсы» в конце главы.

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

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

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

Иногда адекватность конкретного проекта невозможно
оценить, не имея дополнительных сведений о деталях реа-
лизации. Вы можете не знать, приемлема ли конкретная ор-
ганизация базы данных, пока не узнаете, будет ли она удовлетворять конкретным
требованиям к производительности. Вы можете не знать, приемлем ли проект
конкретной подсистемы, пока не будут выбраны конкретные библиотеки GUI. Это
примеры существенной «грязи» при проектировании ПО: вы не можете полностью
определить проблему проектирования, пока не решите ее хоть частично.

С другой стороны, иногда я имею дело с проектами, стра-
дающими от слишком объемной проектной документа-
ции. Здесь вступает в игру своеобразный закон Грешема , и
«механические действия начинают вытеснять творчество»
(Simon, 1965). Чрезмерное внимание к созданию проектной
документации — хорошее подтверждение этого закона. Я
бы предпочел, чтобы разработчики тратили 80% усилий на
разработку и анализ различных вариантов проектирования
и 20% — на создание менее изысканной документации, а не наоборот: 20% — на
создание посредственных решений и 80% — на совершенствование документации
не совсем удачных проектов.

Люди, описывающие проекти-
рование ПО как дисциплиниро-
ванный процесс, тратят много
энергии, заставляя всех нас по-
чувствовать себя виноватыми.
Мы никогда не станем доста-
точно структурированными или
объектно-ориентированными
для достижения нирваны при
жизни. Все мы расплачиваемся
за первородный грех — изуче-
ние Basic в особо впечатлитель-
ном возрасте. Но я готов спо-
рить, что большинство из нас
проектируют программы лучше,
чем кажется пуристам.
Ф. Дж. Плоджер
(P. J. Plauger)

«чем догматичнее вы будете в отношении методики
проектирования, тем меньше реальных проблем решите» (Plauger, 1993)

Adams, James L. Conceptual Blockbusting: A Guide to Better Ideas, 4th ed. — Cambridge,
MA: Perseus Publishing, 2001. Нельзя сказать, что эта книга посвящена непосред-
ственно проектированию ПО, но это не умаляет ее достоинств: она была написана
как учебник по проектированию для студентов инженерного факультета Стэн-
фордского университета. Даже если вы никогда ничего не проектировали и не
проектируете, в ней вы найдете увлекательное обсуждение творческого мышления
и много упражнений, позволяющих развить мышление, для эффективного проек-
тирования. Кроме того, данная книга включает список литературы, посвященной
проектированию и творческому мышлению, с подробными аннотациями. Если
вам нравится решать проблемы, вам понравится эта книга.

Plauger, P. J. Programming on Purpose: Essays on Software Design. — Englewood Cliffs,
NJ: PTR Prentice Hall, 1993. В этой книге я нашел столько хороших советов по про-
ектированию ПО, сколько во всех остальных прочитанных мной книгах вместе
взятых. Плоджер прекрасно разбирается во многих подходах к проектированию,
он прагматичен, и он отличный писатель.

Polya, G. How to Solve It: A New Aspect of Mathematical Method, 2d ed. — Princeton,
NJ: Princeton University Press, 1957. Это обсуждение эвристики и решения про-
блем концентрируется на математике, но актуально и для разработки ПО. Книга
Полья стала первым трудом, посвященным применению эвристики для решения
математических проблем. В ней проводится четкое различие между небрежной
эвристикой, используемой для обнаружения решений, и более аккуратными ме-
тодами, которые применяются для представления найденных решений. Читать ее
нелегко, но если вы интересуетесь эвристикой, то в итоге все равно прочитаете ее,
хотите вы того или нет. Полья ясно показывает, что решение проблем не является
детерминированным процессом и что приверженность единственной методоло-
гии аналогично ходьбе в кандалах. Когда-то в Microsoft эту книгу выдавали всем
новым программистам.
Michalewicz, Zbigniew, and David B. Fogel. How to Solve It: Modern Heuristics. — Berlin:
Springer-Verlag, 2000. Это обновленный вариант книги Полья, который содержит
некоторые нематематические примеры и менее требователен к читателю.
Simon, Herbert. The Sciences of the Artificial, 3d ed. — Cambridge, MA: MIT Press, 1996.
В этой интересной книге проводится различие между науками, имеющими дело
с естественным миром (биология, геология и т. д.), и науками, изучающими ис-
кусственный мир, созданный людьми (бизнес, архитектура и информатика). За-
тем в ней обсуждаются характеристики наук об искусственном, при этом особое
внимание уделяется проектированию. Книга написана в академическом стиле, и
ее следует прочитать всем, кто решил сделать карьеру в области разработки ПО
или любой другой «искусственной» области.

статьи:
Parnas, David L., and Paul C. Clements. «A Rational Design Process: How and Why to Fake
It». — IEEE Transactions on Software Engineering SE%12, no. 2 (February 1986): 251–57.
Parnas, David L. «On the Criteria to Be Used in Decomposing Systems into Modules».
— Communications of the ACM 5, no. 12 (December 1972): 1053-58.
Parnas, David L. «Designing Software for Ease of Extension and Contraction». — IEEE
Transactions on Software Engineering SE%5, no. 2 (March 1979): 128-38.
Parnas, David L., Paul C. Clements, and D. M. Weiss. «The Modular Structure of Com plex Systems».
— IEEE Transactions on Software Engineering SE%11, no. 3 (March 1985): 259-66.

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

Допустим, вы пишете программу, управляющую выводом текста на экран с использованием
разнообразных гарнитур шрифтов, их размеров и атрибутов (например,
«полужирный» и «курсив»). За работу со шрифтами отвечает конкретная часть
программы. При использовании АТД данные — названия гарнитур, размеры и
атрибуты шрифтов — будут объединены в одну группу с обрабатывающими их
методами. Набор данных и методов, служащих одной цели, — это и есть АТД.

Вудфилд, Дансмор и Шен провели исследование, участники которого —
аспиранты и студенты старших курсов факультета информатики — дол-
жны были ответить на вопросы о двух программах: одна была разделена
на восемь методов в функциональном стиле, а вторая — на восемь методов АТД
(Woodfield, Dunsmore, and Shen, 1981). Студенты, отвечавшие на вопросы о вто-
рой программе, получили на 30% более высокие оценки.

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

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

Традиционное объектноориентированное проектирование предоставляет мощные эвристические средства моделирования мира в терминах объектов, но объектный подход не помог бы вам догадаться, что идентификатор следует объявить как IdType, а не как int. Разработчик, использующий объектноориентированный подход, спросил бы: «Рассматривать ли идентификатор как объект?» В зависимости от принятых в проекте стандартов кодирования утвердительный ответ мог бы означать, что программист должен написать конструктор, деструктор, операторы копирования и присваивания, закомментировать все это и сохранить в системе управления конфигурацией. Но скорее всего программист решил бы: «Нет, не стоит создавать целый класс ради какогото идентификатора. Использую просто int».

Смотрите: эффективный вариант проектирования — простое сокрытие типа данных идентификатора — даже не был рассмотрен! Если бы вместо этого разработчик спросил: «Не скрыть ли информацию об идентификаторе?» — он, возможно, решил бы объявить собственный тип IdType как синоним int. Различие между объектноориентированным проектированием и сокрытием информации в этом примере не сводится к простому несоответствию явных правил и предписаний. Принятое в соответствии с принципом сокрытия информации решение прекрасно согласуется с объектноориентированным подходом. Вместо этого различие относится к области эвристики: размышление над сокрытием информации может указать на такие варианты проектирования, которые при использовании объектноориентированного подхода остались бы незамеченными.

Сокрытие информации может пригодиться при проектировании открытого интерфейса класса. Теория и практика проектирования классов во многом расходятся, и многие разработчики, решая, чту включить в открытый интерфейс класса, думают прежде всего об удобном интерфейсе, а это обычно приводит к раскрытию почти всей информации об устройстве класса. Опыт подсказывает мне
что некоторые программисты скорее раскрыли бы все закрытые данные класса, чем написали 10 дополнительных строк для защиты его секретов. Вопрос «Что этот класс должен скрывать?» обнажает самую суть проблемы проектирования интерфейса. Если функцию или данные можно включить в открытый интерфейс класса, не раскрыв его секретов, сделайте это. В противном случае воздержитесь от такого решения.

Почаще задавайте себе вопрос «Что мне скрыть?», и вы удивитесь, сколь ко проблем проектирования растает на ваших глазах.

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

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

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

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

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

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

Вы не имеете понятия о том, реализо ваны ли данные как float x, y и z, хранит ли класс Point эти элементы как double, преобразуя их в float, или же он хранит их на Луне и получает через спутник.

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

Если для понимания того, что происходит, нужно увидеть реализацию, это не абстракция. Ф. Дж. Плоджер (P. J. Plauger)

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

Самое важное правило объектно-ориентированного программирования на C++ таково: открытое наследование означает «является». Запомните это. Скотт Мейерс (Scott Meyers)

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

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

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

Не «переопределяйте» непереопределяемые методычлены И C++, и Java позволяют программисту переопределить непереопределяемый методчлен — ну, или чтото вроде того. Если функция объявлена в базовом классе как private, в производном классе можно создать функцию с тем же именем. Программист, изучающий код производного класса, может прийти к ложному выводу, что эта функция является полиморфной, хотя на самом деле это не так — просто у нее то же имя. Иначе сформулировать это правило можно так: «Не используйте имена непереопределяемых методов базового класса в производных классах».

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

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

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

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

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

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

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

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

Выбрав подход, придерживайтесь его неукоснительно. Если вы решили обрабатывать ошибки на высоком уровне, а в низкоуровневом коде просто сообщать о них, удостоверьтесь, что высокоуровневый код действительно их обрабатывает! Некоторые языки позволяют игнорировать возвращаемое функцией значение (в C++ вы не обязаны что-то то делать с возвращенным результатом), но не игнорируйте информацию об ошибке! Проверяйте значение, возвращаемое из функции. Даже если вы считаете, что ошибка в функции возникнуть не может, все равно проверяйте. Весь смысл защитного программирования в защите от ошибок, которых вы не ожидаете.

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

Программы, использующие исключения как часть нормальной работы алгоритма, страдают от всех проблем с читабельностью и удобством сопровождения так же, как и классический спагетти-код. Энди Хант и Дэйв Томас (Andy Hunt and Dave Thomas)

Рассмотрите альтернативы исключениям Некоторые
языки поддерживают исключения 5–10 лет и более. Однако до сих пор нет
общепринятых правил их безопасного использования.
Некоторые программисты применяют исключения для обработки ошибок только потому,
что их язык программирования предоставляет такой механизм. Вам всегда следует
принимать во внимание все возможные методы обработки ошибок:
локальную обработку ошибок, возврат кода ошибки, запись отладочной информации в файл,
прекращение работы системы и др. Обрабатывать ошибки с помощью
исключений только потому, что это позволяет язык, — классический пример
программирования на языке, а не с использованием языка

И напоследок подумайте, действительно ли вашей программе необходимо обрабатывать
исключения. Точка. Как заметил Бьерн Страуструп, иногда лучшей реакцией на
серьезную ошибку периода выполнения будет освобождение всех ресурсов и
прекращение работы. Пусть пользователь перезапустит программу с надлежащими
входными данными (Stroustrup, 1997).

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

четырехсвязный список

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

Неработающая программа обыч-
но приносит меньше вреда, чем
работающая плохо.
Энди Хант и Дэйв Томас
(Andy Hunt and Dave Thomas)

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

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

Слишком много чего-либо — это
плохо, но слишком много вис-
ки — это просто достаточно.
Марк Твен

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

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

Псевдокод упрощает внесение изменений. Что проще:
исправить линию на чертеже или снести стену и сдвинуть
ее на метр в сторону? В программировании эффект
не столь драматичен в плане физических усилий, но идея
та же: несколько строк псевдокода легче исправить, чем
страницу кода. Одна из основ успеха проекта — отловить
ошибку на «наименее значимой стадии», когда для ее исправления требуется
минимум усилий. Поиск ошибки на стадии псевдокода требует гораздо меньше
затрат, чем после полного кодирования, тестирования и отладки, так что
есть экономический стимул обнаружить ошибку как можно раньше.

Псевдокод как инструмент проектирования трудно переоценить. Исследования
показали, что программисты предпочитают псевдокод за его возможности
упрощать создание программных конструкций, помощь в
определении некорректных проектных решений, простоту документирования и
внесения изменений (Ramsey, Atwood, and Van Doren, 1983). Псевдокод — не единственный
инструмент детального проектирования, но наряду с ППП — полезная
вещь в инструментарии программиста.

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

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

1. Формулирование проблемы.
- самые дорогие ошибки - самые ранние
- не определив проблему можно решить не ту проблему)
2. Выработка требований.
- цена изменений (график и смета)
- контроль изменений (стенд, процедура)
- подходы, позволяющие легко меняться
- насколько должно стать хуже, чтобы оставить проект и чем это отличается от сейчас.
- бизнес-модель, коммерческие следствия решений
- чеклист
3. Архитектура.
- Цена ошибок и изменений сопоставима с требованиями.
- небольшие изменения способны приводить к большим отложенным последствиям.
- были рассмотрены alt-варианты и обоснован текущий выбор.
- обоснование проекта.
- основные классы
- организация данных
- бизнес правила
- гуи
- управление ресурсами
- безопасность
- производительность
- масштабируемость
- коммуникация и взаимодействие
- локализация
- и/о
- обработка ошибок
- отказоустойчивость
- возможность реализации
- избыточная функциональность
- чем лучше покупного. или купить?
- повторное использование
- стратегия изменений
- независимость от языка и платформы
убережет от чрезмерного проектирования
- риски
- баланс
- planguage
- чеклист
- книги с 56 стр.
4. Конструирование
- Язык, Конвенции, Технологии.
- методики конструирования
- чеклист
5. Проектирование
- грязная невидимка
- перебор ошибок
- ограничение ресурсов
- недетерминированность, эвристика и постепенность.
- управление сложностью
- проектирование - объединение конкурирующих целей в удачном наборе компромиссов.
5.1 Целевые ТТХ:
- минимальная сложность
- простота сопровождения
- слабое сопряжение
- расширяемость
- повторное использование
- max fan-in, low fan-out
- портируемость
- ничего лишнего
- стратификация, прокладки между новым и старым кодом
- стандартизация.
5.2 Уровни проектирования (декомпозиции):
5.2.1 - программная система
5.2.2 - компоненты или подсистемы (пакеты)
- - ограничение отношений между пакетами
- - ацикличность отношений
Частые компоненты:
- Бизнес-логика (законы/правила)
- UI
- БД (изоляция БД!)
- взаимодействие с ОС
5.3 Компоненты проектирования, эвристика.
- объекты и их атрибуты (методы и данные)
- возможные действия над объектом
- возможные действия объектов
- видимые части объектов
- интерфейсы объектов
- абстракция как управление сложностью
- наследование - палка о двух концах
- инкапсуляция как запрет усложнения
- Сокрытие информации
- - сокрытие сложности
- - сокрытие источников изменений
5.3.4 Области вероятных изменений
- Определить элементы риска
- Отделить элементы риска
- Изолировать стабильные элементы
Группы риска:
- бизнес-правила (законы, контракты)
- hardware-зависимости
- I/O, форматы файлов
- нестандартные возможности языка
- сложные аспекты конструирования
- Переменные статуса
- - перечисления вместо булевых
- - методы доступа вместо прямого чека
- размеры структур. Скрывать их.
-- Предвосхищение изменений
- Определение ядра (минимальной функциональности)
5.3.5 Слабое сопряжение (loose coupling)
- объем (количество сопряжений в методах и классах)
- видимость (очевидность) списки - круто, паблики - плохо.
- гибкость (легкозаменяемость) - чем проще вызвать модуль, тем лучше
Виды сопряжений:
- параметром (наилучшее)
- создаваемым объектом (тож ничо)
- чужим объектом (уже херовенько)
- семантика (жопа начинается):
- - передача флага (еще терпимо)
- - пабликом (и сразу пиздец!)
- - Перекрестный вызов элементов цепи без проверки порядка
- - Частичная инициализация "под потребителя"
- - Подделка объекта под потребителя
5.3.5.1 Шаблоны проектирования
- Дают готовые абстракции
- Стандартизируют коммуникацию
- Стандартизируют подход
-- Ловушки шаблонов:
- - Чрезмерная адаптация под шаблон
- - Нецелесообразное применение шаблонов
5.3.5.2 Другие принципы:
- связность и целостность
(сфокусированность элементов на цели)
- иерархии абстракций
- Контракты классов
- - Предусловия и постусловия (preconditions/postconditions)
- сферы ответственности
- проектирование для тестирования
- концентрация на угрозах, а не копировании прошлых успехов
- время связывания (binding time)
- центральные точки управления
- грубая сила (последовательность вместо бинарности)
- рисуйте диаграммы (планы, схемы, etc)
- поддерживайте модульность
- диверсифицируйте подходы
- не давите через силу
5.4 Методики проектирования
- итерация проектирования
- разделение проектирования
- top-down and bottom-up
- декомпозиция
- экспериментальное прототипирование
5.4.1 Регистрация проектирования
- включайте в код
- юзайте Wiki (хорошо для удаленки)
- резюме почтой
- фото вместо рисунков
- плакаты со схемами
- карточки "класс-отв-сотр"
- UML- диаграммы
- достаточность проектирования
- чеклист
6.1 АТД
- Возможность сокрытия
- ограничение области изменений
- информативность интерфейса (и кода)
- легкость оптимизации
- легкость проверки кода
- легкочитаемость кода
- ограничение области использования данных
- - низкоуровневые типы данных
- - часто используемые объекты
- - простые элементы
- - отделять от среды хранения
6.2 Интерфейсы классов
- согласованнные абстракции в интерфейсе класса
- класс как механизм реализации АТД
- один класс на один АТД
- обратный метод сразу
- лишнее в другие классы
- убирать семантику!
- беречь целостность интерфейса
- не добавлять несогласованные элементы
- связность = абстракция, но абстракция важнее
- только инкапсуляция
- макс защиты, мин данных всегда
- не уверен - закрой
- уверен - спрячь
- ничего закрытого в интерфейсе
- не предполагать клиентов
- не предполагать собственное использование
- избегать юзать друзей
- не открывай только из-за открытых частей
- и вообще, не открывай
- чтение важней написания
- бой семантике!
- сопряжение = абстракция = инкапсуляция
6.3 Проектирование классов
- Включение (отношение «содержит»)
- - закрытое наследование - крайний случай
- - 7+-2 данных-членов
- Наследование (отношение «является»)
- - Проектируй с учетом либо запрещай
- - LSP
6.3.1 Виды наследования:
- - абстрактный (только интерфейс)
- - переопределяемый (реализация с изменениями)
- - непереопределяемый (изменения запрещены)
- непереопределяй непереопределяемое!
- максимально высокий уровень иерархии наследования
- класс с одним объектом - это просто объект
- только один производный класс не нужен.
- не переопределять "в пустой"
- многоуровневые иерархии повышают сложность
- не более 7+-2 подклассов
- не более 2-3 уровней иерархии
- полиморфизм
- защищенные методы доступа
- Избегать множественного наследования
- Кроме Mix-in-ов
- меньше методов, сопряжения, наследования, иерархий на класс
- баланс между всем этим
- приватный конструктор или оператор присваивания
- Блок неявно сгенерированных методов и операторов
- минимум fan-out
- «Правило Деметры (Law of Demeter)»
- Инициализировать все данные-члены во всех конструкторах
- классы-одиночки закрытым конструктором
- deep copy better
- but shallow copy faster
6.4 Разумные причины создания классов
- моделирование объектов реального мира;
- моделирование абстрактных объектов;
- снижение сложности;
- изоляция сложности;
- сокрытие деталей реализации;
- ограничение влияния изменений;
- сокрытие глобальных данных;
- упрощение передачи параметров в методы;
- создание центральных точек управления;
- облегчение повторного использования кода;
- планирование создания семейства программ;
- упаковка родственных операцией;
- выполнение специфического вида рефакторинга.
Херовые причины создания классов:
- «божественные» классы
- "нерелевантные" (данные без поведения)
- "глагольные" (поведение без данных)
- Чеклист
7. Методы
- снижение сложности;
- формирование понятной промежуточной абстракции;
- предотвращение дублирования кода;
- поддержка наследования;
- сокрытие очередности действий;
- сокрытие операций над указателями;
- улучшение портируемости;
- упрощение сложных булевых проверок;
- повышение быстродействия.
- изоляция сложности;
- сокрытие деталей реализации;
- ограничение влияния изменений;
- сокрытие глобальных данных;
- создание центральных точек управления;
- облегчение повторного использования кода;
- выполнение специфического вида рефакторинга;
- подготовка к расширению.
7.2 Связность
- Функциональная (операционная)
- Последовательная (sequential cohesion)
- Коммуникационная (по данным)
- Временая (по интервалу времени)
Херовые связности:
- Процедурная (порядок ввода)
- Логическая ("нелогичная")
(годен в случае координационного)
- Случайная (coincidental) - треш как он есть
7.3 Имена методов
- Все выходные данные и побочки
- Избегать неоднозначных глаголов
- И номеров
- Оптимальная длина имено 9-15
- Функции - описание выходного значения
- Процедуре - действие (без объекта!)
- Дисциплинированные антонимы
- Конвенции именования частых операций
7.4 - 50-150, максимум - 200
7.5 Параметры методов
- In - modify - out
- свои in & out
- const для in
- передача в согласованном порядке
- передавать только нужное
- статус/коды ошибок последнием
- не использовать параметры в качестве рабочих (const for in)
- Документировать предположения
- - изменяемость (in - modify - out)
- - единицы измерения (дюймы-футы-метры-etc)
- - коды статусов и ошибок
- - диапазоны допустимых значений
- - значения, не должные поступать в метод
- число параметров метода - 7+-2
- конвенции именования параметров (In - modify - out)
- Передавать объекты согласно абстракции интерфейса
- подготовка и разбор объекта после вызова - признак херового проектирования
- именование параметров - способ избежать неверного сопоставления
- факт должен соответствовать dummy
7.6 Фуенкии персонально
- Функция если возврат значения
- проверяй все возможные пути возврата
- инициализируй умолчание на старте как страховку
- не возвращать локальные данные
7.7 Макросы
- в скобки все, что можно
- больше скобок!
- кончилиськруглые - ебашь фигурные!
- готовить макросы к замене на методы
- называть как методы-
Ограничения:
- const
- inline
- min, max
- enum
- typedef
8. Защитное программирование.
8.1 Входные данные.
- Внешние источники.
- - допустимый интервал
- - разрешенные значения
- - длина строк
- - переполнение буфера
- - SQL, HTML, XML-внедрения
- - переполнение типов данных
- Параметры метода
8.2 Assertions (утверждения)
- интервал параметра
- состояние файла/потока на старте/финише
- указатель файла/потока находится в начале/конце на старте/финише
- файл/поток открыт на запись/чтение/все на старте/финише
- значение in не изменяется в методе
- указатель не нулевой
- контейнер может вместить минимум Х элементов
- таблица инициализирована для помещения реальных значений
- контейнер пуст/полон на старте/финише
- результат сложного быстрого модуля равен результату простого и быстрого
8.2.1 Принципы утверждений
- ошибки для ожидаемого, утверждения для того, что не должно быть
- утверждение как исполняемая документация
- требует рекомпиляции и исправления
- никакого исполняемого кода в утверждениях
- для проверки пред- и постусловий из внутренних (доверенных) условий
- проверки ошибок нужны все равно, вместе с утверждениями
8.3 Обработка ошибок:
- нейтральное значение.
- следующий корректный блок
- повторить прошлый результат
- ближайшее допустимое значение
- запись в журнал
- код ошибки
- - статусная переменная
- - статус в ретарне
- - исключение средствами языка (ху!)
- централизованный обработчик ошибок (небезопасно: раскрывает адрес всех ОО)
- немедленное сообщение (нарушает целостность, инкапсуляцию, информирует взломщика)
- обрабатывать локально (плохо самодурством в команде, идеально при единоличности)
- терминация (при высокой ответственности)
8.3.1 Устойчивость против корректности:
- - корректность для риска,
- - устойчивость для остального.
8.3.2 Обработка Ошибок:
- единая стратегия обработки
- уровень архитектуры и проекта
- смысл защитного программирования - нежданчики.
8.4 Исключения
- умыватель рук (передает ошибку вон)
- может увеличивать сложность
- и вообще херовый
- оповещение других частей о критике
- исключения нельзя пропустить (не заметить или игнорировать)
- только для действительной критики
- только если НЕЛЬЗЯ реализовать иначе
- только для ошибок которые НИКОГДА не должны происходить
- нарушают инкапсуляцию
- и вообще не надо их
- ОСОБЕННО в деструкторах и конструктрах
- в исключениях тоже надо соблюдать абстракцию
- в описании все, что вызвало исключение (годные значения и текущее)
- не оставлять catch пустым
- в крайнем случае - писать, почему пустой
- библиотечные исключения: узнать и обработать
- централизованный генератор сообщений
- и да, это - херовая идея
- стандартизация исключений
- объекты от базового класса Exception
- собственный класс исключений
- определить точно throw-catch для локальной обработки ошибок
- определить точно исключения, не перехватываемые локально
- будет ли централизованный генератор сообщений
- допускаются ли исключения в конструкторах и деструкторах
- альтернативы лучше%)
8.5. Изоляция повреждений
- оболочка безопасного кода
- преобразование чем раньше
- снаружи - ошибки, внутри - утверждения
- выбор границ на уровне архитектуры
8.6 Средства отладки
- не юзать промышленную версию как отладочную
- отладочные режимы, меню и функции - отдельно
- поддержка отладки как можно раньше
8.6.1 Наступательное программирование
- ошибки при разработке очевидны
- в промышленном коде позволяют продолжать работу
- утверждения завершают работу
- заполнять любую выделенную память (ошибки выделения памяти)
- заполнять все файлы и потоки (ошибки файловых форматов)
- проверка всех default или else всех операторов на реакцию
- заполнять объекты мусором перед самым их удалением
- отправка журналов ошибок email\etc
8.6.2 Удаление отладочных средств
- инструменты ant и make
- встроенный препроцессор
- #define DEBUG
- многоуровневый отладочный код
- собственный препроцессор
- отладочные заглушки
- защитного программирования в промышленной версии
8.7 Защитный код релиза
- оставлять код существенных ошибкок
- убирать код мелких ошибок
- если он мал - писать в журнал
- неприемлем краш-код, приводящий к потерям данных
- оставлять код аккуратного завершения работы
- код регистрации ошибок
- дружелюбные сообщения + контакты для обратной связи
8.8 Защита от защиты
- избыточные проверки замедляют и усложняют код.
- Чеклист
9. Процесс программирования с псевдокодом
9.1 Этапы создания классов и методов
- Общая структура класса:
- - функции класса
- - секреты класса
- - точный уровень абстракции
- - наследование и наследники
- - открытые методы класса
- - нетривиальные структуры данных
- Методы класса:
- - дополнительные методы
- - пересмотр общей структуры класса
- Оценка и тестирование всего класса
- Этапы построения метода
- - проектирование
- - проверка структуры
- - кодирование
- - проверка кода
9.2. Псевдокод
- формулировки, в точности описывающие отдельные действия
- избегать синтаксиса яп
- псевдокод на уровне намерений
- - назначение подхода, а не то, как этот подход нужно реализовать
- - достаточно низкий уровень
- Выгода псевдокода:
- - упрощает пересмотр конструкции
- - поддерживает идею итеративного усовершенствования
- - упрощает внесение изменений
- - упрощает комментирование программ
- - сопровождать проще, чем другие виды проектной документации
9.3. Конструирование методов с использованием ППП
- Проектирование метода:
- - Проверка предварительных условий
(функции метода четко определены и соответствуют общим проектным решениям)
- Задача, решаемая методом:
- - скрываемая информация
- - входные данные
- - выходные данные
- - предусловия (входные значения, потоки, файлы, буферы)
- - постусловия (выходные значения, потоки, файлы, буферы)
- Название - недвусмысленное выразительное имя.
- Решить, как тестировать метод
- стандартные библиотеки
- стратегия обработки ошибок
- конструкционная эффективность
- микрооптимизация после исчерпания конструкции
- поиск чужих алгоритмов
- псевдокод
- применение данных
- проверка псевдокода
- итерации псевдокодов
- - Кодирование метода
- объявление метода
- крайние операторы
- псевдокод в комментарии
- код под комментариями
- проверка на декомпозицию:
- - выделение в метод
- - разбиение псевдокода
- тесты
- исправления
запись создана: 19.11.2016 в 18:42

@темы: Чужое, Для Мозга, TechDiv, SoftDiv