Лабораторная работа №1. UML. Практикум по промышленному
Скачать 1.02 Mb.
|
Пример создания UML-диаграмм архитектуры проекта с помощью PlantUMLЗадание: разработать эскиз проекта системы управления книгами в библиотеке. При выполнении задания ограничимся диаграммами классов и последовательности. Создадим новый проект. В меню необходимо выбрать File>New Project. В диалоге выбора шаблона проекта, не нужно выбирать никакого шаблона (т.е. проект будет пустой, но с одним одноименным модулем). Название проекта UMLLab. Если появится запрос на замену текущего проекта создаваемым, то лучше разрешить замену, так как старый проект уже не нужен. После открытия проекта разверните содержимое модуля UMLLab с помощью серого треугольника. Папка .idea и файл UMLLab.iml хранят в себе все настройки проекта и модуля. Папка src будет включать в себя исходные коды .java программы. Создание диаграммы классовОткройте контекстное меню модуля и выберите создание новой UMLдиаграммы классов. В качестве имени файла можно ввести LibraryClassDiagram. В итоге среда разработки примет следующий вид. В центре показан “код” диаграммы, которая загружается в качестве примера каждый раз при создании новой диаграммы классов. Слева показана уже нарисованная диаграмма. При редактировании кода диаграмма сразу же изменяется. Код диаграммы начинается строчкой @startumlи заканчивается @enduml. Между ними описываются сущности (классы, интерфейсы, перечисления и т. д.), а также связи между сущностями. Как именно это делать, будет пояснено ниже, но полнуюинформациюможнопосмотретьвфайле PlantUML_Language_Reference_Guideили по адресу http://plantuml.sourceforge.net/classes.html. Удалим все, что находится между конечной и начальной строкой и приступим к проектированию системы управления книгами в библиотеке. Так как составляется диаграммаклассов, то главными сущностями будут именно классы, и диаграмма будет описывать большей частью структурусистемы. В будущей системе будут классы Book и Library. Создадим их. @startuml class Book { } class Library { } @enduml Видно, что ни атрибутов, ни операций у этих двух классов пока нет. Добавим их исходя из необходимой функциональности. У класса Book должны быть атрибуты: международный стандартный книжный номер (ISBN), название, автор, номер книги в библиотеке, год издания, а также операции, устанавливающие и получающие данные атрибуты. Однако по причине их многочисленности и общеупотребительности, эти операции не будут включаться в диаграмму классов. Помимо них, у класса Book нет других операций. В классе Library должны быть атрибуты: количество книг и название библиотеки. Операции: найти нужную книгу по какомулибо параметру, например, по ISBN, добавить новую книгу в библиотеку, удалить необходимую книгу. @startuml skinparam classAttributeIconSize 0 class Book { ISBN : String author : String [1..*] numberInLibrary : long year : int } class Library { numberOfBooks : long name : String +findBookByISBN(ISBN : String) : Book +addNewBook(book : Book) : boolean +deleteBook(number : long) : boolean } @enduml Строка “skinparam classAttributeIconSize 0” отключает изображение метки видимости в виде картинки. Это нужно потому, что символы меток видимости стандартизированы, а картинки нет, поэтому лучше использовать символы, если нет соглашений об иных обозначениях меток видимости. Можно задать вопрос, а где же указано то, что в библиотеке хранятся книги? Пока на диаграмме изображены два разрозненных класса. Добавим эту информацию. Это можно сделать двумя путями, добавить некий атрибут в класс Library, который будет представлять собой список книг, хранящихся в библиотеке, а можно эту же самую информацию представить в виде ассоциации, но не однонаправленной, а двунаправленной. Представление такой информации в виде ассоциации нагляднее, поэтому лучше выбрать именно ассоциацию. Почему ассоциация двусторонняя? Потому что не только библиотеке нужно хранить в себе информацию о всех книгах, входящих в неё, но и книга должна знать, какой библиотеке она принадлежит. Добавим информацию об этой ассоциации, дописав следующую строчку. Book "0..*" <> "1" Library На диаграмме сразу отобразились все изменения, классы были перекомпонованы. Данная ассоциация может быть прочитана как “в библиотеке может быть от нуля до бесконечности книг, а у книги может быть только одна библиотека (в которой она находится)”. Продолжим конкретизировать эскиз. В библиотеке могут находиться также не только обычные книги, но и, к примеру, журналы. Самым очевидным решением было бы сделать класс Magazine подклассом класса Book. Но у журналов нет ISBN и конкретного автора! К тому же для большинства журналов для обозначения даты издания только года недостаточно. Соответственно, архитектурным решением может быть сделать общий абстрактный класс AbstractBook, в котором будут реализованы общие операции для всех сущностей, что находятся в библиотеке. От него будут наследоваться классы Book и Magazine. Но нельзя создать никаких экземпляров класса AbstractBook, а нам необходимо именно ими управлять в классе Library. Это решение не подходит, либо должно быть усовершенствовано. Далее, для того, чтобы сделать возможным управление классаминаследниками AbstractBook, можно вынести методы из AbstractBook в интерфейс LibraryEntity. Абстрактный класс будет реализовывать этот интерфейс. Таким образом, Library сможет управлять объектами типа LibraryEntity. Например, станет возможным такой код. LibraryEntity entity = new Book(); Почему такое возможно? Потому что абстрактный класс AbstractBook реализует интерфейс LibraryEntity, а класс Book наследует абстрактный класс AbstractBook. Язык Java позволяет присваивать интерфейсу ссылки на объекты тех классов, которые реализуют этот интерфейс. Это решение и является подходящим для такой ситуации. Соответственно, будут изменения и в классе Library, изменятся названия атрибутов, добавятся новые операции и изменятся старые. Дополнительно изменено имя свойства “numberInLibrary” на “ID”. Если бы не было этапа проектирования, который сейчас и выполняется, то именно сейчас пришлось бы переписывать огромное количество кода. Именно поэтому принцип “сначала спроектируй потом пиши код” позволяет повысить эффективность. Сейчас изменить архитектуру, даже полностью её переписать, будет намного легче, чем переписать весь код, который её в будущем будет реализовывать. Внесем все эти изменения в UMLкод. @startuml skinparam classAttributeIconSize 0 interface LibraryEntity { +getID() : long +getName() : String } abstract class AbstractBook { ID : long name : String } class Book { ISBN : String author : String [1..*] year : int } class Magazine { datePublication: LocalDate publisher : String } class Library { numberOfEntities : long name : String +findEntityByName(name : String) : LibraryEntity +findEntityByID(id : long) : LibraryEntity +addNewEntity(entity : LibraryEntity) : boolean +deleteEntity(id : long) : boolean } LibraryEntity "0..*" <> "1" Library LibraryEntity <|.. AbstractBook AbstractBook <| Book AbstractBook <| Magazine @enduml Добавим комментарий к классу Magazine. class Magazine { datePublication: LocalDate publisher : String } note right: LocalDate is from DateTime API in Java 8 Эта диаграмма классов только поверхностно охватила систему управления книгами, но этого вполне достаточно в качестве примера, как следует начинатьразрабатывать архитектуру приложений. Безусловно, в этом примере можно сделать еще много уточнений и усовершенствований, прежде чем на основе полученной архитектуры можно будет сделать рабочий проект. Создание диаграммы последовательностейСоздадим в этом же проекте (модуле) файл диаграммы последовательностей. Имя диаграммы: LibrarySequenceDiagram. Так как это диаграмма последовательности, то с помощью её мы сможем показать, какова же последовательность действий в проектируемой системе. Иначе говоря, что же будет делать система при наступлении той или иной ситуации? Разработаем две диаграммы последовательности: одну для сценария нахождения чеголибо в библиотеке по его имени (name), вторую для сценария удаления чеголибо из библиотеки по идентификатору (ID). ПолнуюинформациюоспецификацииPlantUMLдлядиаграммпоследовательностейможнопосмотреть в файле PlantUML_Language_Reference_Guide или по адресу http://plantuml.sourceforge.net/sequence.html. Сценарийнахождениячего-либовбиблиотекепоимени Сначала необходимо определить объекты, которые посылают друг другу сообщение (иначе говоря, вызывают или какимлибо образом используют друг друга). Когда в классе Library вызывается функция findEntityByName(), то эта функция будет брать список всех сущностей LibraryEntityList, хранящихся в библиотеке, и выполнять в нем поиск. Почему этот список сущностей не был указан в атрибутах в диаграмме классов? Потому что этот список там представлен в виде двусторонней ассоциации. Поэтому имя этого списка будет в диаграмме последовательности, но не будет в диаграмме классов. Функция findEntityByName() будет вызываться классомклиентом библиотеки LibraryClient, которого также не было на диаграмме классов. Таким образом, можно написать следующий код, отображающийся в UML. @startuml LibraryClient > Library : findEntityByName(name : String) Library > LibraryEntityList : getEntity LibraryEntityList > Library: anEntity @enduml В диаграмме происходит следующее: LibraryClient вызывает у Library метод findEntityByName, Library запрашивает у списка LibraryEntityList одну сущность, и эта сущность передается библиотеке. Так как при поиске необходимого элемента в библиотеке происходит перебор всех элементов, то в диаграмме необходимо отобразить, что этот процесс происходит в цикле. Также нужно указать, что каждый раз после возврата элемента из списка происходит проверка на соответствие поисковой строке. @startuml LibraryClient > Library : findEntityByName(name : String) loop all entities in list Library > LibraryEntityList : getEntity LibraryEntityList > Library : anEntity alt anEntity.name == name Library > LibraryClient : anEntity end end @enduml Однако по такой диаграмме последовательности становится понятно, что в LibraryClient будут “возвращаться” много элементов библиотеки, если они будут подходить под поисковый запрос. Значит, необходимо возвращать результаты запроса не отдельными элементами (классуклиенту обрабатывать большое количество результатов неудобно), а отдельным классом ResultEntityList. Значит, каждый раз, когда полученный из списка anEntity будет удовлетворять запросу, он будет отправляться в ResultEntityList, а по завершении цикла, список результатов ResultEntityList будет возвращен клиенту LibraryClient. @startuml LibraryClient > Library : findEntityByName(name : String) loop all entities in list Library > LibraryEntityList : getEntity LibraryEntityList > Library : anEntity alt anEntity.name == name Library > ResultEntityList : anEntity end end ResultEntityList > LibraryClient : resultList @enduml Сценарийудалениячего-либоизбиблиотекипоидентификатору Клиент библиотеки посылает библиотеке запрос на удаление элемента из библиотеки. Класс библиотеки просматривает весь список своих элементов и ищет такой элемент, ID которого совпадает с запрашиваемым. Если такой элемент находится (используется фрейм alt), то из списка этот элемент удаляется, а клиенту возвращается значение true (элемент на самом деле удален). Если же в списке не было найдено никакого подходящего элемента, то никакого элемента не было удалено, и клиенту возвращается значение false. @startuml LibraryClient > Library : deleteEntity(id : long) loop all elements in list Library > LibraryEntityList : getEntity LibraryEntityList > Library : anEntity alt anEntity.id == id Library > LibraryEntityList : deleteFromList(entity : LibraryEntity) Library > LibraryClient : true end end Library > LibraryClient : false @enduml Коррекция диаграммы классовПосле создания диаграмм последовательностей, стало ясно, что диаграмма классов нуждается в доработке: появился класс LibraryClient, который связан отношением зависимости с классом Library. Изменились возвращаемые значения в операциях поиска в классе Library. @startuml skinparam classAttributeIconSize 0 interface LibraryEntity { +getID() : long +getName() : String } abstract class AbstractBook { ID : long name : String } class Book { ISBN : String author : String [1..*] year : int } class Magazine { datePublication: LocalDate publisher : String } note right: LocalDate is from DateTime API in Java 8 class Library { numberOfEntities : long name : String +findEntityByName(name : String) : ResultEntityList +findEntityByID(id : long) : ResultEntityList +addNewEntity(entity : LibraryEntity) : boolean +deleteEntity(id : long) : boolean } class LibraryClient LibraryEntity "0..*" <> "1" Library LibraryEntity <|.. AbstractBook AbstractBook <| Book AbstractBook <| Magazine LibraryClient ..> Library @enduml Задания для самостоятельной работыВ каждом варианте необходимо разработать начальную архитектуру проекта. Архитектура должна быть представлена в виде диаграммы классов и двух диаграмм последовательности, представляющих наиболее сложные к пониманию взаимодействия составляющих проект сущностей. Вариант №1, 16Проект “Управление магазином сладостей”. Вариант №2, 17Проект “Расписание железнодорожной станции”. Вариант №3, 18Проект “Рисование геометрических фигур”. Вариант №4, 19Проект “Редактирование видеофайлов”. Вариант №5, 20Проект “Электронная зачетная книжка”. Вариант №6, 21Проект “Хранитель паролей”. Вариант №7, 22Проект “Расчет скидок” для магазина электронной техники. Вариант №8, 23Проект “Управление установленными приложениями”. Вариант №9, 24Проект “Модель Солнечной системы”. Вариант №10, 25Проект “Магазин электронных книг”. Вариант №11, 26Проект “Расписание школьных уроков”. Вариант №12, 27Проект “Электронный школьный дневник”. Вариант №13, 28Проект “Текстовый редактор”. Вариант №14, 29Проект “Справочник болезней”. Вариант №15, 30Проект “Управление закладками”. Литература, ссылкиФаулер M. UML. Основы, 3е издание. – Пер. с англ. – СПб: СимволПлюс, 2004. – 192 с., ил. Иванов Д. Ю., Новиков Ф. А. Основы моделирования на UML: Учеб. пособие. – СПб.: Издво Политехн. унта, 2010. – 249с. https://ru.wikipedia.org/wiki/UML https://ru.wikipedia.org/wiki/%C4%E8%E0%E3%F0%E0%EC%EC%E0_%EA%EB%E0%F1%F1% EE%E2 https://ru.wikipedia.org/wiki/IntelliJ_IDEA http://blog.gelin.ru/2014/02/plantuml.html http://plantuml.sourceforge.net/PlantUML_Language_Reference_Guide.pdf |