Описание образцов проектирования на UML

В настоящее время мало кто из разработчиков не слышал о паттернах проектирования (patterns). Классификация, приводимая в зарубежных источниках, например [1], делит паттерны на четыре группы: архитектурные образцы (architectural patterns), образцы проектирования (design patterns), принципы проектирования (design principles) и идиомы (idioms). В данной статье речь идет о образцах проектирования и способе их описания посредством UML [3].

Данная тема представляется актуальной прежде всего потому, что среди всех остальных паттернов образцы проектирования имеют самую большую аудиторию почитателей, чему в немалой степени способствовал перевод книги [2]. Однако графический способ представления образцов проектирования оставляет желать лучшего. Надо сказать, что этим недостатком грешат как печатные издания (см. ту же книгу [2]), так и интернет порталы (см. например, русскоязычную wikipedia). Между тем унифицированный язык моделирования (UML) предлагает для описания образцов проектирования вполне конкретный механизм — кооперацию (collaboration), и способы ее использования для описания как самого образца, так и способа его использования при разработки модели конкретной программной системы [4].

Паттерны

Паттерн (pattern) — это обобщенное название. Выделяют следующие группы паттернов:

Архитектурные образцы представляют собой примеры высокоуровневых организаций программных систем в виде набора подсистем и описания отношений между ними. Архитектурные образцы призваны, прежде всего, реализовать нефункциональные требования, предъявляемые к программной системе. Например, такие, как производительность, возможность масштабирования и т.п.

Примеры архитектурных образцов: Layers (Уровни), Broker (Брокер), Model-View-Controller (Модель-представление-контроллер).

Принципы проектирования описывают общие правила для проектирования элементов системы. При использовании принципов проектирования подразумевается поиск разумного компромисса, так как сами принципы являются идеальными и часто не достижимыми рекомендациями, при строгом следовании которым один принцип может вступить в противоречие с другим.

Примерами принципов проектирования может служить принцип подстановки Лисков или принцип разделения модели и представления Model-View Separation (разделение модель-представление).

Образцы проектирования описывают проверенное практикой решение типичной задачи (на уровне классов или компонент и их взаимодействия) в некотором контексте. В качестве примеров можно назвать образцы Publish-Subscriber (издатель-подписчик) или Composite (композит).

Идиомы предлагают конкретное проектировочное решения, которое обычно зависит от языка программирования и используемых парадигм. Примером может служить идиома Singleton (одиночка).

Унифицированный язык моделирования UML — это графический язык моделирования общего назначения, предназначенный для спецификации, конструирования, визуализации и документирования всех артефактов, возникающих в процессе разработки программных систем [3]. Таким образом, UML целесообразно применять для описания паттернов.

Тема данной статьи — рассмотрение применения унифицированного языка моделирования (UML) для решения задачи описания образцов проектирования.

Кооперации в UML

Кооперация (collaboration) — это классификатор (classifier), который имеет внутреннюю структуру. С некоторой натяжкой кооперацию можно назвать разновидностью структурированного классификатора (structured classifier). От полноценного структурированного классификатора кооперацию отличает тот факт, что кооперация никогда не владеет своими частями. Части связаны между собой посредством участия в кооперации, а не физическим вхождением внутрь нее.

Есть еще одно отличие, хорошо проявляющееся в нотации кооперации. Кооперация идентифицируется по имени, исходя из задачи, которую решает, в то время как структурированные классификаторы носят имена реальных классов или компонентов. Основное применение коопераций — описание реализации чего-либо. Чаще всего она применяется для реализации вариантов использования и представления совместного поведения группы элементов (обычно классов), которые решают некоторую задачу.

Кооперация определяет необходимый для решения поставленной задачи набор кооперирующихся участников в виде ролей (role). В каждом конкретном случае эти роли будут играть конкретные экземпляры классификаторов. Существенные для решаемой задачи взаимоотношения между ролями показываются на диаграмме соединителями (connector), определяя таким образом необходимые связи.

