Файл: Руководство по стилю программирования и конструированию по.pdf
ВУЗ: Не указан
Категория: Не указан
Дисциплина: Не указана
Добавлен: 30.11.2023
Просмотров: 810
Скачиваний: 2
ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
СОДЕРЖАНИЕ
ГЛАВА
5 Проектирование при конструировании
77
Как указал Дейкстра, сложность современного ПО обусловлена самой его приро- дой, поэтому, как бы вы ни старались, вы все равно столкнетесь со сложностью, присущей самой проблеме реального мира. Исходя из этого, можно предложить двойственный подход к управлению сложностью:
쐽
старайтесь свести к минимуму объем существенной сложности, с ко- торым придется работать в каждый конкретный момент времени;
쐽
сдерживайте необязательный рост несущественной сложности.
Как только вы поймете, что все остальные технические цели разработки ПО вто- ричны по отношению к управлению сложностью, многие принципы проектиро- вания окажутся простыми.
Желательные характеристики проекта
Высококачественные проекты программ имеют несколько общих характеристик. Если вы сумеете достичь всех этих целей, ваш проект на самом деле будет очень хорош. Не- которые цели противоречат другим, но это и есть одна из задач проектирования — объединение конкурирующих целей в удачном наборе компромиссов. Некоторые аспек- ты качества проекта — надежность, производительность и т. д. — описывают и качество программы, тогда как другие являются внутренними характеристиками проекта.
Вот список таких внутренних характеристик проекта.
Минимальная сложность В силу только что описанных причин главной целью проектирования должна быть ми- нимизация сложности. Избегайте создания «хитроумных» проектов: как правило, их трудно понять. Вместо этого соз- давайте «простые» и «понятные» проекты. Если при работе над отдельным фраг- ментом программы проект не позволяет безопасно игнорировать большинство остальных фрагментов, он неудачен.
Простота сопровождения Проектируя приложение, не забывайте о програм- мистах, которые будут его сопровождать. Постоянно представляйте себе вопросы, которые будут возникать у них при взгляде на создаваемый вами код. Думайте о таких программистах как о своей аудитории и проектируйте систему так, чтобы ее работа была очевидной.
Слабое сопряжение Слабое сопряжение (loose coupling) предполагает сведение к минимуму числа соединений между разными частями программы. Для проек- тирования классов с минимальным числом взаимосвязей используйте принципы адекватной абстракции интерфейсов, инкапсуляцию и сокрытие информации.
Это позволит максимально облегчить интеграцию, тестирование и сопровожде- ние программы.
Расширяемость Расширяемостью системы называют свойство, позволяющее улучшать систему, не нарушая ее основной структуры. Изменение одного фраг- мента системы не должно влиять на ее другие фрагменты. Внесение наиболее вероятных изменений должно требовать наименьших усилий.
Работая над проблемой, я ни- когда не думаю о красоте. Я думаю только о решении про- блемы. Но если полученное ре- шение некрасиво, я знаю, что оно неверно.
Р. Бакминстер Фуллер
(R. Buckminster Fuller)
Перекрестная ссылка Эти ха- рактеристики связаны с общими атрибутами качества ПО (см. раздел 20.1).
78
ЧАСТЬ
II
Высококачественный код
Возможность повторного использования Проектируйте систему так, чтобы ее фрагменты можно было повторно использовать в других системах.
Высокий коэффициент объединения по входу При высоком коэффициенте объединения по входу (fan-in) к конкретному классу обращается большое число других классов. Это значит, что система предусматривает интенсивное использо- вание вспомогательных низкоуровневых классов.
Низкий или средний коэффициент разветвления по выходу Это означает, что конкретный класс обращается к малому или среднему числу других классов.
Высокий коэффициент разветвления по выходу (fan-out) (более семи) говорит о том, что класс использует большое число других классов и, возможно, слишком сложен. Ученые обнаружили, что низкий коэффициент разветвления по выходу выгоден как в случае вызова методов из метода, так и в случае вызова методов из класса (Card and Glass, 1990; Basili, Briand, and Melo, 1996).
Портируемость Проектируйте систему так, чтобы ее можно было легко адап- тировать к другой среде.
Минимальная, но полная функциональность Этот аспект подразумевает от- сутствие в системе лишних частей (Wirth, 1995; McConnell, 1997). Вольтер говорил, что книга закончена не тогда, когда в нее больше нечего добавить, а когда из нее ничего нельзя выбросить. При разработке ПО это верно вдвойне, потому что до- полнительный код необходимо разработать, проанализировать, протестировать, а также пересматривать при изменении других фрагментов программы. Кроме того, в будущих версиях приложения придется поддерживать обратную совместимость с дополнительным кодом. Опасайтесь вопроса: «Эту функцию реализовать легко
— почему бы этого не сделать?»
Стратификация Под стратификацией понимают разделение уровней декомпо- зиции, позволяющее изучить систему на любом отдельном уровне и получить при этом согласованное представление. Проектируйте систему так, чтобы ее можно было изучать на отдельных уровнях, игнорируя другие уровни.
Например, если вы создаете современную систему, кото- рая должна использовать большой объем старого, плохо спроектированного кода, напишите уровень, отвечающий за взаи модействие со старым кодом. Спроектируйте этот уровень так, чтобы он скрывал плохое качество старого кода, предоставляя более новым уровням согласованный набор сервисов. Пусть осталь- ные части системы работают с этими классами вместо старого кода. Такой подход сулит два преимущества: 1) он изолирует плохой код и 2) если вы когда-нибудь решите выбросить старый код или выполнить его рефакторинг, вам не придется изменять новый код за исключением промежуточного уровня.
Соответствие стандартным методикам Чем экзо- тичнее система, тем сложнее будет другим программистам понять ее. Попытайтесь придать всей системе привычный для разработчиков облик, применяя стандартные популяр- ные подходы.
Перекрестная ссылка О работе со старыми системами см. раз- дел 24.5.
Перекрестная ссылка Об осо- бенно полезном типе стратифи- кации — применении шаблонов проектирования — см. подраз- дел «Старайтесь использовать популярные шаблоны проекти- рования» раздела 5.3.
ГЛАВА
5 Проектирование при конструировании
79
Уровни проектирования
Проектирование программной системы требует нескольких уровней детальности.
Некоторые методы проектирования используются на всех уровнях, а другие только на одном-двух (рис. 5-2).
Рис. 5-2. Уровни проектирования программы. Систему (1) следует разделить
на подсистемы (2), подсистемы — на классы (3), а классы — на методы и данные
(4); методы также необходимо спроектировать (5)
Уровень 1: программная система
Первому уровню проектирования соответствует вся система.
Некоторые программисты с системного уровня сразу пере- ходят к проектированию классов, но обычно целесообразно обдумать более высокоуровневые комбинации классов, такие как подсистемы или пакеты.
Уровень 2: разделение системы на подсистемы
или пакеты
Главный результат проектирования на этом уровне — определение основных подсистем. Подсистемы могут быть
Иными словами — и это неиз- менный принцип, на котором основан всегалактический успех всей корпорации, — фундамен- тальные изъяны конструкции ее товаров камуфлируются их внешними изъянами.
Дуглас Адамс
(Douglas Adams)
80
ЧАСТЬ
II
Высококачественный код довольно крупными, такими как модуль работы с базами данных, модули GUI, бизнес-правил или создания отчетов, интерпретатор команд и т. д. Суть проек- тирования на данном уровне заключается в разделении программы на основные подсистемы и определении взаимодействий между подсистемами. Обычно этот уровень нужен при работе над любыми проектами, требующими более нескольких недель. При проектировании отдельных подсистем можно применять разные под- ходы: выбирайте тот, который кажется вам оптимальным в каждом конкретном случае. На рис. 5-2 данный уровень проектирования обозначен цифрой 2.
Особенно важный аспект этого уровня — определение правил взаимодействия под- систем. Если все подсистемы могут взаимодействовать, выгода их разделения исчезает.
Подчеркивайте суть подсистем, ограничивая их взаимодействие между собой.
Допустим, вы определили систему из шести подсистем (рис. 5-3). При отсутствии каких-либо ограничений в силу второго закона термодинамики энтропия системы должна увеличиться. Один из способов увеличения энтропии является абсолютно свободное взаимодействие между подсистемами (рис. 5-4).
Рис. 5-3. Пример системы, включающей шесть подсистем
Рис. 5-4. Возможный результат отсутствия правил, ограничивающих взаимодей-
ствие подсистем
Как видите, в итоге все подсистемы начинают напрямую взаимодействовать, что поднимает несколько важных вопросов:
쐽
в скольких разных частях системы нужно хоть немного разбираться разработчи- ку, желающему изменить какой-то аспект подсистемы графических операций?
ГЛАВА
5 Проектирование при конструировании
81
쐽
что будет, если вы попытаетесь задействовать данный модуль бизнес-правил в другой системе?
쐽
что будет, если вы захотите включить в систему новый пользовательский ин- терфейс (например, интерфейс командной строки, удобный для проведения тестирования)?
쐽
что произойдет, если вы захотите перенести модуль хранения данных на уда- ленный компьютер?
Стрелки между подсистемами можно рассматривать как шланги с водой. Если вам захочется «выдернуть» одну из подсистем, к ней наверняка будут подключены не- сколько шлангов. Чем больше шлангов вам нужно будет отсоединить и подключить заново, тем сильнее вы промокнете. Архитектура системы должна быть такой, чтобы замена подсистем требовала как можно меньше возни со шлангами.
При должной предусмотрительности все эти вопросы можно решить, проделав немного дополнительной работы. Реализуйте коммуникацию между подсистемами на основе принципа «необходимого знания», и пусть оно будет действительно не- обходимым. Помните: проще сначала ограничить взаимодействие, а затем сделать его более свободным, чем пытаться изолировать подсистемы после написания не- скольких сотен вызовов между ними. На рис. 5-5 показано, как несколько правил коммуникации могут изменить систему, изображенную на рис. 5-4.
Рис. 5-5. Определив несколько правил коммуникации, можно существенно упро-
стить взаимодействие подсистем
Чтобы соединения подсистем были понятными и легкими в сопровождении, ста- райтесь поддерживать простоту отношений между подсистемами. Самым простым отношением является то, при котором одна подсистема вызывает методы другой.
Более сложное отношение имеет место, когда одна подсистема содержит классы другой. Самое сложное отношение — наследование классов одной подсистемы от классов другой.
Придерживайтесь одного разумного правила: диаграмма системного уровня вроде той, что показана на рис. 5-5, должна быть ациклическим графом. Иначе говоря, программа не должна содержать циклических отношений, при которых класс A использует класс B, класс B использует класс C, а класс C — класс A.
При работе над крупными программами и программными комплексами проек- тирование на уровне подсистем просто необходимо. Если вам кажется, что ваша
82
ЧАСТЬ
II
Высококачественный код программа достаточно мала, чтобы проектирование на уровне подсистем можно было пропустить, хотя бы примите это решение осознанно.
Часто используемые подсистемы
Некоторые типы подсистем снова и снова используются в разных системах. Ниже приведены те, что встречаются чаще всего.
Подсистема бизнес-правил Бизнес-правилами называют законы, директивы, политики и процедуры, реализуемые в компьютерной системе. Например, в случае системы расчета заработной платы бизнес-правилами могли бы быть дирек- тивы налогового управления, определяющие разнообразные виды налогов. Дополнительным источником правил могло бы быть соглашение с профсоюзом, регламентирующее оплату сверхурочной работы, отпуска и т. д. При создании программы для агентства по страхованию автомобилей правила могут быть основаны на соответствующих государственных законах.
Подсистема пользовательского интерфейса Изоляция компонентов пользо- вательского интерфейса в отдельной подсистеме позволяет изменять его, не влияя на остальную программу. Как правило, подсистема пользовательского интерфейса включает несколько подчиненных подсистем или классов, отвечающих за GUI, интерфейс командной строки, работу с меню, управление окнами, справочную систему и т. д.
Подсистема доступа к БД Вы может скрыть детали реализации доступа к
БД, чтобы большая часть программы не нуждалась в знании «грязных» подроб- ностей операций над низкоуровневыми структурами и могла работать с данными в терминах бизнес-проблемы. Подсистемы, скрывающие детали реализации, обе- спечивают важный уровень абстракции, снижающий сложность программы. Они концентрируют операции над БД в одном месте и снижают вероятность ошибок при работе с данными, а также позволяют легко изменять структуру БД без из- менения большей части программы.
Подсистема изоляции зависимостей от ОС Зависимости от ОС следует изолировать в подсистеме по той же причине, что и зависимости от оборудова- ния. Если, например, вы разрабатываете программу для Microsoft Windows, зачем ограничивать себя средой Windows? Изолируйте вызовы Windows в специализи- рованной интерфейсной подсистеме, и если вам позднее захочется перенести программу на платформу Mac OS или Linux, то придется изменить только эту подсистему. Интерфейсная подсистема может быть слишком крупной, чтобы вы могли реализовать ее самостоятельно, однако такие подсистемы уже разработаны и включены в несколько коммерческих библиотек.
1 ... 8 9 10 11 12 13 14 15 ... 104
Уровень 3: разделение подсистем на классы
Этот уровень проектирования предполагает определение всех классов системы.
Например, подсистема доступа к БД может быть далее разде- лена на классы доступа к данным и классы хранения данных, а также метаданные БД. На рис. 5-2 показано, как разделить на классы одну из подсистем уровня 2; конечно, три других подсистемы также следует разделить на классы.
Перекрестная ссылка Об упро- щении бизнес-логики путем ее выражения в форме таблиц см. главу 18.
Дополнительные сведения Про- ектирование БД хорошо рассмо- трено в книге «Agile Data base
Techniques» (Ambler, 2003).
ГЛАВА
5 Проектирование при конструировании
83
Кроме того, на этом уровне следует определить детали взаимодействия каждого класса с остальными элементами системы, особенно интерфейс класса. В целом сутью проектирования на данном уровне является декомпозиция подсистем до такого уровня детальности, который позволит реализовать части подсистем в форме отдельных классов.
Разделение подсистем на классы обычно требуется во всех про- ектах, на реализацию которых уйдет более нескольких дней.
В крупных проектах необходимы и второй, и третий уровни проектирования. При работе над совсем небольшим проектом второй уровень проектирования можно пропустить.
Классы и объекты
Один из важнейших аспектов объектно-ориентированного проектирования — различие между объектами и классами. Объект — это любая конкретная динами- ческая сущность, имеющая конкретные значения и атрибуты и существующая в период выполнения программы. Класс — это статическая сущность, с которой вы имеете дело, просматривая листинг программы. Например, вы можете объявить класс
Person (человек), имеющий такие атрибуты, как фамилия, возраст, пол и т. д.
В период выполнения вы будете работать с объектами
nancy, hank, diane, tony и т. д. — иначе говоря, со специфическими экземплярами класса. Если вы знакомы с терминологией БД, различие между классом и объектом аналогично различию между «схемой» и «экземпляром». Класс можно рассматривать как форму для вы- печки булочек, а объекты — как сами булочки. В этой книге термины «класс» и
«объект» используются неформально и более или менее взаимозаменяемо.
Уровень 4: разделение классов на методы
Данный уровень проектирования заключается в разделении каждого класса на методы. Некоторые методы уже будут определены на уровне 3, при проектирова- нии интерфейсов классов. На уровне 4 вы детально определите закрытые методы классов. При этом многие методы окажутся простыми, тогда как другие будут включать иерархии методов и потребуют дополнительного проектирования.
Полное определение методов класса часто позволяет лучше понять его интерфейс, что может подтолкнуть к соответствующему изменению интерфейса, т. е. к воз- вращению на уровень 3.
Четвертый уровень декомпозиции и проектирования часто оставляется отдельным программистам и необходим в любом проекте, требующем более нескольких часов. Формально выполнять этот этап не обязательно, но хотя бы про себя вы- полнить его нужно.
Уровень 5: проектирование методов
На этом уровне проектирование заключается в детальном определении функциональности отдельных методов, за что обычно отвечают отдельные программисты, работающие над конкретными методами. Данный уровень может включать такие действия, как написание псевдокода, поиск алгоритмов в книгах, размыш- ление над оптимальной организацией фрагментов метода и написание кода. Этот
Перекрестная ссылка Характе- ристики высококачественных классов см. в главе 6.
Перекрестная ссылка О созда- нии высококачественных мето- дов см. главы 7 и 8.