изучить принцип инкапсуляции и его реализацию в языке Java;
ознакомиться с базовой нотацией диаграммы классов UML;
научиться представлять классы в виде нотации диаграммы классов UML;
научиться "читать" диаграмму и писать код исходя из диаграммы классов UML.
Инкапсуляция в Java реализована с помощью использования модификаторов доступа.
Язык Java предоставляет несколько уровней защиты, которые позволяет настраивать область видимости данных и методов. В Java имеется четыре категории видимости элементов класса:
private
– члены класса доступны только членам данного класса. Всё что объявлено private
, доступно только конструкторам и методам внутри класса и нигде больше. Они выполняют служебную или вспомогательную роль в пределах класса и их функциональность не предназначена для внешнего пользования. Закрытие (private
) полей обеспечивает инкапсуляцию;
по умолчанию (package-private) – члены класса доступны классам, которые находятся в этом же пакете;
protected
– члены класса доступны классам, находящимся в том же пакете, и подклассам – в других пакетах;
public
– члены класса доступны для всех классов в этом и других пакетах.
Модификатор класса указывается перед остальной частью описания типа отдельного члена класса. Это означает, что именно с него должен начинаться оператор объявления класса.
Когда член класса обозначается модификатором доступа public
, он становится доступным для любого другого кода в программе, включая и методы, определенные в других классах.
Когда член класса обозначается модификатором private
, он может быть доступен только другим членам этого класса. Следовательно, методы из других классов не имеют доступа к закрытому члену класса.
При отсутствии модификатора доступа, члены класса доступны другим членам класса, который находится в этом же пакете.
Модификатор доступа protected
связан с использованием механизма наследования и будет рассмотрен позже.
Модификатор доступа указывается перед остальной частью описания типа отдельного члена класса (то есть, именно с модификатора доступа начинается объявление члена класса).
Член класса (переменная, конструктор, методы), объявленный public
, доступен из любого метода вне класса.
Всё что объявлено private
, доступно только конструкторам и методам внутри класса и нигде больше. Они выполняют служебную или вспомогательную роль в пределах класса и их функциональность не предназначена для внешнего пользования. Закрытие (private
) полей обеспечивает инкапсуляцию.
В подавляющем большинстве случаев, поля класса объявляются как private
(это не касается статических переменных и констант, там ситуация может быть другая). Должны быть веские основания объявить поле класса общедоступным. Манипулирование данными должно осуществляться только с помощью методов.
Для того чтобы дать возможность получить доступ к переменной или дать возможность изменить ее значение, объявляют специальные методы, которые называются "геттерами" и "сеттерами".
Геттер возвращает значение приватного поля, тогда как сеттер меняет значение приватного поля (новое значение передается в качестве аргумента метода).
Хотя сигнатура и имена геттеров и сеттеров могут быть любыми, приучите себя соблюдать строгий шаблон для объявления геттеров и сеттеров.
Геттер должен иметь префикс get
, после которого идет название поля с большой буквы. Геттер, как правило, не имеет входных аргументов.
Сеттер должен иметь префикс set
, после которого идет название поля с большой буквы. Сеттер принимает на вход новое значение поля. Возвращаемый тип, как правило, void
.
Большинство IDE для Java имеют механизм для генерации геттеров и сеттеров. В IntelliJ IDEA нажмите комбинацию Alt+Insert
находясь в окне редактирования java-файла. Откроется контекстное меню Generate, где вы можете выбрать генерацию геттера и сеттера, после чего указать поля, для которых необходимо сгенерировать методы.
Представим, что нам необходимо создать класс «Корзина» (Cart
), который хранит в себе набор объектов класса «Товар» (Item
).
Какие методы «Корзина» должна предоставлять для внешнего использования? Это могут быть, например, методы «Добавить товар», «Убрать последний добавленный товар», «Подсчет суммы цен товаров в корзине», «Повышение цен в корзине на N процентов» и «Снижение цен в корзине на N процентов».
Как вы можете заметить, это публичные методы, а значит, их можно вызвать через оператор-точку имея ссылку ну объект.
Перечень этих публичных методов и составляет интерфейс класса – то есть, с помощью этих методов объект класса будет взаимодействовать с внешним миром.
Эти методы имеют вполне четко определенные входные аргументы и могут возвращать значения четко определенных типов, и никак иначе. По аналогии с этим, поворот колес автомобиля осуществляется четко определенным образом – поворотом руля, и бензин надо заливать в четко определенное отверстие крышки бензобака, а не как-то еще.
То – как будет реализовано хранение товаров в корзине – это внутренняя логика класса и она не должна быть доступна внешнему миру, она должна быть скрыта от внешнего вмешательства. Другие классы, которые будут использовать объекты класса Cart
не должны знать и не должны иметь доступ к тому – как там «внутри» реализовано хранение товаров, подсчет цен и изменение цены на определенный процент и так далее, они могут только лишь использовать предоставленные им публичные методы. Давайте реализуем «Корзину» с помощью структуры «стек», которая, в свою очередь, реализована обычным массивом.
Как мы видим, массив с товарами, указать на вершину стек объявлены как private
члены класса. Это значит, что мы не можем получить к ним доступ извне – они доступны только внутри данного класса.
Программиста, который будет использовать класс Cart
, не должна волновать ситуация с переполнением стека, с попыткой извлечь элемент из пустого стека, он не должен следить за указателем на вершину стека, он даже не должен знать что это стек.
Для него объект класса Cart
это некоторый объект, который предоставляет «услугу» в виде корзины товаров и с этой корзиной можно работать с помощью определенных публичных методов.
В дальнейшем мы можем переделать класс Cart
и поменять внутреннюю реализацию. Мы можем использовать структуру "очередь", мы можем использовать коллекции, мы можем иначе реализовать операции добавления и удаления элемента в стеке, но если мы сохраним интерфейс класса неизменным, то для внешнего мира эти изменения внутренней логики не будут важны и если мы поменяем внутреннюю логику одного небольшого участка программы, то вся остальная программа будет работать так же.
Добавьте модификаторы доступа к членам классов, разработанных в лабораторной работе 2 (классы TimeSpan
, BankAccount
и Student
).
Подумайте над тем, какие члены класса сделать приватными, а какие публичными
Для модифицированных классов из второй лабораторной работы нарисуйте диаграммы классов UML.
Необходимо создать класс IntStack
, который реализует функционал стека целых чисел с помощью статического массива.
Нарисуйте класс в диаграмме классов UML
При реализации стека учитывайте следующее:
при реализации класса вам необходимо указать область видимости для полей и методов. Подумайте о том, какие методы необходимо отнести к "интерфейсу", а какие к реализации;
размер стека для внешнего пользователя не имеет значения и теоретически неограничен;
внешний пользователь не должен знать и догадываться, что стек "внутри" реализован с помощью обычного статического массива;
вы должны написать "реализацию" класса таким образом, чтобы можно моделировать динамическое поведение стека с помощью обычного статического массива;
продумайте, какие аргументы методы должны принимать и какие значения возвращать.
Перейдите во вкладку "Подсказка", чтобы получить подсказку, которая поможет вам в реализации класса или попробуйте реализовать класс без подсказки.
При создании объекта стека инициализируйте внутренний статический массив с каким-то изначальным размером;
При вставке элементов проверяйте, не выходите ли вы за пределы внутреннего статического массива;
Если внутренний массив переполнен, создавайте новый массив большего размера: создавайте новый массив, копируйте значения из старого массива в новый, заменяйте новый массив на старый. Помните, что массивы в Java - объекты и что поле - просто указатель на массив и что указатель может указывать на другой массив;
При уменьшении размера стека реализуйте уменьшение внутреннего статического массива. Подумайте, как это лучше сделать, чтобы не уменьшать внутренний массив при каждом удалении элемента из стека.
ВАЖНО! Стек необходимо реализовать с помощью статического целочисленного массива!
НЕ ИСПОЛЬЗУЙТЕ КОЛЛЕКЦИИ И ПОТОКИ ДАННЫХ, ТОЛЬКО МАССИВЫ
Для внешнего пользователя класса, поведение класса выглядит следующим образом:
пользователь может создать новый стек с конструктором без параметров;
пользователю доступна команда pop()
для извлечения элемента из стека. Метод pop()
возвращает извлеченный элемент. При попытке извлечь из пустого стека, метод возвращает 0
или выбрасывает исключение (реализуйте выброс исключения, если вы знаете, как это делать и что такое исключение);
пользователю доступна команда push()
, которая добавляет элемент в стек;
пользователю доступна команда peek()
для просмотра элемента на вершине стека без его исключения.
пользователь может получить значение количества элементов в стеке с помощью метода size()
;
пользователь может проверить, пустой ли стек с помощью метода isEmpty()
;
пользователь может очистить стек полностью с помощью метода clear()
.
Создайте Java-классы исходя из диаграммы, представленной ниже. Код методов реализовывать не нужно, только заголовки и общую структуру классов.
UML – унифицированный язык моделирования (Unified Modeling Language) – это система обозначений, которую можно применять для объектно-ориентированного анализа и проектирования. Его можно использовать для визуализации, спецификации, конструирования и документирования программных систем.
Словарь UML включает три вида строительных блоков:
Диаграммы.
Сущности.
Связи.
Сущности – это абстракции, которые являются основными элементами модели, связи соединяют их между собой, а диаграммы группируют представляющие интерес наборы сущностей.
Диаграмма – это графическое представление набора элементов, чаще всего изображенного в виде связного графа вершин (сущностей) и путей (связей). Язык UML включает 13 видов диаграмм, среди которых на первом месте в списке — диаграмма классов.
Диаграмма классов описывает типы объектов системы и различного рода статические отношения, которые существуют между ними. На диаграммах классов отображаются также свойства классов, операции классов и ограничения, которые накладываются на связи между объектами.
Если кто-нибудь подойдет к вам в темном переулке и спросит: «Хотите посмотреть на диаграмму UML?», знайте – скорее всего, речь идет о диаграмме класса. Большинство диаграмм UML, которые я встречал, были диаграммами классов. - Мартин Фаулер
Графически класс изображается в виде прямоугольника, разделенного на три блока горизонтальными линиями:
имя класса;
атрибуты (свойства) класса;
операции (методы) класса.
Для атрибутов и операций может быть указан один из трех типов видимости:
-
private;
~
без модификатора (default);
#
protected;
+
public.
Видимость для полей и методов указывается в виде левого символа в строке с именем соответствующего элемента.
Каждый класс должен обладать именем, отличающим его от других классов. Имя – это текстовая строка. Имя класса может состоять из любого числа букв, цифр и знаков препинания (за исключением двоеточия и точки) и может записываться в несколько строк.
Атрибут (свойство) – это именованное свойство класса, описывающее диапазон значений, которые может принимать экземпляр атрибута. Класс может иметь любое число атрибутов или не иметь ни одного. В последнем случае блок атрибутов оставляют пустым. Можно уточнить спецификацию атрибута, указав его тип, кратность (если атрибут представляет собой массив некоторых значений) и начальное значение по умолчанию.
Статические атрибуты класса обозначаются подчеркиванием.
Операция (метод) – это реализация метода класса. Класс может иметь любое число операций либо не иметь ни одной. Часто вызов операции объекта изменяет его атрибуты. Графически операции представлены в нижнем блоке описания класса. Допускается указание только имен операций. Имя операции, как и имя класса, должно представлять собой текст. Можно специфицировать операцию, устанавливая ее сигнатуру, включающую имя, тип и значение по умолчанию всех параметров, а применительно к функциям – тип возвращаемого значения.
Абстрактные методы класса обозначаются курсивным шрифтом. Статические методы класса обозначаются подчеркиванием.
Изображая класс, не обязательно показывать сразу все его атрибуты и операции. Для конкретного представления, как правило, существенна только часть атрибутов и операций класса. В силу этих причин допускается упрощенное представление класса, то есть для графического представления выбираются только некоторые из его атрибутов. Если помимо указанных существуют другие атрибуты и операции, вы даете это понять, завершая каждый список многоточием. Чтобы легче воспринимать длинные списки атрибутов и операций, желательно снабдить префиксом (именем стереотипа) каждую категорию в них. В данном случае стереотип – это слово, заключенное в угловые кавычки, которое указывает то, что за ним следует.
Рассмотрим пример класса - его графическое представление и код на языке Java.
Ссылка на приложения для построения диаграмм UML -
Название метода
Описание
public Cart(int capacity)
Конструктор с 1 параметром – максимальным количеством товаров в корзине.
public boolean addItem(Item item)
Добавление товара в корзину. Возвращает успешность операции.
public Item deleteLastAddedItem()
Удаление последнего добавленного товара в корзину. Возвращает удаленный товар.
public double calculateItemPrices()
Подсчет суммы цен всех товаров в корзине.
public void raiseItemPrices(double percent)
Поднять цены товаров в корзине на определенный процент (значение процента передается как аргумент метода).
public void cutItemPrices(double percent)
Снизить цены товаров в корзине на определенный процент (значение процента передается как аргумент метода).