Отдельно от структуры кооперации описывается ее поведение, например, через диаграммы взаимодействия.

Нотация

Кооперация изображается в виде пунктирного эллипса, содержащего имя кооперации (Рисунок 1).

design-pattern-description-1

Рисунок 1. Нотация кооперации

Внутренняя структура кооперации в форме ролей (1) и соединителей (2) может быть показана внутри эллипса на отдельной диаграмме (Рисунок 2).

design-pattern-description-2

Рисунок 2. Кооперация начальника и подчиненного

Отношение обобщения между кооперациями

Кооперации могут описываться как специализации более общих коопераций, то есть могут участвовать в отношении обобщения. Роли в специализированной кооперации либо совпадают с соответствующими ролями исходной кооперации, либо являются их специализациями. Отметим, что исходной и специализированной роли может сопоставляться один и тот же класс, если только он поддерживает интерфейсы, необходимые для обеих ролей.

Например, заместитель начальника подразделения, кроме обычных свойств подчиненного, может обладать правом подписи за начальника. Это можно оформить в виде интерфейса IDeputy, который является специализацией интерфейса ISubordinate (Рисунок 3).

design-pattern-description-3

Рисунок 3. Интерфейс подчиненного с правом подписи

Заместитель с правом подписи может выступать в роли подчиненного (subordinate) в описанной выше (см. Рисунок 2) кооперации с начальником. Однако тип этой роли — ISubordinate — не позволяет начальнику поручить заместителю заключение договора или каким-либо другим образом воспользоваться правом подписи своего подчиненного. Специализация кооперации, показанная ниже (см. Рисунок 4), позволяет решить эту проблему. В ней роль subordinate имеет специализированный тип IDeputy, предоставляющий необходимую операцию.

design-pattern-description-4

Рисунок 4. Специализация кооперации

Использование кооперации

Использование кооперации (collaboration use) показывает, как описываемое кооперацией взаимодействие применяется в заданном контексте.

Для этого сущности из контекста сопоставляются ролям, определенным в кооперации. Сущности могут быть классификаторами, частями структурированных классификаторов или ролями в объемлющей кооперации, т.е. кооперации можно вкладывать одна в другую. В одном структурированном классификаторе может несколько раз использоваться одна и та же кооперация, связывая различные части.

Экземпляры кооперации изображаются пунктирным эллипсом. Внутрь эллипса помещаются имя использования (1) и имя кооперации (2), разделенные двоеточием.

design-pattern-description-5

Рисунок 5. Нотация использования кооперации

Сопоставление ролям конкретных классификаторов изображается с помощью пунктирной линии (1), которая проводится от эллипса, обозначающего кооперацию, к требуемым классификаторам (Рисунок 6). Со стороны классификатора линия подписывается именем соответствующей роли (2).

design-pattern-description-6

Рисунок 6. Внутренняя структура подразделения, заместитель без права подписи

design-pattern-description-7

Рисунок 7. Внутренняя структура подразделения, заместитель с правом подписи

Различие между диаграммами, представленными на этих рисунках состоит в том, что в первом случае заместители (их трое!) не обладают правом подписи за начальника, так как используется экземпляр кооперации Subordination (3 на Рисунок 6), а во втором — обладают, так как используется экземпляр кооперации Subordination with power to sign (1 на Рисунок 7). Заметим, что заместители начальника на обеих диаграммах описывается одним и тем же классом (4 на Рисунок 6 и 2 на Рисунок 7).

Образцы проектирования

Для описания образцов проектирования прекрасно подходит сущность кооперация, а точнее ее параметризованный вариант, который носит название шаблон кооперации (collaboration template).

Пример описания образца проектирования Publisher-Subscriber

