Вступление
Я думаю, каждый слышал в детстве такую поговорку как "Семь раз отмерь, один раз отрежь". В программировании так же. Лучше всегда обдумать реализацию до того, как вы потратите время на её исполнение. Часто приходится при реализации создавать классы, придумывать их взаимодействие. И часто визуальное представление этого может помочь решить задачу наиболее правильным образом. В этом нам и помогает UML.Что такое UML?
Если посмотреть картинки в поисковых системах, то станет понятно, что UML – это что-то про схемы, стрелочки и квадратики. Что важно, что UML переводится как Unified Modeling Language. Важно тут слово Unified. То есть наши картинки поймём не только мы, но и остальные, кто знает UML. Получается это такой международный язык рисования схем.
Как гласит Википедия
UML — это язык графического описания для объектного моделирования в области разработки программного обеспечения, моделирования бизнес-процессов, системного проектирования и отображения организационных структур.Самое интересное, о чём не все задумываются или догадываются, UML имеет спецификации. Причём даже есть спецификация UML2. Подробнее со спецификацией можно ознакомиться на сайте Object Management Group. Собственно, эта группа и занимается разработкой спецификаций UML. Интересно и то, что UML не ограничивается описанием структуры классов. Существует множество типов UML диаграмм. Краткое описание типов UML диаграмм можно увидеть в той же Википедии: UML - диаграммы или в видео Тимура Батыршинова Обзор UML диаграмм. UML так же широко применяется при описании различных процессов, например здесь: Единый вход с использованием JWT. Возвращаясь к использованию UML диаграмм классов, стоит отметить книгу Head First : Паттерны проектирования, в которой паттерны иллюстрируются теми самыми UML диаграммами. Выходит, что UML действительно используется. И выходит, что знание и понимание его применения довольно полезный навык.
Применение
Разберём, как с этим самым UML можно работать из IDE. В качестве IDE возьмём IntelliJ Idea. Если использовать IntelliJ Idea Ultimate, то у нас "из коробки" будет установлен плагин "UML Support". Он позволяет автоматически генерировать красивые диаграммы классов. Например, через Ctrl+N или пункт меню "Navigate" -> "Class" перейдём в класс ArrayList. Теперь, через контекстное меню по имени класса выберем "Diagram" -> "Show diagram popup". В результате мы получим красивую диаграмму:Но что, если хочется самому нарисовать, да ещё и нет Ultimate версии Idea? Если мы используем IntelliJ Idea Community Edition, то у нас нет другого выбора. Для этого нужно понять, как такая UML схема устроена.
Для начала нам понадобится установить Graphviz. Это набор утилит для визуализации графов. Его использует плагин, который мы будем применять.
После установки необходимо добавить каталог bin из каталога установленного Graphviz в переменную среды окружения PATH.
После этого в IntelliJ Idea в меню выбрать File -> Settings. В окне "Settings" выбрать категорию "Plugins", нажать кнопку "Browse repositories" и установить плагин PlantUML integration.
Чем так хорош этот PlantUML? Он использует для описания UML язык описания графов под названием "dot" и это позволяет ему быть более универсальным, т.к. данный язык используется не только PlantUML. Более того, всё что мы ниже сделаем мы можем выполнить не только в IDE, но и в онлайн сервисе planttext.com.
После установки плагина PlantUML у нас появится возможность через "File" -> "New" создавать UML диаграммы. Давайте выполним создание диаграммы типа "UML class".
В ходе этого автоматически генерируется шаблон с примером. Удалим его содержимое и создадим своё, вооружившись статьёй с Хабра: Отношения классов — от UML к коду. А чтобы понять, как это изобразить в тексте, возьмём мануал по PlantUML: plantuml class-diagram. В нём в самом начале представлена табличка с тем, как нужно описывать связи:
Про сами же связи можем ещё подсматривать сюда: "Отношения между классами в UML. Примеры".
На основе этих материалов приступим к созданию нашей UML диаграммы. Добавим следующее содержимое, описывающее два класса:
@startuml
class ArrayList {
}
class LinkedList {
}
@enduml
Чтобы увидеть результат в Idea, выберем "View" -> "Tool Windows" -> "PlantUML". Мы получим просто два квадрата, обозначающие классы.
Как мы знаем, оба эти класса реализуют интерфейс List. Данное отношение классов так и называют — реализация (realization). Для изображения такой связи используют стрелку с пунктирной линией. Изобразим её:
interface List
List <|.. ArrayList
List <|.. LinkedList
List
— один из дочерних классов Collection
. То есть он наследуется от Collection. Эта связь называется обобщением (generalization). Выглядит как стрелка с обычной непрерывной линией. Изобразим её:
interface Collection
Collection <|-- List
Для следующего типа связи добавим в описание класса ArrayList
запись о package private массиве элементов:
~Object[] elementData
Теперь мы хотим показать, что ArrayList
содержит какие-то объекты. В данном случае будет тип связи — агрегация (aggregation).
Агрегатом в данном случае является ArrayList
, т.к. он содержит другие объекты. Агрегацию мы выбираем потому, что объекты в списке могут жить и без списка: они не являются его неотъемлемыми частями. Их время жизни не привязано к времени жизни списка. Агрегат с латинского переводится как "собранный", то есть что-то, составленное из чего-то. Например, в жизни, есть насосный агрегат, который состоит из насоса и двигателя. Сам агрегат можно разобрать, оставив что-то из его составных частей. Например, чтоб продать или поставить в другой агрегат. Так и в списке. И выражается это в виде пустого ромбика у агрегата и непрерывной линии. Изобразим это следующим образом:
class Object{
}
ArrayList o- Object
Теперь мы хотим показать, что в отличие от ArrayList
, класс LinkedList
содержит в себе Node
— контейнеры, ссылающиеся на хранимые данные. В данном случае Node
являются частью самого LinkedList
и не могут жить отдельно. Node
не является непосредственнохранимым содержимым, а только содержит ссылку на него. Например, когда мы добавляем в LinkedList
какую-нибудь строку, мы добавляем новый Node
, который содержит ссылку на эту строку, а также ссылку на предыдущий и следующий Node
.
Такой тип связи называется композицией (Composition). Для отображения у композита (того, кто состоит из частей) рисуется закрашенный робмик, к нему ведёт непрерывная линия. Запишем теперь это в виде текстового отображения связи:
class Node{
}
LinkedList *-- Node
И теперь необходимо научиться отображать ещё один важный тип связи — зависимость (dependency relationship). Он используется тогда, когда один класс использует другой, при этом класс не содержит в себе используемый класс и не является его наследником. Например, LinkedList
и ArrayList
умеют создавать ListIterator
. Отобразим это в виде стрелок с пунктирной линией:
class ListIterator
ListIterator <... ArrayList : create
ListIterator <... LinkedList : create
Выглядеть после всего это будет следующим образом:
Детализировать можно настолько, насколько это необходимо. Все обозначения указаны тут: "PlantUML — Диаграмма классов".
Кроме того, в рисовании такой схемы нет ничего сверхъестественного, и при работе над своими задачами её можно быстро рисовать от руки. Это позволит развить навыки продумывания архитектуры приложения и поможет выявить недостатки структуры классов на раннем этапе, а не когда вы уже потратите день на реализацию неправильной модели. Мне кажется, это неплохая причина для того, чтобы попробовать? )
Автоматизация
Есть различные способы автоматической генерации PlantUML диаграмм. Например, в Idea есть плагин SketchIT, но рисует он их не совсем правильно. Скажем, неправильно рисуется имплементация интерфейсов (отображается как наследование). Также в интернете есть примеры того, как это встроить в жизненный цикл сборки вашего проекта. Допустим, для Maven есть пример использования uml-java-docklet. Для того, чтобы показать как это, воспользуемся Maven Archetype для быстрого создания Maven проекта. Выполним команду:mvn archetype:generate
На вопросе выбора фильтра (Choose a number or apply filter) оставляем default, просто нажав Enter. Это всегда будет "maven-archetype-quickstart". Выбираем самую последнюю версию.
Далее отвечаем на вопросы и завершаем создание проекта:
Так как Maven не является целью данной статьи, ответы на свои вопросы по Maven можно найти в Maven Users Centre.
В сгенерированном проекте откроем на редактирование файл описания проекта, pom.xml. В него скопируем содержимое из описания uml-java-docklet installing.
Используемый в описании артефакт не удалось найти в репозитории Maven Central. Но у меня заработало с этим: https://mvnrepository.com/artifact/com.chfourie/uml-java-doclet/1.0.0.
То есть надо в том описании просто заменить groupId с "info.leadinglight" на "com.chfourie" и поставить версию "1.0.0".
После этого можем выполнить в каталоге, где находится файл pom.xml эти комманды:
mvn clean install
и mvn javadoc:javadoc
.
Теперь, если открыть сгенерированную документацию (explorer target\site\apidocs\index.html), мы увидим UML схемы. Кстати, имплементация тут уже отображается верно )
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