#java2uml — Попытка отыскать Java2UML


Содержание

Uml диаграмма по готовому коду java

19.02.2020, 12:48

Генерация UML по существующему коду в NetBeans или Eclipse
Доброго времени суток. Есть небольшой проект (написан не мной) со сложной иерархией сущностей.

Можно ли создать UML — диаграмму в NetBeans 8.1 IDE ПО ГОТОВОМУ КОДУ
Здравствуйте. Есть готовый проект. Можно ли по готовому коду создать UML — диаграмму? Добавлено.

UML счетфактура java
Здравствуйте,помогите разобраться с UML диаграммой на основании этой диаграммы мне нужно создать.

JAVA+UML диаграмма классов
Всем привет! Построить UML-диаграмму классов для реального объекта — магазин БТ отразить.

UML-диаграмма
Можете проверить, правильно ли составлена UML-диаграмма, и, если не правильно, поправить? .

java2uml.ru

Основные сведения:

Безопасность для детей:

Анализ данных java2uml.ru показал, что у этого домена отсутствует рейтинг Alexa и посещаемость данного сайта неизвестна.

Опен-сорс проект Удобный автопостроитель красивых диаграмм классов UML на основании исходного кода Java. Вам ну. Не работает в Linux Ubuntu 16.04. При запуске jar-файла открывается окно приложения. Указываю папку .

Дата последней проверки:

27 Августа 2020

Наиболее популярные страницы домена:

Опен-сорс проект Удобный автопостроитель красивых диаграмм классов UML на основании исходного кода Java. Вам ну.

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

Опен-сорс, все исходники доступны на гитхабе Не продуцирует ненужные комментарии в вашем java-коде Легковесный Основан на мета-языке PlantUML, что делает его гибким, совместимым и легкоподдерживаемым .

Занятие 3

Основные понятия ООП (повторение)

Внимание! На данном этапе обучения вы уже должны владеть знаниями по этой теме. Если их нет, а материалы для повторения непонятны или недостаточны, вы с заданиями не справитесь! Необходимо срочно обратиться к литературе по данной теме.

Роль объектно-ориентированной методологии

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

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

С появлением персональных компьютеров и постепенным внедрением их во все сферы человеческой деятельности, чисто вычислительные задачи отходят на второй план. На первый план выдвигаются задачи обработки и манипулирования данными, причем как задачи, так и сами данные очень сильно «привязаны» к некоторой предметной области реального мира. Обслуживание клиентов банка, автоматизация документооборота на крупном предприятии, управление работой железной дороги — вот примеры новых задач, с которыми традиционный подход к программированию справляется не эффективно.

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

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

Объекты

В основе методологии объектно-ориентированного программирования (ООП) лежит понятие объекта. — это некая сущность (например, предмет или процесс) с четко очерченными границами, имеющий смысл в контексте рассматриваемой предметной области. Например, студент Иванов, файл «info.txt», лекция по информационным технологиям — это все примеры объектов.

Каждый объект обладает набором (или атрибутов). Например, студент Иванов имеет свойства Имя , Возраст , Цвет глаз , Оценка по матанализу . С каждым свойством связано текущее значение определенного типа. К примеру, возможные свойства объекта Студент Иванов : Имя = «Вася» , Возраст = 19 , Цвет глаз = СИНИЙ , Оценка по матанализу = 4 .

Совокупность текущих значений свойств объекта обычно называют его .

Для каждого объекта существует определенный набор действий, которые с ним можно произвести. Например, объект файл «info.txt» можно открыть, прочитать, изменить, сохранить, удалить и т.д. Как правило, в результате выполнения действия изменяются значения некоторых свойств объекта. Совокупность всех действий, которые можно произвести над объектом, называется его .

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

Классы

Объекты, обладающие одинаковым набором свойств и одинаковым поведением, относятся к одному классу. — это абстракция, описание группы однородных объектов. Например, студент, лекция, файл, стул — все это примеры классов. Обратите внимание, имеется в виду не конкретный студент, например, Иванов, а студент вообще; этот класс представляет собой обобщенное понятие студента.

У класса есть имя, атрибуты и методы.

Имя выступает в качестве идентификатора класса, т.е. не может быть классов с одинаковыми именами. В качестве имени класса обычно выбирают существительное, описывающее абстрактный объект этого класса ( Студент , Лекция , Файл , Стул — это все имена классов).

Атрибуты класса описывают свойства его объектов. С каждым атрибутом сопоставляется некоторый тип (это может быть обычный тип языка программирования вроде int , char , boolean , а может быть другой класс). Например, класс Зачетная книжка имеет атрибут Номер типа int (целое число) и атрибут Владелец типа Студент .


Методы класса описывают его поведение. Это действия, которые могут выполнять объекты данного класса. В программе, как правило, методы представляют собой функции, которые могут работать с атрибутами класса.

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

Приблизительный перечень классов получают, изучив предметную область программы и разграничив используемые в ней понятия. К этим классам добавляются те, которые необходимы для поддержки алгоритмов, которые будут использоваться в программе. Большая часть их берется в готовом виде из стандартных библиотек — например, класс Vector , реализующий одну из разновидностей списков в Java. Другие выделяются на основе устоявшихся шаблонов (так называемых паттернов проектирования) или личного опыта программиста.

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

Чтобы требуемые методы могли быть реализованы, в классе должны храниться некоторые данные. Например, чтобы студент мог сообщать свое имя, он должен его «знать», а чтобы операция Перевести на следующий курс применительно к студенту имела смысл, он должен «знать» свой текущий курс и при необходимости его увеличивать. Таким образом выделяется набор атрибутов.

В результате мы получаем объектно-ориентированную модель, которую можно использовать в программе.

Литература по теме:

1. Гради Буч. Объектно-ориентированный анализ и проектирование.

Объектно-ориентированное программирование в Java

Классы и объекты

Java — полностью объектно-ориентированный язык, поэтому, как мы уже отмечали, все действия, выполняемые программой, находятся в методах тех или иных классов.

Описание класса начинается с ключевого слова class , после которого указывается идентификатор — имя класса. Затем в фигурных скобках перечисляются атрибуты и методы класса. Атрибуты в языке Java называются полями (в дальнейшем мы будем использовать это наименование). Поля и методы называются членами класса.

Поля описываются как обычные переменные.

Правила записи методов рассматривались на предыдущем занятии.

Опишем для примера класс Dog (собака). У него будет два поля: кличка и возраст. При описании поведения собаки в этом простом примере ограничимся лаем. Конечно, лаять по-настоящему наша собака не будет (ведь это всего лишь программная конструкция), она будет выводить в консоль «гав-гав». Чтобы было интереснее, предположим, что все собаки, с которыми имеет дело наша программа, умны настолько, что когда их вынуждают лаять, они говорят «гав-гав» столько раз, сколько им лет.

Заметим, что в программе уже есть один класс (тот, в котором описан метод main() ). Поскольку этот класс к собакам отношения не имеет, описывать новый класс Dog следует за его пределами.

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