В качестве примера рассмотрим описание образца проектирования Publisher-Subscriber, который применяется в случае, когда некоторое множество объектов должно следить за изменением состояния другого объекта. Объект, изменение состояния которого требуется отслеживать, называется издателем (publisher), а объекты, которые следят за его состоянием, называются подписчиками (subscriber). Отсюда и название образца проектирования.

Каждый подписчик — экземпляр классификатора с типом параметра шаблона кооперации SType (1), а издатель — экземпляр классификатора с типом параметра шаблона кооперации PType (2).

Издатель (роль Publisher (3)) должен определить две операции subscribe() и unsubscribe(), которые служат для того, чтобы подписать, и соответственно, отписать подписчика (роль Subscriber (4)) от нотификации. Нотификация осуществляется путем вызова метода update(), который определен в интерфейсе ISubscriber. Этот интерфейс должен быть реализован (5) каждым подписчиком (Рисунок 8).

design-pattern-description-8

Рисунок 8. Образец проектирования Publisher-Subscriber

Пример описания и использования образца проектирования Composite

Кооперация для образца проектирования Composite представлена ниже (Рисунок 9).

design-pattern-description-9

Рисунок 9. Кооперация для образца проектирования Composite

Данный образец проектирования используется при обработке иерархических структур, в которых индивидуальные объекты собраны в группы, а группы (и индивидуальные объекты) собраны в группы следующего уровня и так далее. При этом требуется обеспечить унифицированную обработку, как отдельных объектов, так и групп объектов, которые должны иметь информацию о своем составе.

Для данной кооперации введены роли Node, Composite и Leaf, с соответствующими шаблонными типами NType, LType и CType. При этом тип NType является абстрактным суперклассом для своих специализаций: LType и CType. Общее поведение описано в классе NType и должно быть реализовано, возможно, различным образом, в специализирующих конкретных подклассах CType и LType. Помимо обобщения, вводится композиция, благодаря которой группы знают свой состав (child), и все объекты знают группу, в которую входят, если она есть (parent).

В качестве примера рассмотрим следующую предметную область. Есть предприятие (company), оно состоит из отделов (department), каждый из которых имеет штатное расписание, в котором указаны должности (position). Как отделы, так и должности образуют иерархическую структуру.

Диаграмма классов для данного случая приведена ниже.

design-pattern-description-10

Рисунок 10. Исходная ситуация для применения образца проектирования Composite

Теперь мы можем дважды применить кооперацию (образец проектирования Composite), как показано ниже (см. Рисунок 11). На этом рисунке изображены два применения кооперации (collaboration use) (1), причем каждое применение имеет собственное имя (2), что позволяет отличать их друг от друга. Далее, роли кооперации связываются с помощью пунктирных линий с классами модели, показывая тем самым, какую роль должен играть данный класс, участвуя в кооперации. При этом для каждой роли может быть указан свой класс (3), а может быть так, что один класс играет сразу несколько ролей (4).

design-pattern-description-11

Рисунок 11. Применение образца проектирования Composite

Вывод

Простая нотация (см., например, Рисунок 11) несет значительную семантическую нагрузку.

Во-первых, приведенная нами в качестве примера диаграмма определяет структуру, причем в этой структуре может быть определено гораздо больше элементов, чем нарисовано на диаграмме. В частности, подразумевается, что в модели определены отношения композиции для подразделений и должностей (хотя они и не присутствуют на диаграмме), а сами классы обладают нужными средствами для применения образца (то есть, реализованы роли child и parent, см. Рисунок 9).

Во-вторых, если применяемая кооперация содержит не только определение структуры, но и определение поведения, то это поведение определяется для классов, которые играют роли кооперации. Поведение кооперации может быть определено указанием атрибутов и операций ролей и описанием, например, диаграммы коммуникации или последовательности, описывающей взаимодействие ролей.

Таким образом, применение UML позволяет наглядно, точно и кратко описывать весьма сложные образцы проектирования и их практическое применение.

Список литературы

Денис Иванов, Федор Новиков