Совершенный код. Совершенный код. Мастер-класс. Стив Макконнелл. Руководство по стилю программирования и конструированию по
Скачать 5.88 Mb.
|
ЧАСТЬ II Высококачественный код довольно крупными, такими как модуль работы с базами данных, модули GUI, бизнес#правил или создания отчетов, интерпретатор команд и т. д. Суть проек- тирования на данном уровне заключается в разделении программы на основные подсистемы и определении взаимодействий между подсистемами. Обычно этот уровень нужен при работе над любыми проектами, требующими более нескольких недель. При проектировании отдельных подсистем можно применять разные под- ходы: выбирайте тот, который кажется вам оптимальным в каждом конкретном случае. На рис. 5#2 данный уровень проектирования обозначен цифрой 2. Особенно важный аспект этого уровня — определение правил взаимодействия под- систем. Если все подсистемы могут взаимодействовать, выгода их разделения исчезает. Подчеркивайте суть подсистем, ограничивая их взаимодействие между собой. Допустим, вы определили систему из шести подсистем (рис. 5#3). При отсутствии каких#либо ограничений в силу второго закона термодинамики энтропия системы должна увеличиться. Один из способов увеличения энтропии является абсолютно свободное взаимодействие между подсистемами (рис. 5#4). Рис. 5'3. Пример системы, включающей шесть подсистем Рис. 5'4. Возможный результат отсутствия правил, ограничивающих взаимодей- ствие подсистем Как видите, в итоге все подсистемы начинают напрямую взаимодействовать, что поднимает несколько важных вопросов: в скольких разных частях системы нужно хоть немного разбираться разработчи- ку, желающему изменить какой#то аспект подсистемы графических операций? CC2_Part2_ch5_2010.indd 80 22.06.2010 12:31:33 ГЛАВА 5 Проектирование при конструировании 81 что будет, если вы попытаетесь задействовать данный модуль бизнес#правил в другой системе? что будет, если вы захотите включить в систему новый пользовательский ин- терфейс (например, интерфейс командной строки, удобный для проведения тестирования)? что произойдет, если вы захотите перенести модуль хранения данных на уда- ленный компьютер? Стрелки между подсистемами можно рассматривать как шланги с водой. Если вам захочется «выдернуть» одну из подсистем, к ней наверняка будут подключены не- сколько шлангов. Чем больше шлангов вам нужно будет отсоединить и подключить заново, тем сильнее вы промокнете. Архитектура системы должна быть такой, чтобы замена подсистем требовала как можно меньше возни со шлангами. При должной предусмотрительности все эти вопросы можно решить, проделав немного дополнительной работы. Реализуйте коммуникацию между подсистемами на основе принципа «необходимого знания», и пусть оно будет действительно не- обходимым. Помните: проще сначала ограничить взаимодействие, а затем сделать его более свободным, чем пытаться изолировать подсистемы после написания не- скольких сотен вызовов между ними. На рис. 5#5 показано, как несколько правил коммуникации могут изменить систему, изображенную на рис. 5#4. Рис. 5'5. Определив несколько правил коммуникации, можно существенно упро- стить взаимодействие подсистем Чтобы соединения подсистем были понятными и легкими в сопровождении, ста- райтесь поддерживать простоту отношений между подсистемами. Самым простым отношением является то, при котором одна подсистема вызывает методы другой. Более сложное отношение имеет место, когда одна подсистема содержит классы другой. Самое сложное отношение — наследование классов одной подсистемы от классов другой. Придерживайтесь одного разумного правила: диаграмма системного уровня вроде той, что показана на рис. 5#5, должна быть ациклическим графом. Иначе говоря, программа не должна содержать циклических отношений, при которых класс A использует класс B, класс B использует класс C, а класс C — класс A. При работе над крупными программами и программными комплексами проек- тирование на уровне подсистем просто необходимо. Если вам кажется, что ваша CC2_Part2_ch5_2010.indd 81 22.06.2010 12:31:34 82 ЧАСТЬ II Высококачественный код программа достаточно мала, чтобы проектирование на уровне подсистем можно было пропустить, хотя бы примите это решение осознанно. Часто используемые подсистемы Некоторые типы подсистем снова и снова используются в разных системах. Ниже приведены те, что встречаются чаще всего. Подсистема бизнес'правил Бизнес#правилами называют законы, директивы, политики и процедуры, реализуемые в компьютерной системе. Например, в случае системы расчета заработной платы бизнес#правилами могли бы быть дирек- тивы налогового управления, определяющие разнообразные виды налогов. Дополнительным источником правил могло бы быть соглашение с профсоюзом, регламентирующее оплату сверхурочной работы, отпуска и т. д. При создании программы для агентства по страхованию автомобилей правила могут быть основаны на соответствующих государственных законах. Подсистема пользовательского интерфейса Изоляция компонентов пользо- вательского интерфейса в отдельной подсистеме позволяет изменять его, не влияя на остальную программу. Как правило, подсистема пользовательского интерфейса включает несколько подчиненных подсистем или классов, отвечающих за GUI, интерфейс командной строки, работу с меню, управление окнами, справочную систему и т. д. Подсистема доступа к БД Вы может скрыть детали реализации доступа к БД, чтобы большая часть программы не нуждалась в знании «грязных» подроб- ностей операций над низкоуровневыми структурами и могла работать с данными в терминах бизнес#проблемы. Подсистемы, скрывающие детали реализации, обе- спечивают важный уровень абстракции, снижающий сложность программы. Они концентрируют операции над БД в одном месте и снижают вероятность ошибок при работе с данными, а также позволяют легко изменять структуру БД без из- менения большей части программы. Подсистема изоляции зависимостей от ОС Зависимости от ОС следует изолировать в подсистеме по той же причине, что и зависимости от оборудова- ния. Если, например, вы разрабатываете программу для Microsoft Windows, зачем ограничивать себя средой Windows? Изолируйте вызовы Windows в специализи- рованной интерфейсной подсистеме, и если вам позднее захочется перенести программу на платформу Mac OS или Linux, то придется изменить только эту подсистему. Интерфейсная подсистема может быть слишком крупной, чтобы вы могли реализовать ее самостоятельно, однако такие подсистемы уже разработаны и включены в несколько коммерческих библиотек. Уровень 3: разделение подсистем на классы Этот уровень проектирования предполагает определение всех классов системы. Например, подсистема доступа к БД может быть далее разде- лена на классы доступа к данным и классы хранения данных, а также метаданные БД. На рис. 5#2 показано, как разделить на классы одну из подсистем уровня 2; конечно, три других подсистемы также следует разделить на классы. Перекрестная ссылка Об упро- щении бизнес-логики путем ее выражения в форме таблиц см. главу 18. Дополнительные сведения Про- ектирование БД хорошо рассмо- трено в книге «Agile Data base Techniques» (Ambler, 2003). CC2_Part2_ch5_2010.indd 82 22.06.2010 12:31:35 ГЛАВА 5 Проектирование при конструировании 83 Кроме того, на этом уровне следует определить детали взаимодействия каждого класса с остальными элементами системы, особенно интерфейс класса. В целом сутью проектирования на данном уровне является декомпозиция подсистем до такого уровня детальности, который позволит реализовать части подсистем в форме отдельных классов. Разделение подсистем на классы обычно требуется во всех про- ектах, на реализацию которых уйдет более нескольких дней. В крупных проектах необходимы и второй, и третий уровни проектирования. При работе над совсем небольшим проектом второй уровень проектирования можно пропустить. Классы и объекты Один из важнейших аспектов объектно#ориентированного проектирования — различие между объектами и классами. Объект — это любая конкретная динами- ческая сущность, имеющая конкретные значения и атрибуты и существующая в период выполнения программы. Класс — это статическая сущность, с которой вы имеете дело, просматривая листинг программы. Например, вы можете объявить класс Person (человек), имеющий такие атрибуты, как фамилия, возраст, пол и т. д. В период выполнения вы будете работать с объектами nancy, hank, diane, tony и т. д. — иначе говоря, со специфическими экземплярами класса. Если вы знакомы с терминологией БД, различие между классом и объектом аналогично различию между «схемой» и «экземпляром». Класс можно рассматривать как форму для вы- печки булочек, а объекты — как сами булочки. В этой книге термины «класс» и «объект» используются неформально и более или менее взаимозаменяемо. Уровень 4: разделение классов на методы Данный уровень проектирования заключается в разделении каждого класса на методы. Некоторые методы уже будут определены на уровне 3, при проектирова- нии интерфейсов классов. На уровне 4 вы детально определите закрытые методы классов. При этом многие методы окажутся простыми, тогда как другие будут включать иерархии методов и потребуют дополнительного проектирования. Полное определение методов класса часто позволяет лучше понять его интерфейс, что может подтолкнуть к соответствующему изменению интерфейса, т. е. к воз- вращению на уровень 3. Четвертый уровень декомпозиции и проектирования часто оставляется отдельным программистам и необходим в любом проекте, требующем более нескольких часов. Формально выполнять этот этап не обязательно, но хотя бы про себя вы- полнить его нужно. Уровень 5: проектирование методов На этом уровне проектирование заключается в детальном определении функциональности отдельных методов, за что обычно отвечают отдельные программисты, работающие над конкретными методами. Данный уровень может включать такие действия, как написание псевдокода, поиск алгоритмов в книгах, размыш- ление над оптимальной организацией фрагментов метода и написание кода. Этот Перекрестная ссылка Характе- ристики высококачественных классов см. в главе 6. Перекрестная ссылка О созда- нии высококачественных мето- дов см. главы 7 и 8. CC2_Part2_ch5_2010.indd 83 22.06.2010 12:31:35 84 ЧАСТЬ II Высококачественный код уровень проектирования выполняется во всех случаях, но не всегда осознанно и качественно. На рис. 5#2 он отмечен цифрой 5. 5.3. Компоненты проектирования: эвристические принципы Разработчики ПО любят четкие и ясные правила: «Сделай A, B и C, и это обяза- тельно приведет к X, Y и Z». Мы испытываем гордость, когда находим тайные дей- ствия, приводящие к желаемым результатам, и сердимся, если команды работают не так, как описано. Стремление к детерминированному поведению прекрасно согласуется с детальным программированием, при котором строгое внимание к деталям может определить успех или провал программы. Однако проектирование ПО — совсем другая история. Так как проектирование не является детерминированным, главным аспектом про- ектирования качественного ПО становится умелое применение набора эффек- тивных эвристических принципов. Ниже мы рассмотрим ряд таких принципов — подходов, способных привести к удачным решениям. Можете считать эвристиче- ские принципы правилами выполнения проб при использовании метода проб и ошибок. Несомненно, некоторые из них вам уже известны. Каждый из эвристи- ческих принципов будет описан в контексте Главного Технического Императива Разработки ПО — управления сложностью. Определите объекты реального мира Первый, и самый популярный, подход к проектированию — «общепринятый» объектно#ориентированный подход — основан на определении объектов реального мира и ис- кусственных объектов. При проектировании с использованием объектов опреде- лите: объекты и их атрибуты (методы и данные); действия, которые могут быть выполнены над каждым объектом; действия, которые каждый объект может выполнять над другими объектами; части каждого объекта, видимые другим объектам, т. е. открытые и закрытые части; открытый интерфейс каждого объекта. Эти часто повторяющиеся действия не обязательно выполнять в указанном по- рядке. Помните о важности итерации. Определите объекты и их атрибуты В основе создания программ обычно лежат сущности реального мира. Например, система расчета повременной оплаты может быть основана на таких сущностях, как сотрудники, клиенты, карты учета времени и счета (рис. 5#6). Прежде всего следует узнать, не что система выполняет, а над ЧЕМ она это выполняет! Бертран Мейер (Bertrand Meyer) Перекрестная ссылка О про- ектировании с использованием классов см. главу 6. CC2_Part2_ch5_2010.indd 84 22.06.2010 12:31:35 ГЛАВА 5 Проектирование при конструировании 85 Рис. 5'6. Эта система расчета оплаты состоит из четырех основных объектов (пример упрощен) Определить атрибуты объектов не сложнее, чем сами объекты. Каждый объект имеет характеристики, релевантные для компьютерной программы. Скажем, в системе расчета повременной оплаты объект «сотрудник» обладал бы такими атрибутами, как имя/фамилия, должность и уровень оплаты. С объектом «счет» были бы связаны такие атрибуты, как сумма, имя/фамилия клиента, дата и т. д. Объектами системы GUI были бы разнообразные окна, кнопки, шрифты и ин- струменты рисования. При дальнейшем изучении проблемной области вы можете прийти к выводу, что установление однозначного соответствия между объектами программы и объектами реального мира — не самый лучший способ определения объектов, но для начала он тоже неплох. Определите действия, которые могут быть выполнены над каждым объ- ектом Объекты могут поддерживать самые разные операции. В нашей системе расчета оплаты объект «сотрудник» мог бы поддерживать изменение должности или уровня оплаты, объект «клиент» — изменение реквизитов счета и т. д. Определите действия, которые каждый объект может выполнять над другими объектами Суть этого этапа ясна из его названия. Двумя универсаль- ными действиями, которые объекты могут выполнять друг над другом, являются включение (containment) и наследование. Какие объекты могут включать другие (какие?) объекты? Какие объекты могут быть унаследованными от других (ка- ких?) объектов? На рис. 5#6 объект «карта учета времени» может включать объект «сотрудник» и объект «клиент», а объект «счет» может включать карты учета вре- мени. Кроме того, счет может сообщать, что клиент оплатил услуги, а клиент — оплачивать указанную в счете сумму. Более сложная система включала бы допол- нительные взаимодействия. Определите части каждого объекта, видимые другим объектам Один из главных аспектов проектирования — определение частей объекта, которые следует сделать открытыми, и частей, которые следует держать закрытыми. Этого решения требуют и данные, и методы. CC2_Part2_ch5_2010.indd 85 22.06.2010 12:31:35 86 ЧАСТЬ II Высококачественный код Определите интерфейс каждого объекта Для каждо- го объекта надо определить формальный синтаксический интерфейс на уровне языка программирования. Данные и методы, которые объект предоставляет в распоряжение остальным объектам, называются «открытым интерфейсом». Части объекта, доступные производным от него объектам, называются «защищенным интерфейсом» объекта. Проектируя программу, обду- майте интерфейсы обоих типов. Завершая проектирование высокоуровневой объектно#ориентированной органи- зации системы, вы будете использовать два вида итерации: высокоуровневую, на- правленную на улучшение организации классов, и итерацию на уровне каждого из определенных классов, направленную на детализацию проекта каждого класса. Определите согласованные абстракции Абстракция позволяет задействовать концепцию, игнорируя ее некоторые детали и работая с разными деталями на разных уровнях. Имея дело с составным объектом, вы имеете дело с абстракцией. Если вы рассматриваете объект как «дом», а не как комбинацию стекла, древесины и гвоздей, вы прибегаете к абстракции. Если вы рас- сматриваете множество домов как «город», вы прибегаете к другой абстракции. Базовые классы представляют собой абстракции, позволяющие концентрироваться на общих атрибутах производных классов и игнорировать детали конкретных классов при работе с базовым классом. Удачный интерфейс класса — это абстрак- ция, позволяющая сосредоточиться на интерфейсе, не беспокоясь о внутренних механизмах работы класса. Интерфейс грамотно спроектированного метода обеспечивает такую же выгоду на более низком уровне детальности, а интерфейс грамотно спроектированного пакета или подсистемы — на более высоком. С точки зрения сложности, главное достоинство абстракции в том, что она позво- ляет игнорировать нерелевантные детали. Большинство объектов реального мира уже является абстракциями некоторого рода. Как я только что сказал, дом — это абстракция окон, дверей, обшивки, электропроводки, водопроводных труб, изо- ляционных материалов и конкретного способа их организации. Дверь же — это абстракция особого вида организации прямоугольного фрагмента некоторого материала, петель и ручки. А дверную ручку можно считать абстракцией конкрет- ного способа упорядочения медных, никелевых или стальных деталей. Мы используем абстракции на каждом шагу. Если б, открывая или закрывая дверь, вы должны были иметь дело с отдельными волокнами древесины, молекулами лака и стали, вы вряд ли смогли бы войти в дом или выйти из него. Абстракция — один из главных способов борьбы со сложностью реального мира (рис. 5#7). Перекрестная ссылка О классах и сокрытии информации см. подраздел «Скрывайте секреты (к вопросу о сокрытии инфор- мации)» раздела 5.3. CC2_Part2_ch5_2010.indd 86 22.06.2010 12:31:35 |