Класс должен описывать некоторое законченное понятие. Это может быть понятие из предметной области программы (собака, велосипед, аквариум, сессия) или понятие, необходимое для работы самой программы (очередь, список, строка, окно, кнопка, программа * ).

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

Методы класса, как правило, работают с данными этого класса. Например, метод voice() в нашем примере обращается к полю age (возраст).

Когда какой-то класс описан, могут создаваться объекты этого класса и с ними можно работать, вызывая их методы (кормить собаку, выгуливать, просить ее лаять — словом делать все то, что позволяет поведение класса, т.е. совокупность его методов).

Для обращения к объектам удобно использовать переменные, имеющие тип класса. Например, для работы с собаками опишем переменную типа Dog :

Переменная типа класса является ссылочной переменной, она не хранит данные (как переменные простых типов int , char и т.д.), а указывает на место в памяти, где эти данные хранятся (как переменные типа массива). Данными, на которые указывает только что описанная переменная x , может быть объект класса Dog . Его необходимо предварительно создать командой new :

Теперь переменная x указывает на некий объект класса Dog , хранящий в памяти свои данные (возраст и кличку). Кроме того, эту собаку можно заставить лаять, вызвав соответствующий метод командой:

Для того, чтобы обратиться к члену класса, необходимо указать его имя после имени объекта через точку.

Обратите внимание, «залаяла» именно та собака, на которую «указывала» переменная x . Если в программе были созданы другие собаки, они будут молчать до тех пор, пока не будет вызван их метод voice() .

Таким образом, когда данные (поля) и команды (методы) описываются в одном классе, они оказываются тесно связаны друг с другом в объектах этого класса. Метод вызывается не сам по себе, а для конкретного объекта и работает с полями именно этого объекта.

не имеют никакого смысла, если употребляются вне методов класса Dog . Обязательно указание на конкретный объект, с которым производится действие. Внутри метода указание на конкретный объект вовсе не обязательно: в рассмотренном примере запись

