воскресенье, 20 января 2019 г.

PlantUML в Visual Studio Code. Диаграммы классов


В предыдущей статье инструкция что устанавливать и как начать работать. Сегодня поговорим про нотацию используемую в PlantUML для построения диаграмм классов. Напоминаю, что диаграмма классов (англ. Static Structure diagram) это структурная диаграмма нотации UML, демонстрирующая общую структуру иерархии классов системы, их кооперацию, атрибуты (поля), методы, интерфейсы и взаимосвязи между ними. Широко применяется не только для документирования и визуализации, но также для конструирования посредством прямого или обратного проектирования.
Итак, начинаем.

Задание класса

Класс и его элементы в PlantUML можно задать двумя способами: указав имя класса и через
двоеточие член класса или указав перед именем ключевое слово class:
Первый способ избыточен (нам для каждого свойства придется указывать к какому классу оно относится), да и когда мы хотим промоделировать отношения классов без их внутреннего устройства - неприменим. Поэтому далее я буду использовать нотацию с применением ключевого слова class. Для того чтобы редактору дать понять какие члены относятся к классу используются фигурные скобки. Дальше редактор определяет данные или метод находятся перед ним на основе круглых скобок. Пусть у нас есть самолет, который умеет взлетать, и у него есть некоторая коллекция двигателей. Текст такой диаграммы может иметь вид:
@startuml Example
class Plane {
Engines : Engine[1..*]
TakesOff()
}

class Engine {

}
@enduml
В редакторе такому коду будет соответствовать следующая диаграмма:
Это последняя картинка, когда я делаю скриншот с Visual Studo Code, дальше буду показывать текст разметки и результирующую диаграмму.

Члены класса

Как я написал выше, методы и поля PlantUML отличает по круглым скобкам. Для задания модификатора доступа используются те же значки, что и в нотации UML, по умолчанию, они заменяются значками:
Т.е. мы можем задать для нашего самолета, что двигатели являются приватными, запуск двигателей доступен в потомках, техобслуживание является доступным только в рамках сборки, а команда на взлет - публичной:
class Plane {
-Engines : Engine[1..*]
#StartEngine(int index)
~CarryOutMaintenance()
+TakesOff()
}

В этом случае диаграмма будет выглядеть вот так:

Если лень запоминать значки, то можно вернуть отображение к стандартной UML нотации, для этого достаточно добавить в файл строку:
skinparam classAttributeIconSize 0

После этого, отображение вернется к стандартным значкам UML:

Кроме модификаторов доступа можно как и в UML помечать статические члены класса (подчеркнутые) и абстрактные (выделяются курсивом). Для этого используются ключевые слова static и abstract указываемые перед именем члена. Давайте добавим статическую переменную Count для хранения общего количества самолетов и абстрактный метод Fly:
class Plane {
-Engines : Engine[1..*]
{static} -Count : int
#StartEngine(int index)
~CarryOutMaintenance()
{abstract} +Fly()
+TakesOff()
}

Вот так это будет выглядеть на диаграмме:

Абстрактные классы и интерфейсы

Для отображения абстрактных классов и интерфейсов, что логично, используются ключевые слова abstract и interface:
interface IPlane {
+Fly()
+TakesOff()
}

abstract class BasePlane {
{abstract} #StartEngine(int index)
{abstract} +Fly()
+TakesOff()
}

Отображаться эти элементы будут аналогично классам, только будет отличаться буква перед именем класса и имя класса будет курсивом:

Связи

1. Обобщение, оно же - наследование. Изображается стрелкой с треугольником на конце. Для задания связи между объектами в PlantUML применяется синтаксис состояний из имен двух элементов диаграммы и стрелки между ними. Для треугольной стрелки со сплошной линией отношение будет заданно следующим образом:
BasePlane <|-- Plane

Внешний вид такой связи на диаграмме:

2. Реализация - классом некоторого интерфейса. Изображается как наследование, только линия пунктирная. Для задания такой линии необходимо минусы заменить на точки:
IPlane <|.. BasePlane

Выглядит связь следующим образом:

3. Зависимость - показывает, что при изменении одного класса, должен измениться и зависимый. Связь отличается от предыдущие отсутствием вертикальной черты, также обратите внимание, что направление стрелки можно задать и в противоположную сторону:
AirportTechnician ..> Plane

Выглядит:


4. Ассоциация - ссылка одного класса на другой. Изображается обычной стрелкой с названием отношения:
Person <-- span=""> Plane : Управляется

Внешний вид такой связи на диаграмме:

5. Агрегация - белее жесткое уточнение ассоциации, задающее отношение часть-целое. Причем часть может быть отделена и использована сама по себе (например, мы можем снять двигатель и переставить на другой самолет). Задается такое отношение указанием вместо стрелки латинской буквы o:
Plane o-- Engine

Выглядит:

6. Композиция - более жесткое уточнение ассоциации, задающее отношение часть-неотделимое целое. Если в предыдущем примере мы не моделируем снятие двигателя с самолета, и уничтожение самолета повлечет за собой уничтожение двигателя, в этом случае заменяем o на *:
Plane *-- Engine

Выглядит связь аналогично предыдущей, только ромб будет закрашен:

Пример

В заключении давайте посмотрим, как будет выглядеть итератор в виде кода PlantUML и диаграммы UML соответственно.
Код:
@startuml Example

skinparam classAttributeIconSize 0

interface IEnumerable {
+IEnumerator GetEnumerator()
}

interface IEnumerator {
+object Current
+bool MoveNext()
+void Reset()
}

IEnumerable ..> IEnumerator : Создает

class Array {
+int Length
{static} +Sort(Array a)
+IEnumerator GetEnumerator()
}

Array ..|> IEnumerable

class ArrayEnumerator {

}

ArrayEnumerator ..|> IEnumerator

Array ..> ArrayEnumerator : Создает
ArrayEnumerator --> Array : Перемещается по

IEnumerable -[hidden]> IEnumerator
IEnumerable -[hidden]-> Array
IEnumerator -[hidden]-> ArrayEnumerator
@enduml

Обратите внимание на три строки перед @enduml, они позволяют задать расположение элементов относительно друг друга, я потом отдельно о этих связях расскажу, а то и так уже много получилось. Ну и внешний вид получившейся диаграммы:
Статический метод Sort добавил для примера, на самом деле у Array еще много чего еще есть, но все перечислять лень :)

2 комментария:

  1. Алексей, спасибо большое, именно этой информации очень не хватало к предыдущему посту! Осталось заучить все эти стрелки, а это легко делается на практике :)
    Работать с хорошо спроектированной системой всегда приятно, когда у меня нет 1000 вопросов к РП и все ответы можно найти в схемах, жаль, что в большинстве случаев всё в точности на оборот :) хорошо еще, если сам накидаешь схему, для фиксации и для упорядочивания написания кода.

    ОтветитьУдалить
    Ответы
    1. Пожалуйста. Да, картинки наш мозг воспринимает лучше, поэтому лучшая документация - код, а вот чтобы быстро разобраться какой код смотреть, картинки - наше все.

      Удалить