392 ли, что такое разделение жизненно важно для долго живущих систем.
Выделение иерархии (Extract Hierarchy) упрощает чрезмерно сложные классы путем превращения их в группы подклассов.
1. Разделение наследования (Tease Apart Inheritance). Наследование способствует написанию чрезвычайно «сжатого» кода в подклассах.
Механизм наследования обладает такой мощью, что случаи его непра- вильного применения не должны удивлять. Неправильное употребление накапливается постепенно. Например, разработчик вводит небольшой класс для решения простой задачи, потом добавляет другие подклассы для решения той же задачи в других местах иерархии. И через месяц
(или год) имеет запутанное наследование, которое представляет собой потенциальный источник неприятностей, потому что оно приводит к дублированию кода (как говорит М. Фаулер, – бичу программистов).
Оно осложняет модификацию, потому что стратегии решения опреде- ленного рода проблемы распространены по всей программе. Наконец, труднее понимать результирующий код.
Легко можно обнаружить одну иерархию наследования, которая ре- шает две задачи. Если у каждого подкласса на определенном уровне иерархии есть подклассы, имена которых начинаются с одинаковых прилагательных, то, вероятно, делается два дела с помощью одной ие- рархии. Таким образом, если есть иерархия наследования, которая вы- полняет одновременно две задачи, М. Фаулер рекомендует создать две иерархии и использовать делегирование для вызова одной из другой.
2. Преобразование процедурного проекта в объекты (Convert
Procedural Design to Objects). Часто приходится сталкиваться с пробле- мой, когда код процедурного вида надо сделать более объектно- ориентированным. Типичную ситуацию представляют длинные проце- дурные методы в классе, хранящем мало данных, и «немые» объекты данных, в которых нет ничего, кроме методов доступа к полям. Если надо преобразовать чисто процедурную программу, то и «немых» объ- ектов может не оказаться, но для начала и это неплохо. Нельзя утвер- ждать, что объектов с поведением и малым количеством данных (или полным их отсутствием) вообще быть не должно. Часто используются маленькие объекты стратегий, когда требуется варьировать поведение.
Однако такие процедурные объекты обычно невелики и применяются, когда возникает особая потребность в гибкости.
Если имеется код, написанный в процедурном стиле, М. Фаулер ре- комендует преобразовать записи данных в объекты, разделить поведе- ние и переместить поведение в объекты.
3. Отделение предметной области от представления (Separate
Domain from Presentation). В разговорах об объектах можно услышать о
«модели-представлении-контроллере» (MVC) [1]. Эта идея послужила фундаментом связи между графическим интерфейсом пользователя
(GUI) и объектами предметной области в Smalltalk-80. В основе MVC лежит разделение кода пользовательского интерфейса (представления, называвшегося раньше view, а сейчас чаще presentation) и логики пред- метной области (модели - model). Классы представления содержат толь-
393 ко ту логику, которая нужна для работы с интерфейсом пользователя.
Объекты предметной области не содержат кода визуализации, зато со- держат всю бизнес-логику.
В результате сложная программа разделяется на части, которые проще модифицировать. Кроме того, появляется возможность сущест- вования нескольких представлений одной и той же бизнес-логики. Тот, кто имеет опыт работы с объектами, пользуется таким разделением ин- стинктивно, и оно доказало свою ценность. Но многие из тех, кто рабо- тает с GUI, не придерживаются такой архитектуры. В большинстве сред с GUI «клиент/сервер» реализована двухзвенная конструкция: данные находятся в базе данных, а логика находится в классах представления.
Среда часто навязывает такой стиль проектирования, мешая поместить логику в другое место.
Java – правильная объектно-ориентированная среда, позволяющая создавать невизуальные объекты предметной области, содержащие биз- нес-логику. Однако часто можно столкнуться с кодом, написанным в двухзвенном стиле. Имеются классы GUI, содержащие логику предмет- ной области. В таких случаях рекомендуется выделить логику предмет- ной области в отдельные классы предметной области.
4
. Выделение иерархии (Extract Hierarchy). При эволюционном про- ектировании класс часто представляется как реализация одной идеи, а впоследствии обнаруживается, что на самом деле он реализует две или три идеи, а то и все десять. Сначала создается простой класс. Через не- которое время разработчик замечает, что если добавить в него один флаг и пару проверок, то можно его использовать еще в одном случае.
Через месяц обнаруживается еще одна такая возможность, а через год наступает полная неразбериха: всюду раскиданы флаги и условные вы- ражения.
Столкнувшись с такой ситуацией, надо найти стратегию, позволяю- щую расплести отдельные пряди. Описываемая в [32] стратегия дейст- вует, если только условная логика сохраняет статичность в течение сро- ка жизни объекта, в противном случае, прежде чем начать отделять од- ни случаи от других, может потребоваться провести «Выделение клас- са» (Extract Class). «Выделение иерархии» ( Extract Hierarchy ) - такой рефакторинг, который нельзя завершить за один день. Для распутыва- ния сложного проекта могут потребоваться недели или месяцы.
Поэтому если есть класс, выполняющий слишком много работы, частично или полностью через многочисленные условные операторы, рекомендуется создать иерархию классов, в которой каждый подкласс представляет конкретный случай.
1 ... 29 30 31 32 33 34 35 36 37
8.6. Архитектурный рефакторинг. Архитектурные паттерны 8.6.1. Когда нужен архитектурный рефакторинг
Потребность в изменении существующей программной системы мо- жет возникнуть в ходе решения широкого круга задач по ее модерниза-
394 ции [14, 15]. В общем случае изменения существующей программной системы затрагивают не только программный код, но и все остальные артефакты, связанные с трансформируемой программной системой.
Часто существенным является изменение архитектуры программной системы. В качестве примеров можно привести следующие сценарии, требующие изменения архитектуры существующей ПС.
1. Преобразования, обусловленные функциональными изменениями
ПС. Желательно, чтобы внедрение новой функциональности не затро- нуло существующую логику системы. Также желательно, чтобы слож- ность внедрения новой функциональности в существующую систему не превышала существенным образом сложность реализации этой функ- циональности в рамках нового проекта. Изменение существующей ар- хитектуры – хороший шаг на пути внедрения новой функциональности, облегчающий и дальнейшую эволюцию системы.
2. Смена платформы ПС. Смена платформы ПС (как аппаратной, так общего программного обеспечения, в частности операционной системы) должна минимально затрагивать существующий код. Желательно огра- ничиться изменениями только в узкой платформенно-зависимой про- слойке системы. Выделение такой прослойки – архитектурная задача.
Ее решение всегда сопряжено с необходимостью изменения архитекту- ры.
3. Обновление технологии разработки программного продукта, свя- занное, например, с переходом компонентное программирование, вне- дрением комплексной среды коллективной разработки с возможностя- ми гибкого, формального и смешанного планирования и ведения отчет- ности, доступными на одной платформе, например, IBM Rational Team
Concert .
4. Преобразования, связанные с реорганизацией компании, ведущей разработку. Преобразования, связанные с реорганизацией компании, ведущей разработку. Примером, такой реорганизации может стать аут- сорсинг. Решение об использовании аутсорсинга – типичный шаг по оптимизации производства. К сожалению, этот шаг зачастую затрудня- ется проблемой выделения и передачи компонентов для внешней разра- ботки. Изменение архитектуры программной системы способно облег- чить решение этой задачи.
При описании методов рефакторинга принято использовать частич- но формализованный формат – шаблон или паттерн [30, 36]. Это повто- римая архитектурная конструкция, представляющая собой решение проблемы проектирования в рамках некоторого часто возникающего контекста. Обычно шаблон не является законченным образцом, который может быть прямо преобразован в код; это лишь пример решения зада- чи, который можно использовать в различных ситуациях. Объектно- ориентированные шаблоны показывают отношения и взаимодействия между классами или объектами, без определения того, какие конечные классы или объекты приложения будут использоваться [36]. «Низко- уровневые» шаблоны, учитывающие специфику конкретного языка про- граммирования, называются идиомами. Это хорошие решения проекти-
395 рования, характерные для конкретного языка или программной плат- формы, и потому не универсальные. На наивысшем уровне существуют архитектурные шаблоны, они охватывают собой архитектуру всей про- граммной системы.
Любой паттерн описывает и именует типовую задачу, которая по- стоянно возникает в работе, а также принцип ее решения, причем таким образом, что это решение можно использовать потом снова и снова.
Паттерн именует, абстрагирует и идентифицирует ключевые аспекты структуры общего решения. Помимо прочего, паттерны формируют словарь в проблемной области и позволяют двум специалистам в этой области именовать типовые решения и понимать друг друга, не объяс- няя каждый раз суть самих решений.
Например, в [32] паттерн имеет следующую структуру: название паттерна; краткая сводка ситуаций, в которых требуется данный метод; мотивировка применения; пошаговое описание применения метода рефакторинга.
Известно, что рефакторинг объектно-ориентированного кода заре- комендовал себя как эффективный способ решения задач эволюции и сопровождения программ. Однако, и это отмечается в статьях М. Ксен- зова [14, 15], опубликованных в 2004 году, практически не существует исследований, освещающих рефакторинг на более высоком уровне аб- стракции – уровне архитектуры ПО. Ситуация мало изменилась и к на- стоящему времени. Поэтому вызывает интерес, возможен ли перенос данной методологии на более высокий уровень абстракции, с целью получения аналогичной методики систематического изменения архи- тектуры ПО?
Специфика исследования и трансформации архитектуры программ- ного обеспечения заключается в том, что архитектура не имеет явного представления, за исключением, может быть, тех случаев, когда она явно задокументирована. Однако даже в последнем, случае трудно га- рантировать соответствие задокументированной архитектуры фактиче- ской высокоуровневой логической структуре, которая на самом деле существует. Способом описания архитектуры и ее изменений могут стать структурные модели. В настоящее время существует большое ко- личество нотаций и инструментов, поддерживающих структурное моде- лирование программных систем. В свете соответствия модели фактиче- ской структуре существующего кода представляется исключительно важной возможность автоматического извлечения таких моделей из ко- да программных систем, поскольку в этом случае гарантирует их точ- ность.
8.6.2. Построение архитектуры ПС по ее программному коду
Данной задаче посвящен ряд работ. Одной из них, наиболее инте- ресной и универсальной по предлагаемому подходу является статья В.
Миронова [17]. В этой работе рассматривается технология анализа про-
396 грамм большого размера на основе построения графов, что обеспечива- ет навигацию по исходным файлам с показом необходимых свойств
(например, используемых классов, методов, функций и др.). Предвари- тельно стоит рассмотреть возможные подходы и инструментальные средства анализа и формального представления текстов программ.
1. Язык UML, предназначенный для определения, визуализации, конструирования и документирования артефактов программных систем.
Он позволяет в объектно-ориентированном виде описать систему прак- тически со всех возможных точек зрения, в том числе и разные аспекты поведения системы. Основной недостаток – неточная семантика. Язык
UML предназначен для графического представления системы на сред- нем и высоком уровнях, но малоэффективен для описания детальной логики программ нижнего уровня.
2. CASE-технологии – программные комплексы, автоматизирующие технологический процесс анализа, проектирования, разработки и сопро- вождения сложных программных систем. CASE-технологии использо- вались в реинжиниринге с момента их появления. Однако по своей при- роде CASE-средства малопригодны для низкоуровневого описания структуры ПС. Кроме того, они предполагают рассмотрение ПС со сто- роны организации бизнес-процессов, потоков и создания оптимальной
ИС для предприятия. Несмотря на высокие потенциальные возможно- сти CASE-технологии далеко не все разработчики ИС, использующие
CASE-средства, достигают ожидаемых результатов.
3. Комплекс программ построения графов Graphviz [4], разработан- ный специалистами лаборатории AT&T пакет утилит по автоматиче- ской визуализации графов, заданных в виде описания на языке «dot». В пакет утилит Graphviz входит автоматический визуализатор «dot» для формирования ориентированных графов на основе входного текстового описания (на специальном языке описания объектов, связей и их харак- теристик) в виде графического, векторного или текстового файла. Дан- ная технология является вспомогательным средством, обеспечивающим поддержку оптимального расположения вершин графа.
4. Kscope – программа для исследования и редактирования исходных текстов больших проектов на языке C 9 (Сейчас KScope не поддержива- ется. Прошлые релизы и репозитарий исходного кода еще доступны на
SourceForge.net). Она позволяет построить граф-дерево вызовов, кото- рый наглядно показывает отношения между функциями. В Kscope ис- пользуется свой подход к процессу визуализации: составление и вызов запроса. Недостатками данного подхода является малая информатив- ность (очень сложно увидеть на графе всю программу целиком) и огра- ниченность синтаксисом языка C.
Рассмотрим принципы подхода к решению задачи построения архи- тектуры ПС по ее программному коду [17]. Одним из способов прове- дения анализа является формирование графов с различным уровнем отображения внутренних объектов и связей. Модель данных большой программной системы, реализованной на языках программирования
С/С++, представляет собой наборы директорий, содержащих различные