for ( int i = 1; i

означает, что для определения «продолжительности» лая будет проверяться возраст того самого объекта, для которого будет вызван данный метод. Этот объект обозначается ключевым словом this .

Метод voice() можно было описать и так:

Ключевое слово this в этом примере наглядно указывает, что используется атрибут age именно того объекта класса Dog , для которого вызывается метод voice() .

Когда имя метода или атрибута записывается без указания объекта, всегда подразумевается объект this .

Конструкторы классов

— это особенный метод класса, который вызывается автоматически в момент создания объектов этого класса. Имя конструктора совпадает с именем класса.

Например, в классе Dog может быть конструктор с двумя параметрами, который при создании новой собаки позволяет сразу задать ее кличку и возраст.

public Dog(String n, int a)


Конструктор вызывается после ключевого слова new в момент создания объекта. Теперь, когда у нас есть такой конструктор, мы можем им воспользоваться:

Dog dog1 = new Dog( «Тузик» , 2);

В результате переменная dog1 будет указывать на «собаку» по кличке Тузик, имеющую возраст 2 года. Кстати, этот возраст можно узнать, заставив собаку лаять командой

Конструкторы добавляются в класс, если в момент создания объекта нужно выполнить какие-то действия (начальную настройку) с его данными (полями). Сразу задать кличку и возраст собаки более естественно, чем каждый раз порождать безымянного щенка, а затем давать ему имя и быстро выращивать до нужного возраста (с учетом того, что программа скорее всего обрабатывает данные о собаках, которые на самом деле уже давно родились). Хотя до появления ООП программисты часто делали именно так.

Наследование

— это отношение между классами, при котором один класс расширяет функциональность другого. Это значит, что он автоматически перенимает все его поля и методы, а также добавляет некоторые свои.

Наследование обычно возникает, когда все объекты одного класса одновременно являются объектами другого класса (отношение общее/частное). Например, все объекты класса Студент являются объектами класса Человек . В этом случае говорят, что класс Студент наследует от класса Человек . Аналогично класс Собака может наследовать от класса Животное , а класс Далматинец от класса Собака . Класс, который наследует, называется подклассом или потомком, а класс, от которого наследуют, называется суперклассом или предком.

Заметим, что если класс №2 является потомком класса №1, а класс №3 является потомком класса №2, то класс №3 является также потомком класса №1.

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

Для того, чтобы один класс был потомком другого, необходимо при его объявлении после имени класса указать ключевое слово extends и название суперкласса.

Если ключевое слово extends не указано, считается, что класс унаследован от универсального класса Object.

С класса Object начинается иерархия наследования. Любой другой класс является потомком класса Object и наследует от него три метода:

equals(Object obj) — позволяет сравнивать два объекта. Возвращает true , если объекты равны. Например, мы сравнивали строки (объекты класса String ):

toString() — «переводит» объект в строку (которую можно вывести в методе println() ).

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

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

Модификаторы видимости

Доступ к любому члену класса — полю или методу — может быть ограничен. Для этого перед его объявлением ставится ключевое слово private . Оно означает, что к этому члену класса нельзя будет обратиться из методов других классов.

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

Ключевое слово protected означает, что доступ к полю или методу имеет сам класс и все его потомки.

Если при объявлении члена класса не указан ни один из перечисленных модификаторов, используется модификатор по умолчанию ( default ). Он означает, что доступ к члену класса имеют все классы, объявленные в том же пакете.

Цукерберг рекомендует:  Java - Убрать null

Перепишем класс Dog следующим образом:

Поля age и name окажутся скрытыми. Это значит, что мы не можем изменять их (или считывать их значение) где-либо за пределами класса * . Мы не сможем в методе main() создать объект класса Dog , а затем присвоить его полю age или name новое значение, как в следующем примере:

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

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

Например, в классе Dog есть целочисленное поле age (возраст). Можно оставить его открытым, тогда его при необходимости можно будет изменить простым присваиванием (очень удобно). Но при этом ничто не мешает присвоить этому полю заведомо некорректное значение (например, 666 или -5 или 3000). Это может произойти из-за ошибки в программе. Или, к примеру, пользователь вводит возраст собаки в текстовое поле, а программа присваивает его в ответ на нажатие кнопки (и пользователь может ошибиться). Это нежелательный случай. Лучше сделать поле age закрытым ( private ) и добавить два открытых метода: getAge() и setAge() . Первый метод будет просто возвращать значение скрытого поля:

public int getAge()

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

public void setAge ( int newAge) < if (newAge "Как это понимать? Собака еще не родилась?" ); else if (newAge >30) System.out.println( «Они столько не живут» ); else age = newAge; >

Теперь мы видим, что возраст можно изменить, лишь вызвав метод setAge() , который в случае неподходящего параметра выведет в консоль сообщение и не будет ничего изменять.

Профессиональные программисты, разрабатывающие программы по объектно-ориентированной методологии, скрывают все поля своих классов, создавая для каждого из них открытые методы c приставками get и set , причем в методах set проводятся все необходимые проверки.

Заметим напоследок, что в нашем примере неправильный возраст может «прорваться» через конструктор при создании нового объекта. Никто ведь не мешает написать:

Dog dog1 = new Dog( «Тузик» , 2000);

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

public Dog(String n, int a)

Теперь проверка осуществляется в конструкторе. Попытка завести в программе собаку с явно некорректными данными не увенчается успехом. Если возраст будет меньше 0 или больше 30, присваивание не выполнится и атрибут age будет иметь значение по умолчанию (для типа int это 0). Такая вот маленькая собачка.

Упражнение


Наш класс Dog содержит принципиальную ошибку. Он имеет скрытое поле name , которому можно присвоить начальное значение во время создания объекта, но нельзя изменить и даже узнать впоследствии. Напишите методы getName() и setName() . Никаких проверок проводить не нужно.

Перегрузка

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

Например, в дополнение к конструктору, который уже есть в классе Dog , мы можем описать конструктор без параметров:

В этом конструкторе объекту класса Dog (очевидно, собака с неизвестной кличкой) присваивается при регистрации в программе имя «Незнакомец». Теперь мы можем воспользоваться одним из двух конструкторов:

Dog dog1 = new Dog( «Тузик» , 2); // Собака по кличке Тузик, возраст 2 года Dog dog2 = new Dog(); // Собака по кличке «Незнакомец», возраст 0 Dog dog3 = new Dog(10); // Неверно! Не существует конструктора с такими параметрами

Нельзя создавать несколько одноименных методов с одинаковым числом и типом параметров.

Упражнение

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

Полиморфизм

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

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

Пусть, к примеру, мы хотим расширить наш класс Dog классом BigDog , для того, чтобы наша программа особым образом моделировала поведение больших злых собак. В частности, большие собаки лают по-другому. Во-первых, громче, а во-вторых, они не умеют считать. Поэтому мы переопределим метод voice() :

Теперь создадим в методе main() двух разных собак: обычную и большую и заставим их лаять.

Dog dog = new Dog( «Тузик» , 2); dog.voice(); BigDog bigdog = new BigDog(); bigdog.voice();

Объект подкласса всегда будет одновременно являться объектом любого из своих суперклассов. Поэтому в том же примере мы могли бы обойтись и одной переменной:

Dog dog = new Dog( «Тузик» , 2); dog.voice(); dog = new BigDog(); dog.voice();

Т.е. переменная dog имеет тип Dog , но в третьей строке она начинает указывать на объект класса BigDog , то есть БОЛЬШУЮ собаку, которая при вызове метода voice() будет лаять как БОЛЬШАЯ собака. Это одна из впечатляющих возможностей объектно-ориентированного программирования.

Приемы программирования: наследование и полиморфизм

Главное преимущество полиморфизма — это возможность работать с объектами разных классов, происходящих от одного общего предка так, как будто бы они относились к одному классу.

Рассмотрим типичный пример.

Предположим, мы разрабатываем программу для рисования. В этой программе пользователь может создавать различные фигуры: треугольники, прямоугольники, круги, точки. При этом заранее неизвестно, сколько и каких фигур он создаст. *

Время от времени программа должна выполнять над этими фигурами какие-то действия. Например, когда окно программы сворачивается, а потом снова разворачивается, надо заново нарисовать все эти фигуры. Когда пользователь щелкает по фигуре мышкой, ее надо выделить, а когда пользователь перетаскивает границы фигуры — изменить ее размеры.

Придерживаясь методологии объектно-ориентированного программирования, мы приходим к выводу, что каждая фигура должна рисовать себя «сама». То есть, команды для прорисовки круга выполняются в одном из методов класса Circle, например, в методе paint() . Действительно, все параметры фигуры должны храниться в полях ее класса, поэтому легко можно написать такой метод. Аналогично, фигура «сама» рисует себе выделение — для этого есть метод paintSelection() — и передвигается — метод move(int x, int y) . Задача основной программы — просто обращаться к этим методам при необходимости.

Программа должна где-то хранить объекты, которые создаст пользователь. Поскольку заранее неизвестно, сколько будет этих объектов, необходимо воспользоваться какой-нибудь структурой для хранения множества объектов, например массивом. Но при создании массива требуется указать тип его элементов. А в нашей программе пользователь может создавать самые разные объекты. Так что придется завести несколько массивов: один для точек, один для кругов и так далее. Если понадобится заново нарисовать все объекты на экране, нужно будет перебрать все элементы в каждом из этих массивов:

for ( int i = 0; i points[i].paint(); > for ( int i = 0; i circles[i].paint(); >

. и так далее, для каждого типа фигуры.

Более того, если пользователь щелкнул мышкой по экрану, чтобы выбрать фигуру, программа, получившая координаты мыши, должна найти фигуру, в которую попадают эти координаты. Предположим, каждая фигура сама может осуществить проверку с помощью метода checkPoint(int x, int y) , который возвращает значение true , если точка с координатами x, y находится внутри этой фигуры. Но для того, чтобы вызвать этот метод, снова придется перебрать все массивы. И так для каждой операции, что очень неудобно.

Благодаря наследованию мы имеем две прекрасные возможности. Для того, чтобы ими воспользоваться, нам нужно создать класс Figure и описать в нем методы, общие для всех фигур: paint() , checkPoint(int x, int y) и так далее. Не обязательно программировать эти методы, мы все равно не будем обращаться к ним. * Важно, чтобы они были.

Первая возможность: мы можем присваивать объекты классов-потомков переменным любого из классов-предков.

Это вполне логично. Ведь если класс Кошка унаследован от класса Животное , то объект Мурзик является одновременно объектом класса Кошка и объектом класса Животное .

Следовательно, мы можем создать один большой массив * для хранения объектов класса Figure :

Figure[] figures = new Figure[100]; // создаем массив для хранения 100 фигур

Теперь мы можем помещать в этот массив любые фигуры:

figures[0] = new Point(30, 30); // добавили в массив точку с координатами 30, 30 figures[1] = new Circle(60, 20, 10); // добавили круг с координатами 60, 20 радиуса 10 figures[2] = new Rectangle(0, 0, 30, 40); // добавили прямоугольник .

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

Мы можем нарисовать все фигуры, хранящиеся в нашем массиве:


for ( int i = 0; i if (figures[i] != null ) figures[i].paint(); >

В массиве хранятся элементы типа Figure . В этом классе есть метод paint() , поэтому мы вполне можем к нему обратиться. Но в самом классе Figure этот метод не делает ничего (ведь мы не могли разработать процедуру рисования, подходящую для всех без исключения фигур). Зато в классе Point , унаследованном от класса Figure , мы переопределили этот метод — написали его заново так, чтобы он рисовал точку (координаты точки хранятся в скрытых атрибутах класса Point ). А в первом элементе массива figures[0] у нас хранится именно точка. Хотя мы обращаемся с ней как с просто фигурой, Java знает, что при вызове метода paint() нужно использовать именно тот вариант, который переопределен в классе Point . Аналогично команда figures[1].paint(); нарисует круг, а figures[2].paint(); нарисует прямоугольник.

Мы рассмотрели очень подробный пример, поскольку описанный прием является одним из наиболее часто используемых средств в арсенале объектно-ориентированного программирования.

Конструктор по умолчанию

Если в классе не описан ни один конструктор, для него автоматически создается конструктор по умолчанию. Этот конструктор не имеет параметров, все что он делает — это вызывает конструктор без параметров класса-предка.

Поэтому мы и смогли создать объект класса BigDog в примере с большой собакой, хотя не описывали в классе никаких конструкторов. Если вспомнить конструктор без параметров, который у нас есть в классе Dog , мы поймем, что переменная bigdog в предыдущем примере ссылалась на собаку по кличке «Незнакомец».

Вызов конструктора суперкласса

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

BigDog bigdog = new BigDog( «Полкан» , 8); // Ошибка. Такого конструктора в классе нет

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

BigDog (String n, int a)

Ключевое слово super означает суперкласс (в нашем случае это класс Dog ). В примере мы вызываем с его помощью конструктор суперкласса. При этом мы передаем два параметра — строку и число, — так что из всех конструкторов будет выбран именно тот, который нас интересует.

Вызов конструктора суперкласса должен происходить в самом начале конструктора.

Вместо вызова конструктора суперкласса можно вызвать один из конструкторов того же самого класса. Это делается с помощью ключевого слова this () — с параметрами в скобках, если они нужны.

Если в начале конструктора нет ни вызова this () , ни вызова super () , автоматически происходит обращение к конструктору суперкласса без аргументов.

Приведение типов

Объект класса-потомка можно присвоить переменной типа класса-предка. При этом Java производит автоматическое преобразование типа, называемое расширением. — это переход от более конкретного типа к менее конкретному. Переход от byte к int — это тоже расширение.

Рассмотрим пример, имеющий отношение к основному заданию. В программе есть класс User , предназначенный для обработки информации о пользователях системы. В этом классе есть метод enter(String login, String password) , который возвращает true , если переданные в метод логин и пароль совпадают с логином и паролем, скрытым в полях класса.

Мы наследуем от класса User два подкласса: Admin и, к примеру, Member (для программы координации участников встречи, см. задание 13). Класс Admin может понадобиться нам впоследствии для каких-то специфичных действий, связанных с управлением системой, а класс Member моделирует участников проекта, которые с помощью программы пытаются выбрать оптимальное место для встречи. Открытый метод addRequest(String place, int day, int from, int to) вызывается, когда участник проекта предлагает новый вариант времени и места встречи.

В главном классе программы мы храним массив * users , содержащий всех пользователей системы. Элементы этого массива имеют тип User , но мы можем присваивать им ссылки на объекты как класса Member , так и класса Admin . В этот момент и будет происходить расширение типа.

Member member = new Member(. ); users[3] = member; // Java проводит автоматическое преобразование типа Member к типу User, чтобы поместить переменную member в массив users

Для того, чтобы найти пользователя с введенными логином и паролем программа выполняет запрос:

for ( int i = 0; i if (users[i].enter(log, passw)) currentUser = users[i]; >

Несмотря на то, что все объекты, добавленные в массив, сохраняют свой «настоящий» класс, программа работает с ними как с объектами класса User . Этого вполне достаточно, чтобы можно было найти нужного пользователя по логину и паролю (ведь метод enter() у них общий) и присвоить найденный объект переменной currentUser типа User . В этой переменной хранится текущий пользователь, авторизовавшийся в системе.

Предположим, нам известно, что переменная currentUser сейчас ссылается на объект класса Member и текущий пользователь предлагает встретиться у фонтана в среду с 17 до 19 часов. Необходимо вызвать метод addRequest() , но у нас не получится сделать это командой

currentUser.addRequest( «Фонтан» , 3, 17, 19);

поскольку в классе User нет метода addRequest() .

Однако мы можем осуществить явное преобразование переменной currentUser к типу Member . Такое преобразование (переход от менее конкретного типа к более конкретному) называется . Явное преобразование делается с помощью оператора, представляющего собой имя целевого типа в скобках.

((Member)currentUser).addRequest( «Фонтан» , 3, 17, 19);

Здесь мы, прежде чем вызвать метод addRequest() , преобразовали переменную currentUser к типу Member . Нам было позволено сделать это, поскольку Member является потомком User . Однако, если бы во время выполнения программы оказалось, что на самом деле переменная currentUser не ссылалась на объект класса Member , в программе возникла бы ошибка.

Оператор instanceof

Чтобы уточнить, соответствует ли текущее значение переменной конкретному типу, используется оператор instanceof .

Проверим, не является ли текущий пользователь администратором (в этом случае программа должна перейти в режим управления):

if (currentUser instanceof Admin)

Анонимные и вложенные классы

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

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


Мы сразу же добавили в класс Dog два поля класса Eye и проинициализировали их вновь созданными объектами (это можно было сделать и в конструкторе). Теперь у собаки есть два глаза и она может открывать их и закрывать. Например, предположим, что все собаки лают с закрытым правым глазом. Тогда метод voice() надо переписать так:

Обратиться ко вложенному классу нужно с помощью составного имени (в нашем случае это Dog.Eye ). Если обращение происходит из содержащего его класса, имя можно сократить (как и было сделано выше).

Класс можно объявить внутри метода другого класса. В этом случае класс «виден» только внутри метода (за пределами метода нельзя объявить переменную типа этого класса).

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

Описание анонимного класса начинается с вызова конструктора его суперкласса, после чего в фигурных скобках описывается тело класса.

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

Пусть, например, в нашей программе собачьего питомника имеется массив dogs объектов типа Dog . И мы хотим добавить в этот массив совершенно уникальную собаку, которая не лает, а разговаривает. Необходимо описать класс, унаследованный от класса Dog , в котором будет соответствующим образом переопределен метод voice() . Но поскольку нам гарантированно понадобится только одна такая собака, мы можем описать анонимный класс прямо в месте добавления собаки в питомник (посадим ее в клетку № 10):

Цукерберг рекомендует:  Мне хочется попробовать все! #яжедевочка

И, несмотря на то, что мы не сможем создавать переменных этого класса, мы спокойно можем пользоваться новой собакой как объектом класса Dog . А благодаря полиморфизму вызов метода

приведет к тому, что уникальная собака будет уникально подавать голос.

Модификатор static

Любой член класса можно объявить статическим, указав перед его объявлением ключевое слово static . Статический член класса «разделяется» между всеми его объектами.

Для поля это означает, что любое изменение его значения, сделанное одним из объектов класса, сразу же «увидят» все остальные объекты.

Метод, объявленный с модификатором static , «дает обещание» не изменять никаких полей класса, кроме статических.

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

Упражнение

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

System.out.println( «Всего было создано собак: » + Dog.count);

Модификатор final

Любое поле класса можно объявить неизменяемым, указав перед его объявлением ключевое слово final . Неизменяемому полю можно присвоить значение только один раз (обычно это делается сразу при объявлении).

Константы в языке Java очевидным образом описываются путем совмещения модификаторов static и final . Например, мы можем объявить константу PI (лучше это делать в основном классе, а не в классе Dog * ), написав:

final static double PI = 3.14;

Если ключевое слово final указать перед объявлением метода, это будет обозначать, что метод нельзя переопределять при наследовании (т.е. данная версия метода будет окончательной).

Перед объявлением класса модификатор final ставится в том случае, если необходимо запретить от него наследование.

Диаграммы классов в языке UML

Внимание! На данном этапе обучения вы уже должны владеть знаниями по этой теме. Если их нет, а материалы для повторения непонятны или недостаточны, вы с заданиями не справитесь! Необходимо срочно обратиться к литературе по данной теме.

На этапе проектирования программного продукта, разрабатываемого в соответствии с объектно-ориентированной методологией, составляется диаграмма классов. Одним из стандартных средств для создания этих диаграмм является язык UML.

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

Основное задание практикума по Java содержит уже разработанные фрагменты UML-диаграмм (чтобы студенты могли сосредоточиться на программировании). Необходимо уметь в них разбираться.

Напомним основные обозначения, применяемые в диаграммах классов на примере UML-диаграммы системы LogisticSystem (см. задание 10).

Классы изображаются на диаграмме прямоугольниками, разделенными на три части. В верхней части указывается имя класса. В средней части перечисляются атрибуты (поля) класса, а в нижней — его методы. На рисунке изображены классы User , Admin , Driver , Map , Edge , LogisticSystem .

Закрытые (private) члены класса помечаются знаком минус или изображением замочка. На рисунке в классе LogisticSystem все поля и методы закрыты, кроме метода main() . У других классов закрытыми являются все поля (в соответствии с принципом инкапсуляции), а методы открыты.

Тип атрибутов (полей) класса указывается через двоеточие после его имени. Так же указываются типы параметров методов и возвращаемых значений методов. Например, в классе LogisticSystem показано описание метода findUser() :

findUser(login : String, password : String) : User

На языке Java заголовок этого метода на самом деле выглядит так:

public User findUser(String login, String password);

Однако UML не привязан к конкретному языку программирования, поэтому заголовки методов придется «переводить».

Наследование изображается в виде стрелки с полым белым наконечником, указывающей от класса-потомка к классу-предку. На рисунке классы Admin и Driver являются наследниками класса User . Унаследованные члены класса в классах-потомках не отображаются.


Между классами могут быть еще два вида отношений. Ассоциация — это связь между объектами двух классов, изображаемая прямой линией. Например, между классом LogisticSystem (система составления маршрутов) и классом Map (карта города) существует ассоциативная связь. В данном случае она означает, что система пользуется картой для выполнения своих функций. Агрегация — это отношение вида «часть/целое». Например, класс Edge (дорога между районами) является частью класса Map , то есть карта состоит из районов и связей между ними (как видно из рисунка, сами районы отдельным классом не представлены, они описываются с помощью обычных строк — см. атрибут points класса Edge ). Агрегация изображается в виде линии с ромбом на конце (ромб ставится около класса, являющегося частью другого).

Отношения ассоциации и агрегации помогают понять взаимосвязи между разными классами. Эти взаимосвязи могут быть уже реализованы на диаграмме через атрибуты (поля) классов. Например, в классе Map есть атрибут edges , представляющий собой список ссылок на объекты класса Edge , а в класс LogisticSystem уже включен атрибут map для ссылки на карту, используемую системой. Поэтому при выполнении заданий следует обращать внимание на ассоциации и агрегации лишь для лучшего понимания модели.

Для лучшего понимания модели могут оказаться полезными мощности отношений, которые проставляются на концах линий. Рассмотрим агрегацию между классами Map и Edge . Около класса Map стоит число 1, которое означает, что объект Edge может являться частью только одной карты (что неудивительно, поскольку в программе предусмотрена единственная карта). Около класса Edge стоит 0..n . Это означает, что с объектом класса Map может быть связано произвольное число объектов класса Edge , в том числе ни одного.

Заметим, что на диаграмме опущены две ассоциативных связи между классами LogisticSystem и User . Первая связывает систему и зарегистрированных в ней пользователей (реализуется через атрибут users ). Вторая связывает систему и текущего пользователя (реализуется через атрибут currentUser ). Эти связи опущены для увеличения наглядности остальной диаграммы.

Литература по теме:

1. Терри Кватрани. Rational Rose 2000 и UML. Визуальное моделирование.

Дополнительная литература

1. Вязовик Н.А. Программирование на Java. (главы 6—8)

2. Хабибуллин И.Ш. Самоучитель Java 2. (глава 2)

Задание

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

Java2UML скачать бесплатно

Java2UML — генератор UML диаграмм на основе кода Java. Может пригодиться для изучения и документирования кода.

Удобный инструмент для программистов и разработчиков кода, позволяет создать красивые диаграммы UML класса из .jar и проектов Java. Вы можете посмотреть структуру всего Java-приложения в одной схеме, добавить комментарии в код, изменить расположение элементов.

Программа бесплатная, портативная. интерфейс на русском и английском языках. Имеется подробная справка-help по работе с Java2UML.

Enterprise JavaBeans™ EJB 3.0

UML Profile Diagram Example

Here we provide an example of simplified and unofficial UML Profile for Enterprise JavaBeans™ (EJB) 3.0 with support for session, entity, and message-driven Enterprise JavaBeans.

Official Metamodel and UML Profile for Java and EJB Specification, Version 1.0 by OMG and JCP [UML Profile for Java and EJB. Version 1.0] is for Java 1.3, EJB 1.1 and most likely UML 1.4, so it could be only of some interest to bookworms.

UML 2.4 specification [UML 2.4 — Superstructure] provides example profile for unspecified version of J2EE/Enterprise Java Beans (EJB) in Annex D.1. That UML sample profile is neither normative (official) nor complete, and is provided only as an illustration.

The EJB 3.0 specification defines both stateful and stateless session beans. There are differences in the API between stateful session beans and stateless session beans. The client of a session bean may be a local client, a remote client, or a web service client, depending on the interface provided by the bean and used by the client.

Simplified example of unofficial Java EJB 3.0 Profile

Stateful session bean represents a conversational session with a particular client. Such session objects automatically maintain their conversational state across multiple client-invoked methods.

An entity object represents a fine-grained persistent object. The client of an entity bean may be a local client or the client may be a remote client.

The message-driven bean class must implement the appropriate message listener interface for the messaging type that the message-driven bean supports or specify the message listener interface using the MessageDriven metadata annotation or the messaging-type deployment descriptor element.

The ejb-jar file must contain the deployment descriptor in the format defined in EJB Specification. The deployment descriptor must be stored with the name META-INF/ejb-jar.xml

Noticed a spelling error? Select the text using the mouse and press Ctrl + Enter.

This document describes UML 2.5 and is based on OMG™ Unified Modeling Language™ (OMG UML®) 2.5 specification [UML 2.5 FTF — Beta 1].

Representation of Java Collections in UML2

Is there a standard representation for typed java collections in UML2? I am working on a class diagram and would like to avoid Java syntax, when using something like Map as type for a class attribute.

2 Answers 2

UML2 has parameterized classes, and the syntax is in fact pretty much the same as Java’s — both Java and the UML spec were inspired by the C++ syntax here.

I’m against using too much design documents to program. Documents are only for communicating ideas, IMHO.

So if you need Java Types for your UML chances are:

you are reverse engineering: then Java Syntax will be fine

you are trying to program trhough UML: don’t do it. Program in Java. Use UML only for object relation and collaboration

  • if the types are primitive: it’s likely you don’t need to express internal data structures in UML. You only need to express what’s the responsibility of the class.
  • if the types are objects: ok, maybe it’s the point. I’m not sure, but try to use the minimum.


Введение в UML. Обзор UML. Зачем нужно использовать платформу J2EE и язык XJML совместно. Проблемы моделирования J2EE в UML , страница 4

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

В дополнение к способности создавать универсальные модели, средства моделирования языка UML совершенствуются, и вскоре их можно будет применять для наложения на проект системы согласованных и общепринятых шаблонов взаимодействия объектов. Рассмотрим, например, вопрос, когда нужно использовать сеансовые компоненты с поддержкой состояний когда — не имеющие состояний, а также когда использовать компоненты JavaServer Pages (JSP), а когда— сервлеты. В будущем такие разновидности проектных решений можно будет кодировать в самом средстве моделирования и применять при необходимости.

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

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

В проблеме использования неспециализированного UML для моделирования J2EE есть нечто подобное. Можно выполнить нужную работу, но ее эффективность и вероятность успешного результата будет меньше, чем хотелось бы.

Более конкретно, спецификации, из которых состоит J2EE, ставят несколько различных проблем моделирования.

· Класс Enterprise JavaBeans (EJB) реализует деловые методы интерфейса Remote (удаленного), но не сам интерфейс. Это противоречит стандартной концепции реализации интерфейса, принятой в UML.

· Компонент EJB, по определению, связан с местным (Ноте) и удаленным (Remote) интерфейсами. Необходимо, чтобы разработчик модели на языке UML учитывал эту особенность архитектуры.

· Сервлеты и EJB имеют связанные с ними описатели развертывания.

· В отличие от большинства других классов Java, EJB, сервлеты и JSP вместе со своими описателями развертывания содержатся в файлах архива определенного типа.

· Атрибуты компонента-сущности отображаются на элементы в базе данных.

· Компоненты EJB имеют аспекты транзакций и безопасности.

· Сеансовые EJB потенциально могут иметь значимое динамическое поведение.

· Компоненты-сущности могут применять различные схемы персистентности.

ООП, UML и Java — как это соединить для программирования?

Чтобы приготовить любое блюдо, необходимо наличие посуды, ингредиентов и повара.

Аналогично и в программировании. Чтобы создать приложение (десктопное, мобильное, серверное и т. п.) необходимо наличие инструментов для программирования (программная среда разработки), язык программирования и его конструкций (ингредиенты), программист. Правильное сочетание этих «ингредиентов» даёт качественно программированный продукт.

Какие же составляющие нужны для программирования на Java?

Не секрет, что в нашем мире всё является объектом. На основе этого понятия возникло много технологий и концепций процесса моделирования и программирования. Главная из которых, это объектно-ориентированное программирование. Оно нашло своё проявление в объектно-ориентированном языке (ООП) программирования Java.

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

Для обобщения этих правил был создан Unified Modeling Language (UML). Это графическая запись абстрактной модели определённой системы. UML создан для визуального проектирования и документирования информационных и программных систем. Он не язык программирования, хотя с помощью него возможно предварительное генерирование кода на основе UML-моделей.

Объектная модель или UML – диаграмма не только описывает программу, но и является абстрактно-визуальной инструкцией для бизнес-процессов, системного обеспечения и оформления структуры данных.

Отметим, что процесс моделирования объектов и их поведений, состояний – это своеобразный и творческий процесс, который в первую очередь зависит от самой личности программиста. Ведь один и тот же объект, отдельно взятые программисты могут интерпретировать и описывать по-разному. Поэтому его можно назвать, ни много ни мало – искусством. В результате получаем что ООП по своей сути является плохо формализованным. Подробнее об этом и других особенностях программирования можно узнать на http://pmbk.ru.

Вспомним главные характеристики ООП:

А также тот факт, что объект имеет свои данные (состояния – атрибуты) и поведения (методы).

Для описания объектной модели используют разные типы UML- диаграмм:

— структурные (пакетов, классов, объектов, развёртывания и набора);

— поведения (действия, состояний, взаимодействия, коммуникации, последовательности, синхронизации и т. п.).

Характерная для языка Java, UML – диаграмма структуры пакетов. Пакет – логическая область хранения классов в пространстве имён. С этим понятием тесно связаны видимость и доступность класса и его элементов.

Диаграмма класса описывает статическую структуру системы классов.

Здесь класс описывается прямоугольником, разделённым на три области: название, атрибуты, методы. Скрытые от посторонних, отмеченные спереди, атрибуты и методы обозначаются символом «-». Это сокрытие данных в программировании называется инкапсуляцией. Стрелки указывают на наследование одного класса от другого.

Обобщённая структура взаимосвязей, демонстрирующая иерархию связей между классами.

Отношение обобщение – наследование. Бинарная ассоциация – одному объекту может относиться только один объект (человек – ID-карта). N-арная ассоциация – соотношение многих объектов к одному (человек — ID-карта, комната, ПК и т. д.).


Агрегация – объединение нескольких объектов в группу (много работников — отдел). Композиция – исключительный вариант агрегирования, процесс объединения объектов в новый составной объект (корпус, блок питания, ОЗУ, материнская плата, процессор, винчестер — компьютер).

Реализация – имеет материальное выражение в языке Java: объявление интерфейса, возможность реализации этого интерфейса каким-либо классом. Пример, внутренняя логика работы калькулятора. При смене работы калькулятора, кнопки остались те же (интерфейс), а внутренние процессы (реализация) обработки информации поменялись. Но и результаты суммирования, отнимания, умножения и деления остались те же.

UML-диаграммы классов

UML – унифицированный язык моделирования (Unified Modeling Language) – это система обозначений, которую можно применять для объектно-ориентированного анализа и проектирования.
Его можно использовать для визуализации, спецификации, конструирования и документирования программных систем.
Словарь UML включает три вида строительных блоков:

Цукерберг рекомендует:  Вакансии MerLion

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

Диаграмма – это графическое представление набора элементов, чаще всего изображенного в виде связного графа вершин (сущностей) и путей (связей). Язык UML включает 13 видов диаграмм, среди которых на первом месте в списке — диаграмма классов, о которой и пойдет речь.
Диаграммы классов показывают набор классов, интерфейсов, а также их связи. Диаграммы этого вида чаще всего используются для моделирования объектно-ориентированных систем. Они предназначены для статического представления системы.
Большинство элементов UML имеют уникальную и прямую графическую нотацию, которая дает визуальное представление наиболее важных аспектов элемента.

Сущности

Диаграммы классов оперируют тремя видами сущностей UML:

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

Поведенческие сущности – динамические части моделей UML. Это «глаголы» моделей, представляющие поведение модели во времени и пространстве. Основной из них является взаимодействие – поведение, которое заключается в обмене сообщениями между наборами объектов или ролей в определенном контексте для достижения некоторой цели. Сообщение изображается в виде линии со стрелкой, почти всегда сопровождаемой именем операции.

Аннотирующие сущности – это поясняющие части UML-моделей, иными словами, комментарии, которые можно применить для описания, выделения и пояснения любого элемента модели. Главная из аннотирующих сущностей – примечание . Это символ, служащий для описания ограничений и комментариев, относящихся к элементу либо набору элементов. Графически представлен прямоугольником с загнутым углом; внутри помещается текстовый или графический комментарий.

Структурные сущности — классы

Класс – это описание набора объектов с одинаковыми атрибутами, операциями, связями и семантикой.

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

  • имя класса
  • атрибуты (свойства) класса
  • операции (методы) класса.

Для атрибутов и операций может быть указан один из трех типов видимости:

  • — private (частный)
  • # — protected (защищенный)
  • + — public (общий)

Видимость для полей и методов указывается в виде левого символа в строке с именем соответствующего элемента.

Каждый класс должен обладать именем, отличающим его от других классов. Имя – это текстовая строка. Имя класса может состоять из любого числа букв, цифр и знаков препинания (за исключением двоеточия и точки) и может записываться в несколько строк.
На практике обычно используются краткие имена классов, взятые из словаря моделируемой системы. Каждое слово в имени класса традиционно пишут с заглавной буквы (верблюжья конвенция), например Sensor (Датчик) или TemperatureSensor (ДатчикТемпературы).

Для абстрактного класса имя класса записывается курсивом.

Атрибут (свойство) – это именованное свойство класса, описывающее диапазон значений, которые может принимать экземпляр атрибута. Класс может иметь любое число атрибутов или не иметь ни одного. В последнем случае блок атрибутов оставляют пустым.
Атрибут представляет некоторое свойство моделируемой сущности, которым обладают все объекты данного класса. Имя атрибута, как и имя класса, может представлять собой текст. На практике для именования атрибута используются одно или несколько коротких существительных, выражающих некое свойство класса, к которому относится атрибут.

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

Статические атрибуты класса обозначаются подчеркиванием.

Операция (метод) – это реализация метода класса. Класс может иметь любое число операций либо не иметь ни одной. Часто вызов операции объекта изменяет его атрибуты.
Графически операции представлены в нижнем блоке описания класса.
Допускается указание только имен операций. Имя операции, как и имя класса, должно представлять собой текст. На практике для именования операции используются короткие глагольные конструкции, описывающие некое поведение класса, которому принадлежит операция. Обычно каждое слово в имени операции пишется с заглавной буквы, за исключением первого, например move (переместить) или isEmpty (проверка на пустоту).
Можно специфицировать операцию, устанавливая ее сигнатуру, включающую имя, тип и значение по умолчанию всех параметров, а применительно к функциям – тип возвращаемого значения.

Абстрактные методы класса обозначаются курсивным шрифтом.
Статические методы класса обозначаются подчеркиванием.

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

Отношения между классами

Существует четыре типа связей в UML:

  • Зависимость
  • Ассоциация
  • Обобщение
  • Реализация

Эти связи представляют собой базовые строительные блоки для описания отношений в UML, используемые для разработки хорошо согласованных моделей.

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

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

Ассоциация – это структурная связь между элементами модели, которая описывает набор связей, существующих между объектами.
Ассоциация показывает, что объекты одной сущности (класса) связаны с объектами другой сущности таким образом, что можно перемещаться от объектов одного класса к другому.
Например, класс Человек и класс Школа имеют ассоциацию, так как человек может учиться в школе. Ассоциации можно присвоить имя «учится в». В представлении однонаправленной ассоциации добавляется стрелка, указывающая на направление ассоциации.

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


Множественность ассоциации представляет собой диапазон целых чисел, указывающий возможное количество связанных объектов. Он записывается в виде выражения с минимальным и максимальным значением; для их разделения используются две точки. Устанавливая множественность дальнего конца ассоциации, вы указываете, сколько объектов может существовать на дальнем конце ассоциации для каждого объекта класса, находящегося на ближнем ее конце. Количество объектов должно находиться в пределах заданного диапазона. Множественность может быть определена как единица 1 , ноль или один 0..1 , любое значение 0..* или * , один или несколько 1..* . Можно также задавать диапазон целых значений, например 2..5 , или устанавливать точное число, например 3 .

Агрегация – особая разновидность ассоциации, представляющая структурную связь целого с его частями. Как тип ассоциации, агрегация может быть именованной. Одно отношение агрегации не может включать более двух классов (контейнер и содержимое).
Агрегация встречается, когда один класс является коллекцией или контейнером других. Причём, по умолчанию агрегацией называют агрегацию по ссылке, то есть когда время существования содержащихся классов не зависит от времени существования содержащего их класса. Если контейнер будет уничтожен, то его содержимое — нет.
Графически агрегация представляется пустым ромбом на блоке класса «целое», и линией, идущей от этого ромба к классу «часть».

Композиция — более строгий вариант агрегации. Известна также как агрегация по значению.
Композиция – это форма агрегации с четко выраженными отношениями владения и совпадением времени жизни частей и целого. Композиция имеет жёсткую зависимость времени существования экземпляров класса контейнера и экземпляров содержащихся классов. Если контейнер будет уничтожен, то всё его содержимое будет также уничтожено.
Графически представляется как и агрегация, но с закрашенным ромбиком.

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

Четвертая – реализация – это семантическая связь между классами, когда один из них (поставщик) определяет соглашение, которого второй (клиент) обязан придерживаться. Это связи между интерфейсами и классами, которые реализуют эти интерфейсы. Это, своего рода, отношение «целое-часть». Поставщик, как правило, представлен абстрактным классом. В графическом исполнении связь реализации – это гибрид связей обобщения и зависимости: треугольник указывает на поставщика, а второй конец пунктирной линии – на клиента.

Пример кода и диаграммы классов для него

Программа получает данные с датчика температуры (вводятся с консоли) — по 5 измерений для каждого из двух объектов класса TemperatureMeasure и усредняет их. Также предусмотрен класс ShowMeasure для вывода измеренных значений.

#include
using namespace std;
class Sensor <
int value;
public :
Sensor() < value = 0; >
void setValue( int value) < this ->value += value; >
int getValue() < return value; >
>;
class MeasureCount
<
int number;
static int total;
public :
MeasureCount() < number = 0; >
void increment() < number++; total++; >
int getNumber() < return number; >
static int getTotal() < return total; >
>;
int MeasureCount::total = 0;
class ITemperatureMeasure
<
public :
virtual void setValue() = 0;
virtual double getValue() = 0;
>;
class TemperatureMeasure : public virtual ITemperatureMeasure
<
private :
Sensor *h; // агрегация
MeasureCount *measure; // композиция
public :
TemperatureMeasure(Sensor *h);
int getNumber() < return measure->getNumber(); >
double getValue() < return ( double )h->getValue() / measure->getNumber(); >
void setValue()
<
int value;
measure->increment();
cout «t[» measure->getNumber() «]= » ;
cin >> value;
h->setValue(value);
>
static int getTotal() < return MeasureCount::getTotal(); >
>;
TemperatureMeasure::TemperatureMeasure(Sensor *h)
<
measure = new MeasureCount();
this ->h = h;
>
class ShowTemperature // зависимость
<
public :
static void Show(TemperatureMeasure t)
<
cout t.getNumber() «: » ;
cout t.getValue() » oC» endl;
>
>;

int main()
<
Sensor *h1 = new Sensor();
TemperatureMeasure tc1(h1);
for ( int i=0; i endl;
Sensor *h2 = new Sensor();
TemperatureMeasure tc2(h2);
for ( int i = 0; i endl;
cout «Total: » TemperatureMeasure::getTotal() endl;
cin.get(); cin.get();
return 0;
>

UML-диаграмма классов для приведенного выше кода будет выглядеть следующим образом:

На диаграмме классов основным классом является класс TemperatureMeasure , который и является измерителем температуры. В качестве измеренного значения формируется среднее арифметическое всех измерений — сумма всех измерений, деленная на их количество.
Для получения измерений и их суммирования используется класс Sensor (в качестве датчика температуры). В консольной задаче сами измерения передаются в этот класс для суммирования. Класс состоит в отношении агрегации с основным классом TemperatureMeasure : мы сначала создаем объект класса Sensor , а потом передаем его в качестве параметра конструктора классу TemperatureMeasure , чтобы использовать его в качестве части класса.
Количество измерений формируется классом MeasureCount , который содержит статическое свойство total для подсчета общего измерений, а также свойство count для подсчета количества измерителей конкретного объекта TemperatureMeasure . Класс MeasureCount находится в отношении композиции с классом TemperatureMeasure : объект MeasureCount создается непосредственно при создании объекта TemperatureMeasure (в его конструкторе).
Класс ITemperatureMeasure представляет собой интерфейс класса TemperatureMeasure и является своего рода поставщиком в отношении реализации.
Наконец, класс ShowTemperature находится в отношении зависимости с классом TemperatureMeasure , поскольку реализация единственного метода Show класса ShowTemperature зависит от структуры класса TemperatureMeasure .

Общие механизмы языка UML

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

  • спецификации (Specifications);
  • дополнения (Adornments);
  • принятые деления (Common divisions);
  • механизмы расширения (Extensibility mechanisms).

UML — это не просто графический язык. За каждой частью его системы графической нотации стоит спецификация, содержащая текстовое представление синтаксиса и семантики соответствующего строительного блока. Например, пиктограмме класса соответствует спецификация, полностью описывающая его атрибуты, операции (включая полные сигнатуры) и поведение, хотя визуально пиктограмма порой отражает только малую часть этой совокупности. Более того, может существовать другое представление этого класса, отражающее совершенно иные его аспекты, но тем не менее соответствующее все той же спецификации. С помощью графической нотации UML вы визуализируете систему, с помощью спецификаций UML — описываете ее детали. Таким образом, допустимо строить модель инкрементно, то есть пошаговым образом — сначала нарисовать диаграмму, а потом добавить семантику в спецификацию модели, или наоборот — начать со спецификации (возможно, применив обратное проектирование к существующей системе), а потом на ее основе создавать диаграммы.

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

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

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

Рис. 2.16Дополнения

Каждый элемент нотации UML содержит базовый для него символ, к которому можно добавлять разнообразные специфичные для него дополнения (см. главу 6).

Принятые деления. При моделировании объектно-ориентированных систем реальность членится с учетом по крайней мере двух подходов.

Прежде всего, существует разделение на классы и объекты. Класс — это абстракция, объект — конкретная материализация этой абстракции (см. главу 13). В языке UML можно моделировать и классы, и объекты, как показано на рис. 2.17.

Рис. 2.17. Классы и объекты

На этом рисунке показан один класс Customer (Клиент) и три объекта: Jan (явно определенный как объект данного класса), :Customer (анонимный объект класса Customer) и Elyse (спецификация которого относит его к классу Customer, хотя это и не выражено явно).

Практически все строительные блоки UML характеризуются дихотомией «класс/объект». Так, имеются прецеденты и экземпляры прецедентов, компоненты и экземпляры компонентов, узлы и экземпляры узлов и т.д. В графическом представлении для объекта принято использовать тот же символ, что и для его класса, а название объекта подчеркивать.

Еще одним вариантом членения является деление на интерфейс и его реализацию. Интерфейс декларирует контракт (см. главу 11), а реализация представляет конкретное воплощение этого контракта и обязуется точно следовать объявленной семантике интерфейса. UML позволяет моделировать обе эти категории, интерфейсы и их реализации, как показано на рис. 2.18: в данном случае один компонент spellingwizard.dll реализует два интерфейса lUnknown и ISpelling. Почти все строительные блоки UML характеризуются дихотомией «интерфейс/реализация». Например, прецеденты реализуются кооперациями, а операции — методами.

Рис. 2.18Интерфейсы и реализации

Механизмы расширения. UML — это стандартный язык для разработки «чертежей» программного обеспечения, но ни один замкнутый язык не в состоянии охватить нюансы всех возможных моделей в различных предметных областях. Поэтому UML является открытым языком, то есть допускает контролируемые расширения. Механизмы расширения UML (см. главу 6) включают:

  • стереотипы;
  • помеченные значения;
  • ограничения.

Стереотип (Stereotype) расширяет словарь UML, позволяя на основе существующих блоков языка создавать новые, специфичные для решения конкретной проблемы. Например, работая с такими языками программирования, как Java или C++, часто приходится моделировать исключения (Exceptions) — они являются обыкновенными классами, хотя и рассматриваются особым образом. Обычно требуется, чтобы исключения можно было возбуждать и перехватывать, и ничего больше. Если пометить исключения соответствующим стереотипом, то с ними можно будет обращаться как с обычными строительными блоками языка; на рис. 2.19 это продемонстрировано на примере класса Overflow.

Рис. 2.19Механизмы расширения

Помеченное значение (Tagged value) расширяет свойства строительных блоков UML, позволяя включать новую информацию в спецификацию элемента. Скажем, если вы работаете над «коробочным» продуктом и выпускаете много его версий, то зачастую необходимо отслеживать версию и автора какой-нибудь важной абстракции. Ни версия, ни автор не являются первичными концепциями UML, но их можно добавить к любому блоку, такому, например, как класс, задавая для него новые помеченные значения. На рис. 2.19 показано, как это можно сделать, на примере класса EventQueue.

Ограничения (Constraints) расширяют семантику строительных блоков UML, позволяя определять новые или изменять существующие правила. Вы можете, например, ограничить класс EventQueue так, чтобы все события добавлялись в очередь по порядку. На рис. 2.19 показано, как можно определить ограничение, которое явно постулирует это правило для операции add.

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

Не нашли то, что искали? Воспользуйтесь поиском:

Понравилась статья? Поделиться с друзьями:
Все языки программирования для начинающих