Для упрощения
модели проектирования ее можно разбить на блоки. Сгруппировав элементы Модели проектирования по пакетам и подсистемам и
затем указав, как они связаны друг с другом, вы сделаете общую структуру модели проще для понимания. Учтите, что подсистема проектирования моделируется как компонент, реализующий один или
несколько интерфейсов; дополнительная информация приведена в разделах Рабочий продукт: подсистема проектирования и Рекомендация по рабочему продукту: подсистема проектирования. Пакеты проектирования,
напротив, предназначены исключительно для группировки.
Класс, содержащийся в пакете, может быть общедоступным или частным. Общедоступный класс можно связать с любым другим классом. Частный класс можно связать только с
классами, содержащимися в пакете.
Интерфейс пакета состоит из общедоступных классов пакета. Интерфейс пакета (общедоступные классы) изолирует и реализует
зависимости от других пакетов. Это позволяет упростить параллельную разработку, поскольку вы можете сравнительно рано
установить интерфейсы, а разработчикам достаточно знать только об изменениях в интерфейсах других пакетов.
Существуют различные причины для разбиения Модели проектирования на разделы:
-
Пакеты и подсистемы можно использовать в качестве единиц заказа, конфигурации или доставки по окончании разработки
системы.
-
Выделение ресурсов и уровень компетентности различных коллективов разработки могут потребовать разделения проекта
между различными группами, находящимися в различных местах. Подсистемы, с их определенными интерфейсами, позволяют
координированно разделить задание между группами, обеспечив параллельное проектирование и реализацию.
-
Подсистемы позволяют структурировать модель проектирования с учетом пользовательских типов. Многие требования об
изменениях поступают от пользователей; подсистемы гарантируют, что изменения от конкретного пользовательского типа
повлияют только на те части системы, которые соответствуют этому пользовательскому типу.
-
В некоторых приложениях определенная информация должна быть доступна только узкому кругу лиц. Подсистемы позволяют
сохранить секретность там, где она необходима.
-
Если вы создаете систему поддержки, то с помощью подсистем и пакетов вы можете придать ей структуру, схожую со
структурой поддерживаемой системы. Это позволит синхронизировать обслуживание этих двух систем.
-
Подсистемы служат для представления существующих
продуктов и служб, используемых системой (например, продуктов COTS и библиотек), как разъясняется в нескольких
следующих разделах.
Когда пограничные классы распределяются по пакетам, существуют две стратегии, которые применимы в этом случае; какую из
них выбрать, зависит от того, предполагается ли вносить значительные изменения в системные интерфейсы в будущем.
-
Если системный интерфейс, скорее всего, будет заменен или претерпит серьезные изменения, то интерфейс следует
отделить от остальной модели проектирования. Изменение пользовательского интерфейса повлияет только на эти пакеты.
Примером такого серьезного изменения может служить переключение с интерфейса, ориентированного на линию, на
интерфейс, ориентированный на окна.
Если главная цель - упростить внесение изменений в интерфейс, то пограничные классы следует поместить в один (или
несколько) отдельных пакетов.
-
Если вносить серьезные изменения в интерфейс не планируется, то во главу угла следует поставить изменения в
системных службах, а не в интерфейсе. Пограничные классы следует упаковать вместе с сущностными и управляющими
классами, с которыми они функционально связаны. Это позволит легко выяснить, какие пограничные классы будут
затронуты при изменении какого-либо сущностного или управляющего класса.
Для того чтобы упростить изменение системных служб, пограничные классы упаковываются вместе с классами, с которыми они
функционально связаны.
Обязательные пограничные классы, не связанные функционально ни с какими сущностными и управляющими классами, следует
поместить в отдельные пакеты, вместе с другими пограничными классами из того же интерфейса.
Если пограничный класс связан с необязательной службой, разместите его вместе с остальными классами, обеспечивающими
работу этой службы, в отдельной подсистеме. Подсистема будет отображена в необязательный компонент, который будет
предоставляться в случае заказа этой необязательной функциональности.
Для каждой группы функционально связанных классов необходим отдельный пакет. Существует несколько практических
критериев, позволяющих определить, связаны ли функционально два класса. Они перечислены ниже, в порядке убывания
важности:
-
Если изменения в поведении или структуре одного класса вынуждают изменения в другом классе, то эти два класса
функционально связаны.
Пример
При добавлении нового атрибута в сущностный класс Заказ, скорее всего, потребуется обновить управляющий
класс Администратор заказов. Таким образом, эти классы входят в один пакет - Обработка заказов.
-
Выяснить, связан ли функционально один класс с другим, можно, удалив класс - например, сущностный класс - из
системы и изучив последствия удаления. Все классы, которые станут лишними в результате удаления данного класса,
связаны с ним так или иначе. Под "лишним" понимается класс, используемый только данным удаленным классом или
зависящий от него.
Пример
Пакет Обработка заказов содержит два управляющих класса Администратор заказов и Регистратор
заказов и находится в Системе управления складом. Оба управляющих класса моделируют службы обработки
заказов на складе. Все атрибуты и взаимосвязи заказа хранятся в сущностном классе Заказ, предназначенном
исключительно для обработки заказов. В случае удаления сущностного класса Заказ классы Администратор
заказов и Регистратор заказов станут не нужны, поскольку они полезны только при наличии этого класса.
Следовательно, сущностный класс Заказ следует включить в один пакет с этими двумя управляющими классами.
Администратор заказов и Регистратор заказов принадлежат тому же пакету, что и Заказ, поскольку
они станут лишними при удалении класса Заказ из системы.
-
Два объекта могут быть функционально связаны, если они интенсивно обмениваются сообщениями или еще каким-либо
образом взаимодействуют друг с другом.
Пример
Управляющий класс Исполнитель задач отправляет и принимает много сообщений от Интерфейса
транспортера. Это другой признак того, что их нужно включить в один пакет, Обработка задач.
-
Пограничный класс может быть функционально связан с конкретным сущностным классом, если функция пограничного класса
представляет сущностный класс.
Пример
Пограничный класс Форма поддона в Системе управления складом представляет экземпляр сущностного
класса Поддон для пользователя. Каждый Поддон представлен идентификационным номером на экране. В
случае изменения информации о Поддоне, например, если Поддону присваивается еще и имя, может
потребоваться изменить и пограничный класс. Таким образом, Форма поддона должна быть включена в тот же
пакет, что и Поддон.
-
Два класса могут быть функционально связаны, если они взаимодействуют с одним и тем же субъектом или на них влияют
изменения в субъекте. Если у двух классов нет общего субъекта, то они не должны находиться в одном пакете.
Разумеется, при наличии серьезных контрдоводов последнее правило можно нарушать.
Пример
В Системе управления складом есть пакет Обработка задач, включающий, помимо прочего, управляющий
класс Исполнитель задач. Это единственный пакет, связанный с субъектом Транспортер - физическим
транспортером поддонов на складе. Субъект взаимодействует с управляющим классом Исполнитель задач через
пограничный класс Интерфейс транспортера. Таким образом, этот пограничный класс должен быть включен в пакет
Обработка задач.
Интерфейс транспортера и Исполнитель задач входят в один пакет, поскольку на них обоих влияют
изменения в субъекте Транспортер.
-
Два класса могут быть функционально связаны, если у них есть взаимосвязи друг с другом (ассоциации, агрегирования и
т.п.). Разумеется, данный критерий нельзя применять автоматически, но он может пригодиться, если все остальные
критерии не подходят.
-
Класс может быть функционально связан с классом, создающим его экземпляры.
Следующие два критерия позволяют выяснить, когда два класса не следует помещать в один пакет:
-
Два класса, связанные с разными субъектами, не следует помещать в один пакет.
-
Необязательный и обязательный классы не следует помещать в один пакет.
Прежде всего, все элементы в пакете должны быть одинаковой степени обязательности: в обязательном пакете не должно быть
необязательных элементов модели.
Пример
Одним из атрибутов обязательного сущностного класса Тип изделия является Порог пополнения запаса. Функция
пополнения запаса, однако, необязательна в системе. По этой причине, класс Изделие следует разбить на два
сущностных класса, и необязательный класс должен быть связан с обязательным.
Пакет, считающийся обязательным, не может зависеть от необязательного пакета.
Как правило, один пакет не может использоваться двумя различными субъектами. Причина в том, что изменение в поведении
одного субъекта не должно влиять на другие субъекты. В этом правиле есть исключения, например, для пакетов, образующих
необязательные службы. Пакеты этого типа не должны разбиваться, независимо от числа использующих их субъектов. Из
сказанного следует, что вы должны разбить все пакеты и классы, используемые несколькими субъектами, за исключением
необязательных пакетов.
Все классы в одном пакете должны быть функционально связаны. Если вы следовали критериям из раздела "Найти пакеты из
функционально связанных классов", то классы в одном пакете будут функционально связаны друг с другом. Однако конкретный
класс может содержать "слишком много" поведения, либо взаимосвязи, не принадлежащие ему. В этом случае часть класса
следует удалить, превратив ее в новый класс или перенеся ее в другой класс, возможно из другого пакета.
Пример
Поведение управляющего класса A из одного пакета не должно слишком сильно зависеть от класса B из другого
пакета. Для того чтобы изолировать поведение, обусловленное классом B, необходимо разбить управляющий класс
A на два управляющих класса - A' и A". Поведение, обусловленное классом B, помещается в
новый управляющий класс A", который заносится в тот же пакет, что и класс B. Новый класс A" также
получает взаимосвязь, например обобщение, с исходным объектом A'.
Для того чтобы изолировать поведение, обусловленное классом B, неоднородный управляющий класс A
разбивается на два управляющих класса - A' и A".
Если у класса в одном пакете есть ассоциация с классом в другом пакете, то эти пакеты зависят друг от друга.
Зависимости пакетов моделируются с помощью взаимосвязи зависимости между пакетами. Взаимосвязи зависимости помогают
оценить последовательность изменений: пакет, от которого зависит много пакетов, труднее изменить, чем пакет, от
которого не зависит ни один пакет.
Так как во время спецификации пакетов будут обнаружены несколько зависимостей, подобных описанным, эти взаимосвязи
будут вынуждены изменяться во время работы. Описание взаимосвязи зависимости может содержать информацию о взаимосвязях
классов, послуживших причиной появления зависимости. Поскольку при этом появляется информация, которую трудно
обслуживать, это следует делать, только если эта информация ценная и имеет отношение к делу.
Пример
В Системе управления складом есть взаимосвязь зависимости, ведущая от пакета Обработка заказов к пакету
Обработка предметов. Эта ассоциация появляется, поскольку у сущностного класса Заказ в Обработке
заказов есть ассоциация с сущностным классом Тип предмета в другом пакете.
Пакет Обработка заказов зависит от пакета Обработка предметов, поскольку между двумя классами из этих
пакетов существует ассоциация.
Связывание пакетов - это и хорошо, и плохо: хорошо, поскольку связывание представляет повторное использование, а плохо
- поскольку связывание представляет зависимости, усложняющие эволюцию системы. Ниже перечислены некоторые общие
принципы:
-
Пакеты не должны быть перекрестно связанными (т.е. взаимозависимыми); например, два пакета не должны зависеть друг
от друга.
В этих случаях необходимо реорганизовать пакеты, удалив перекрестные зависимости.
-
Пакеты нижних уровней не должны зависеть от пакетов верхних уровней. Пакеты должны зависеть только от пакетов того
же и следующего нижнего уровня.
В таких случаях необходимо заново организовать функциональность. Например, можно выразить зависимости в терминах
интерфейсов и организовать интерфейсы на нижнем уровне.
-
В целом, зависимости не должны перескакивать через уровни, кроме случаев, когда зависимое поведение является общим
для всех уровней и альтернативой является простой вызов операции удаленного подключения на всех уровнях.
-
Пакеты не должны зависеть от подсистем, а лишь от других пакетов или интерфейсов.
|