Файл: Руководство по стилю программирования и конструированию по.pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 30.11.2023
Просмотров: 781
Скачиваний: 2
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
Иерархии помогают в достижении Главного Технического Императива Разработ- ки ПО, позволяя сосредоточиться только на том уровне детальности, который
ГЛАВА
5 Проектирование при конструировании
103
заботит вас в конкретный момент. Иерархия не устраняет детали — она просто выталкивает их на другой уровень, чтобы вы могли думать о них, когда захотите, а не все время.
Формализуйте контракты классов
На более детальном уровне полезную информацию можно получить, рассматривая интерфейс каждого класса как кон- тракт с остальными частями программы. Обычно контракт имеет форму «Если вы обещаете предоставить данные x, y и z и гарантируете, что они будут иметь характеристики a, b и c, я обязуюсь выполнить операции 1, 2 и 3 с ограничениями
8, 9 и 10». Обещания клиентов классу обычно называются
предусловиями (preconditions), а обязательства класса перед клиентами — по-
стусловиями (postconditions).
Контракты помогают управлять сложностью, потому что хотя бы теоретически объект может свободно игнорировать любое поведение, не описанное в контракте.
На практике этот вопрос куда сложнее.
Грамотно назначайте сферы ответственности
Еще один эвристический принцип — обдумывание сфер ответственности, которые следует назначить объектам. Рассмотрение сферы ответственности объекта ана- логично вопросу о том, какую информацию он должен скрывать, но мне кажется, что первый способ может привести к более общим ответам, чем и объясняется уникальность этого эвристического принципа.
Проектируйте систему для тестирования
На некоторые интересные идеи можно натолкнуться, спросив, как будет выглядеть система, если спроектировать ее для обеспечения максимальной легкости тестиро- вания. Отделять ли пользовательский интерфейс от остальной части программы, чтобы протестировать его независимо? Организовывать ли каждую подсистему так, чтобы минимизировать ее зависимость от других подсистем? Проектирование для тестирования часто приводит к разработке более формализованных интерфейсов классов, что обычно выгодно.
Избегайте неудач
Профессор гражданского строительства Генри Петроски в интересной книге
«Design Paradigms: Case Histories of Error and Judgment in Engineering» (Petroski,
1994), посвященной истории неудач в отрасли проектирования мостов, утверждает, что многие известные мосты рушились из-за чрезмерного внимания к прошлым успехам и неадекватного рассмотрения возможных причин аварий. Он делает вы- вод, что аварий вроде крушения моста Tacoma Narrows можно было бы избежать, если б инженеры тщательно рассматривали возможные причины аварий, а не просто копировали другие успешные проекты.
Крупные бреши в защите многих известных систем, обнаруженные в прошедшие годы, заставляют подумать о том, как применить идеи Петроски в области про- ектирования ПО.
Перекрестная ссылка О контрак- тах см. подраздел «Используйте утверждения для документиро- вания и проверки предусловий и постусловий» раздела 8.2.
104
ЧАСТЬ
II
Высококачественный код
Тщательно выбирайте время связывания
Временем связывания (binding time) называют тот момент, когда переменной присваивается конкретное значение.
Раннее связывание обычно упрощает код, но и снижает его гибкость. Иногда к полезным идеям проектирования можно прийти, спросив себя: «Что, если связать эти значения раньше? Что, если связать их позже? Что, если инициализировать эту таблицу в этом месте кода? Что, если получить значение этой переменной от пользователя в период выполнения про- граммы?»
Создайте центральные точки управления
Ф. Дж. Плоджер говорит, что главным его принципом является «Принцип Одного
Верного Места: в программе должно быть Одно Верное Место для поиска нетри- виального фрагмента кода и Одно Верное Место для внесения вероятных изме- нений» (Plauger, 1993). Управление может быть централизовано в классах, мето- дах, макросах препроцессора, файлах, включаемых директивой
#include, — даже именованная константа может быть центральной точкой управления.
Этот принцип также способствует снижению сложности: если какой-то программ- ный элемент встречается в минимальном числе фрагментов, его изменение ока- жется проще и безопаснее.
Подумайте об использовании грубой силы
Грубая сила — один из мощнейших эвристических инстру- ментов. Не стоит ее недооценивать. Работоспособное реше- ние проблемы методом грубой силы лучше, чем элегантное, но не работающее решение. Создавать элегантные решения зачастую долго и сложно. Так, описывая историю разработки алгоритмов поиска, Дональд Кнут указал, что, хотя первое описание алгоритма двоичного поиска было опубликовано в 1946 г., алгоритм, правильно обрабаты- вающий списки всех размеров, был разработан только спустя 16 лет (Knuth, 1998).
Двоичный поиск элегантнее, но и основанный на грубой силе последовательный поиск часто приемлем.
Рисуйте диаграммы
Диаграммы — еще один мощный эвристический инструмент. Все знают, что лучше один раз увидеть, чем сто раз услышать. Диаграммы позволяют представить про- блему на более высоком уровне абстракции, и никакие описания их не заменят.
Помните: иногда проблему следует рассматривать на детальном уровне, а иногда целесообразно иметь дело с более общими аспектами.
Поддерживайте модульность проекта системы
Этот принцип подразумевает, что каждый метод или класс должен быть похож на «черный ящик»: вы знаете, что в него поступает и что из него выходит, но не знаете, что происходит внутри. Черный ящик имеет такой простой интерфейс и такую ясную функциональность, что для любых конкретных входных данных можно точно предсказать соответствующие выходные данные.
Перекрестная ссылка О времени связывания см. раздел 10.6.
Если сомневаетесь, используйте грубую силу.
Батлер Лэмпсон
(Butler Lampson)
ГЛАВА
5 Проектирование при конструировании
105
Концепция модульности связана с сокрытием информации, инкапсуляцией и дру- гими эвристическими принципами проектирования. И все же размышление о том, как собрать систему из набора черных ящиков иногда приводит к догадкам, к ко- торым сокрытие информации и инкапсуляция привести не могут, так что принцип модульности заслужил право занять место в вашем арсенале полезных идей.
Резюме эвристических принципов проектирования
Ниже приведен список основных эвристических принципов проектирования:
쐽
определите объекты реального мира;
쐽
определите согласованные абстракции;
쐽
инкапсулируйте детали реализации;
쐽
используйте наследование, когда это возможно;
쐽
скрывайте секреты (помните про сокрытие информации);
쐽
определите области вероятных изменений;
쐽
поддерживайте сопряжение слабым;
쐽
старайтесь использовать популярные шаблоны проекти- рования.
Следующие эвристические принципы также иногда бывают полезны:
쐽
стремитесь к максимальной связности;
쐽
формируйте иерархии;
쐽
формализуйте контракты классов;
쐽
грамотно назначайте сферы ответственности;
쐽
проектируйте систему для тестирования;
쐽
избегайте неудач;
쐽
тщательно выбирайте время связывания;
쐽
создайте центральные точки управления;
쐽
подумайте об использовании грубой силы;
쐽
рисуйте диаграммы;
쐽
поддерживайте модульность проекта системы.
Советы по использованию эвристических принципов
Подходы к проектированию ПО могут быть основаны на подходах, применяемых в других областях. Одной из первых книг, посвященных использованию эвристики при решении проблем, является «How to Solve It (Как решать задачу)» Д. Полья (Polya, 1957).
Обобщенный подход Полья к решению проблем концентрируется на решении мате- матических задач. Он резюмирован на рис. 5-10 (шрифт оригинала сохранен).
Больше беспокоит то, что про- граммист вполне может выпол- нить ту же задачу двумя или тремя способами: иногда нео- сознанно, но довольно часто просто ради изменения или же создания элегантной вариации.
А. Р. Браун и У. А. Сэмпсон
(A. R. Brown and
W. A. Sampson)
http://cc2e.com/0592
106
ЧАСТЬ
II
Высококачественный код
Рис. 5-10. Д. Полья разработал подход к решению математических задач, который
полезен и при решении проблем, связанных с проектированием ПО (Polya 1957)
Одним из самых ценных советов, которые можно дать по поводу проектирова- ния ПО, является использование разных подходов. Если проект, разработанный с помощью UML, неудачен, выразите его на обычном языке. Напишите небольшую тестовую программу. Попробуйте совершенно другой подход. Подумайте об ис- пользовании грубой силы. Продолжайте рисовать эскизы и наброски карандашом, и мозг последует за рукой. Если ничего не выходит, отложите решение проблемы.
ГЛАВА
5 Проектирование при конструировании
107
Прежде чем возвращаться к работе над ней, прогуляйтесь, подумайте о чем-то другом. Довольно часто это приводит к более быстрому получению нужного ре- зультата, чем простое упорство.
Никто не заставляет вас разработать весь проект за раз. Если натолкнетесь на препятствие, подумайте, обладаете ли вы информацией, достаточной для решения всех специфических проблем? Зачем через силу разрабатывать оставшиеся 20% проекта, если впоследствии они прекрасно станут на свое место? Зачем принимать неудачные решения, основанные на недостаточной информации, если позже можно будет принять более подходящие решения? Некоторые разработчики чувствуют дискомфорт, если не могут создать полный проект программы к окончанию этапа проектирования, но после создания нескольких удачных программ без заблаго- временного решения всех вопросов на этапе проектирования такая ситуация начинает казаться вполне естественной (Zahniser, 1992; Beck, 2000).
5.4. Методики проектирования
Предыдущий раздел был посвящен эвристическим принципам, связанным с атри- бутами проектов. Иначе говоря, эти принципы характеризуют то, каким должен быть завершенный проект приложения. В этом разделе мы рассмотрим эвристиче- ские
методики проектирования — действия, которые часто приводят к хорошим результатам.
Используйте итерацию
Возможно, у вас были случаи, когда вы так много узнали во время написания про- граммы, что желали бы написать ее заново, опираясь на полученные знания. Этот же феномен наблюдается и при проектировании, но этап проектирования короче, тогда как влияние, оказываемое им на последующие этапы, выражено сильнее, поэтому вы вполне можете выполнить этап проектирования несколько раз.
Проектирование — итеративный процесс. Выйдя из точки А и достигнув точки Б, не останавливайтесь, а вернитесь в точку А.
Изучая возможные варианты проектирования и пробуя разные подходы, вы будете рассматривать и высокоуровневые, и низкоуровневые аспекты. Общая картина, которую вы получаете при работе над высокоуровневыми вопросами, поможет вам лучше понять низкоуровневые детали. Детали, которые вы узнаете при работе над низкоуровневыми вопросами, помогут вам создать прочный фун- дамент для принятия высокоуровневых решений. Некоторые конфликты между высокоуровневыми и низкоуровневыми соображениями — вполне здоровое яв- ление; это напряжение способствует созданию структуры, более стабильной, чем структура, полностью созданная «сверху вниз» или «снизу вверх».
Многим программистам — и вообще многим людям — трудно переключаться между высокоуровневыми и низкоуровневыми точками зрения, но эта способность — важное условие эффективного проектирования. Занимательные упражнения, по- зволяющие развить гибкость ума, можно найти в книге «Conceptual Blockbusting»
(Adams, 2001), описанной в разделе «Дополнительные ресурсы» в конце главы.
108
ЧАСТЬ
II
Высококачественный код
Если первая попытка создания проекта кажется вполне удач- ной, не останавливайтесь! Вторая попытка почти всегда оказывается лучше первой, и при каждой попытке вы буде- те узнавать что-то такое, что поможет вам улучшить общий проект. Говорят, что, когда Томаса Эдисона, который пытался создать нить лампочки и испробовал на тот момент уже тысячу разных материа- лов, спросили, не жалеет ли он о том, что зря потратил время, так ничего и не обнаружив, Эдисон ответил: «Ни в коей мере. Я обнаружил тысячу вариантов, ко- торые не работают». Во многих случаях, решив проблему при помощи одного подхода, вы получите знания, которые позволят решить проблему иным, более эффективным способом.
Разделяй и властвуй
Как указал Эдсгер Дейкстра, никто не обладает умом, способным вместить все детали сложной программы. То же можно сказать и о проектировании. Разделите программу на разные области и спроектируйте их по отдельности. Если, работая над одной из областей, вы попадете в тупик, вспомните про итерацию!
Инкрементное улучшение — мощное средство управления сложностью. Вспомни- те, как Полья советовал решать математические задачи: поймите задачу, составьте план решения, осуществите план и
оглянитесь назад, чтобы лучше понять, что и как вы сделали (Polya, 1957).
Нисходящий и восходящий подходы к проектированию
Слова «нисходящий» и «восходящий» могут казаться устаревшими, но они предо- ставляют много ценной информации об объектно-ориентированных способах проектирования. Нисходящее (top-down) проектирование начинается на высоком уровне абстракции. Например, вы сначала определяете базовые классы или другие неспецифические элементы проекта. По ходу работы вы повышаете уровень де- тальности и определяете производные классы, сотрудничающие классы и другие детали.
Восходящее (bottom-up) проектирование начинается со специфики и постепенно переходит ко все большей общности. Как правило, оно начинается с определения конкретных объектов, на основе которых затем разрабатываются более общие объединения объектов и базовые классы.
Некоторые разработчики утверждают, что лучше всего начинать с общего и дви- гаться по направлению к частному, а другие — что общие принципы проектиро- вания нельзя определить, не обдумав важных деталей. Аргументы обеих сторон описаны ниже.
Аргументы в пользу нисходящего проектирования
Нисходящее проектирование основано на том факте, что человеческий мозг в каждый конкретный момент времени может работать только с определенным объемом информации. Если вы начинаете проектирование с общих классов, вы- полняя их декомпозицию на более специализированные классы шаг за шагом, мозгу не приходится иметь дело со слишком большим числом деталей сразу.
Перекрестная ссылка Безопас- ный способ попробовать разные варианты кода предоставляет рефакторинг (глава 24).