Среда NetBeans при установленном пакете NetBeans Enterprise Pack позволяет по имеющемуся исходному коду построить UML-диаграммы. Для этого следует открыть проект и нажать на главной панели среды разработки кнопку “Reverse Engineer…”
Кнопка “Reverse Engineering”
Появится диалоговая форма задания параметров создаваемого проекта, в которой следует изменить название проекта на осмысленное, по которому легко можно будет определить, к какому проекту Java он относится.
Диалоговая форма задания параметров создаваемого UML-проекта
В нашем случае UMLProject7 мы заменим на UML_Figure. После нажатия на кнопку Finish (“Закончить”) будет выдана форма с ненужной вспомогательной информацией, и в ней следует нажать кнопку Done (“Сделано”). В результате чего мы получим новый UML-проект, в котором можно просмотреть параметры, относящиеся к каждому классу:
Параметры UML-проекта, относящиеся к классу Circle
Для класса показываются конструкторы и обычные методы (узел Operations), а также отношения наследования и другие варианты отношений (узел Relationships).
В UML-проекте можно сгенерировать UML-диаграммы, щёлкнув правой кнопкой мыши по имени соответствующего класса:
Всплывающее меню действий с классом в UML-проекте
Если выбрать пункт “Create Diagram From Selected Elements” (“Создать диаграмму из выбранных элементов”), и далее выбрать тип диаграммы “Class Diagram”,
Выбор типа создаваемой диаграмы
можно получить диаграмму такого вида:
Диаграмма для класса Circle
При этом лучше заменить имя создаваемой диаграммы, например, на Circle Diagram. Переименование можно сделать и позже, щёлкнув правой кнопкой мыши по имени диаграммы и выбрав в появившемся всплывающем меню пункт Rename… (“Переименовать…”).
Если же выделить Circle,Dot,Figure, ScalableFigure, мы получим диаграмму наследования, которой можно дать имя Inheritance Diagram.
Диаграмма для классов Circle,Dot,Figure, ScalableFigure
Если для класса Circle во всплывающем меню выбрать пункт “Generate Dependency Diagram” (“Сгенерировать диаграмму зависимостей”), получим следующую диаграмму:
Диаграмма зависимостей для класса Circle
Пункт всплывающего меню Navigate to Source позволяет вместо диаграмм показывать редактор исходного кода.
На диаграммах можно добавлять в классы или удалять из них поля и методы, проводить переименования, менять модификаторы. Причём изменения, сделанные на любой из диаграмм, автоматически отражаются как на других диаграммах UML-проекта, так и в исходном коде проекта Java (это проектирование – Forward Enineering). И наоборот - изменения, сделанные в исходном коде Java, автоматически применяются к диаграммам UML (это обратное проектирование – Reverse Enineering).
В настоящее время работа с UML-проектами в NetBeans Enterprise Pack не до конца отлажена, иногда наблюдаются “баги” (мелкие ошибки). Но можно надеяться, что в ближайшее время недостатки будут исправлены.
Краткие итоги по главе 6
ü Наследование опирается на инкапсуляцию. Оно позволяет строить на основе первоначального класса новые, добавляя в классы новые поля данных и методы. Первоначальный класс называется прародителем (ancestor), новые классы – его потомками (descendants). От потомков можно наследовать, получая очередных потомков. Набор классов, связанных отношением наследования, называется иерархией классов. Класс, стоящий во главе иерархии, от которого унаследованы все остальные (прямо или опосредованно), называется базовым классом иерархии.
ü Иерархия нужна для того, чтобы писать полиморфный код. Основные преимущества объектного программирования обеспечиваются наличием полиморфного кода.
ü Поля отражают состояние объекта, а методы - задают его поведение.
ü Чем ближе к основанию иерархии лежит класс, тем более общим и универсальным (general) он является. Чем дальше от базового класса иерархии стоит класс, тем более специализированным (specialized) он является.
ü Каждый объект класса-потомка при любых значениях его полей данных должен рассматриваться как экземпляр класса-прародителя на уровне абстракций поведения, но с некоторыми изменениями в реализации этого поведения.
ü Проектирование классов осуществляется с помощью UML-диаграмм.
ü В Java подпрограммы задаются только как методы в каком-либо классе и называются функциями. Объявление в классе функции состоит из задания заголовка и тела функции (её реализации).
ü Параметры, указанные в заголовке функции при её декларации, называются формальными. А те параметры, которые подставляются во время вызова функции, называются фактическими. Формальные параметры нужны для того, чтобы указать последовательность действий с фактическими параметрами после того, как те будут переданы в подпрограмму во время вызова. Это ни что иное, как особый вид локальных переменных, которые используются для обмена данными с внешним миром.
ü Параметры в Java передаются в функцию всегда по значению. Передача по ссылке отсутствует. В частности, ссылки передаются в функции по значению.
ü В Java имеются уровни видимости private (“частный”, “закрытый”), пакетный, protected (“защищённый”) и public (Общедоступный). Пакетный – уровень видимости по умолчанию для полей и методов, для задания остальных уровней используются модификаторы private, protected и public.
ü Ссылка this обеспечивает ссылку на объект из метода объекта. Чаще всего она используется при перекрытии области видимости имени поля объекта формальным параметром функции.
ü В классе-наследнике методы можно переопределять. При этом у них должен сохраняться контракт – в который входит весь заголовок метода за исключением имён формальных параметров.
ü Можно задавать перегруженные (overloaded) варианты методов, отличающиеся сигнатурой. В сигнатуру входит только часть заголовка метода – имя функции, а также число, порядок и тип её параметров.
ü Переменной некоторого объектного типа можно присваивать выражение, имеющее тот же тип или тип класса-наследника. В качестве фактического параметра функции вместо формального параметра некоторого объектного типа можно подставлять выражение, имеющее тот же тип или тип класса-наследника. Именно это правило обеспечивает возможность использования полиморфного кода.
ü Для приведения типа используется имя типа, заключённое в круглые скобки – как и для преобразования типа. Но при преобразовании типа могут меняться содержимое и размер ячейки, к которой применяется данный оператор, а при приведении типа ячейка и её содержимое остаются теми же, просто начинают считать, что у ячейки другой тип.
ü Проверка на то, что объект является экземпляром заданного класса, осуществляется оператором instanceof: if(figure instanceof Circle)...
ü Возможна программная проверка точного соответствия объекта нужному типу с помощью ссылки на класс: if(figure.getClass()==Circle.class)... При этом для экземпляра класса-наследника Circle сравнение даст false, в отличие от экземпляра Circle.
ü Оператор isInstance позволяет проверять, является ли тип объекта совместимым с классом, на который задана ссылка c: if(c.isInstance(figure))... При если класс, экземпляром которого является объект figure, является наследником класса c или совпадает с ним, сравнение даст true.
ü Рефакторинг – изменение структуры существующего проекта без изменения его функциональности. Три наиболее часто встречающихся примера рефакторинга: 1) Переименование элементов программы – классов, переменных, методов. 2) Перемещение элементов программы с одного места на другое. 3) Инкапсуляция полей данных.
ü С помощью средств Reverse Engineering можно создавать UML-диаграммы классов и зависимостей классов. Причём после создания UML-проекта, сопровождающего Java-проект, изменения, сделанные в исходном коде Java, автоматически применяются к диаграммам UML, и наоборот.
Типичные ошибки:
- Очень часто ошибочно считают более общим объект, из которого можно получить другой при каких-либо конкретных значениях полей данных. Например, окружность считают частным значением эллипса, а точку – частным значением окружности. Но в объектном программировании отношения общности и специализации объектов определяются по сложности их устройства (наличию дополнительных полей данных и методов), а также их поведению при произвольных значениях их полей данных. Чем сложнее объект, тем он более специализирован (менее общий).
- Очень часто возникают ошибки при попытке вернуть из функции изменённые значения ссылочных переменных, переданных через список параметров. Для такого возврата надо либо использовать глобальную переменную, либо передавать ссылку на объект, полем которого является изменяемая переменная.
- Используют неправильное приведение типа. Например, если переменной Object object присвоена ссылка на объект типа Dot, а пытаются сделать приведение (Circle) object. Такая ошибка на этапе компиляции не может быть распознана, и возникает исключительная ситуация неправильного приведения типа (invalid typecast) во время выполнения программы.
- Оператор instanceof используют для распознавания типов одной иерархии, считая, что он
Задания
- В классе MathUtil написать подпрограмму вычисления факториала
public static double factorial(int n)
Модификатор static помечает подпрограмму как метод класса. То есть позволяет вызывать метод через имя класса без создания объекта.
Напомним, что факториал натурального числа n – это произведение всех натуральных чисел от 1 до n:
n!=1·2·…·(n-1)·n
Кроме того, 0! считается равным 1. Обозначение факториала в виде n! математическое, в Java символ “!” зарезервирован для других целей. Также написать подпрограммы вычисления факториала с другими типами возвращаемых значений:
public static long factorial_long(int n) и
public static int factorial_int(int n)
Сравнить работу подпрограмм при n=0,1,5,10,20,50,100. Объяснить результаты.
- Разработать в пакете приложения библиотеку классов для иерархии фигур. Реализация должна быть с заглушками при реализации методов show и hide. Вместо показа на экране эти методы должны выводить в консольное окно вывода имя класса фигуры и слово show или hide, а также координаты x и y фигуры.
- Разработать приложение, в котором используются эти классы – создаётся фигура нужного типа при нажатию на кнопку и “показывается”. Создать документационные комментарии для полей и методов разработанных классов. Вызвать генерацию документации для проекта, просмотреть в ней созданные комментарии.
- Проверить в исходном коде проекта справку, возникающую при вызовах figure. и dot., где эти переменные заданы как Figure figure и Dot dot.
- Создать пакет figures. С помощью средств рефакторинга переместить классы фигур в пакет figures.
- С помощью средств Reverse Engineering создать UML-диаграммы классов и зависимостей классов для разработанного пакета figures.