Файл: Применение объектно-ориентированного подхода при проектировании информационной системы (УНИФИЦИРОВАННЫЙ ЯЗЫК МОДЕЛИРОВАНИЯ).pdf

ВУЗ: Не указан

Категория: Курсовая работа

Дисциплина: Не указана

Добавлен: 30.06.2023

Просмотров: 38

Скачиваний: 3

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

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

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

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

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

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

Двойные ассоциации представляются линией без стрелок на концах, соединяющей два классовых блока.
Ассоциация может быть именованной, и тогда на концах представляющей её линии будут подписаны роли, принадлежности, индикаторы, мультипликаторы, видимости или другие свойства.
Множественность ассоциации представляет собой диапазон целых чисел, указывающий возможное количество связанных объектов. Он записывается в виде выражения с минимальным и максимальным значением; для их разделения используются две точки. Устанавливая множественность дальнего конца ассоциации, вы указываете, сколько объектов может существовать на дальнем конце ассоциации для каждого объекта класса, находящегося на ближнем ее конце. Количество объектов должно находиться в пределах заданного диапазона. Множественность может быть определена как единица 1, ноль или один0...1, любое значение 0...* или *, один или несколько 1…*. Можно также задавать диапазон целых значений, например 2…5, или устанавливать точное число, например 3.
Агрегация – особая разновидность ассоциации, представляющая структурную связь целого с его частями. Как тип ассоциации, агрегация может быть именованной. Одно отношение агрегации не может включать более двух классов (контейнер и содержимое).
Агрегация встречается, когда один класс является коллекцией или контейнером других. Причём, по умолчанию агрегацией называют агрегацию по ссылке, то есть, когда время существования содержащихся классов не зависит от времени существования содержащего их класса. Если контейнер будет уничтожен, то его содержимое — нет.
Графически агрегация представляется пустым ромбом на блоке класса «целое», и линией, идущей от этого ромба к классу «часть».
Композиция — более строгий вариант агрегации. Известна также как агрегация по значению.
Композиция – это форма агрегации с четко выраженными отношениями владения и совпадением времени жизни частей и целого. Композиция имеет жёсткую зависимость времени существования экземпляров класса контейнера и экземпляров содержащихся классов. Если контейнер будет уничтожен, то всё его содержимое будет также уничтожено.
Графически представляется как и агрегация, но с закрашенным ромбиком.
Третья связь – обобщение – выражает специализацию или наследование, в котором специализированный элемент (потомок) строится по спецификациям обобщенного элемента (родителя). Потомок разделяет структуру и поведение родителя. Графически обобщение представлено в виде сплошной линии с пустой стрелкой, указывающей на родителя.

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


Рисунок 2.1 – UML диаграмма классов

Рисунок 2.2 – Код программы на С++

Рисунок 2.3 – Продолжение кода программы на С++

Рисунок 2.4 – Результат выполнения программы

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

ОШИБКИ ПРИ ИСПОЛЬЗОВАНИИ ПОДХОДА

Благодаря трудам Штанюка Антона Александровича, доцента Нижегородского государственного университета им. Н.И. Лобачевского были собраны и классифицированы ошибки при использовании ОО подхода. Несколько простейших языковых ошибок.

Рассмотрим несколько типичных ошибок, определяемых на ранних стадиях.

Неправильное использование конструкций, вызванное особенностями реализации в разных языках. Типичный пример: когда конструкции try…catch() в языке С++ ошибочно приписывают блок finally, используемый в Java. Другой пример: для создания абстрактного класса языке С++ необходимо объявить в таком классе абстракный (чисто-виртуальный) метод, в то время как в языке Java можно воспользоваться спецификатором abstract для этой цели. - Ограниченное использование спецификатора const в С++ Использование const для создания ссылок на неизменяемые объекты и определения константых методов в классе предотвращает ошибочные изменения данных в объектах, что способствует повышению надежности всей программы. Начинающие программисты часто не уделяют этому факту должного внимания и не используют данный спецификатор. В языке Java подобным образом может игнорироваться спецификатор final.


Ошибки именования. Ошибки, связанные с неправильными именами классами, методами и объектами. Неправильные имена классов. В языке Java имя файла должно совпадать с именем публичного класса, определенного в этом файле. Несоответствие имен приводит к ошибкам компиляции. Кроме того, в программах с большим количеством классов, неправильные имена могут приводить к путанице при наследовании. - Неправильные имена методов. Помимо путаницы в использовании, неправильные имена в иерархиях (как и ошибки в сигнатурах методов) часто приводят к ошибкам замещения (override) или перегрузок (overload) методов. Например, если разработчик базового класса при выборе метода допустил синтаксическую ошибку, то разработчик производного класса может этого не заметить и выбрать корректное имя, что и приведет к ошибке. Частое использование методов, которые не возвращают результаты своей работы, может свидетельствовать о дефектах в проектировании классов (сильной зависимости от состояния объекта) и может потребовать дополнительного комментирования в тексте или документирования. - Слишком длинные или короткие имена Начинающие разработчики часто предпочитают давать объектам программы короткие имена, которые в дальнейшем быстро “заканчиваются”, при этом создавая известную путаницу в силу своей схематичности. Использование слишком длинных имен, особенно при активной разработке «оберток» (wrappers) к существующим объектам, может приводить к появлению сверхдлинных цепочек имен, затрудняющих работу с кодом. Пример длинного выражения: container.add((new JLabel("label text")).setMaximumSize(new Dimension(100,200))); В языке Java принято собирать классы в пакеты, которые, в свою очередь вкладываются друг в друга. В результате глубоких вложений или при использовании очень длинных имен могут образовываться очень длинные цепочки.

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

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


Рисунок 3.1 – усложнение модели предметной области

Недостаточная декомпозиция, малое количество сущностей. Здесь наблюдается обратная картина: вместо хорошей декомпозиции, разработчик работает с малым количеством сущностей. Классы становятся громоздкими, нарушается принцип повторного использования, поскольку один и тот же функционал начинает дублироваться в разных классах. Возникает такой антипаттерн ООП, как «божественный объект», при котором одному объекту программы соответствует слишком большой функционал. Нарушение принципов ООП - Использование большого количества открытых членов класса 81 Сокрытие данных и кода придумали для их защиты от прямого доступа (в целях безопасности) и для облегчения тестирования и отладки. С другой стороны, этот принцип может стать помехой при написании кода, и начинающие программисты охотно заменяют закрытые и защищенные элементы классов на открытые. Вместо закрытых классов в С++ применяют открытые по умолчанию структуры. - Злоупотребление «дружбой» классов в С++ Ключевое слово friend, принятое в С++ используется для предоставления исключительного доступа одного класса к другому. Если этой возможностью начать злоупотреблять, то принцип сокрытия сойдет на нет и в программе начнется бесконтрольный прямой доступ между классами. - Нарушение принципа единственной ответственности (SRP) Если проектировать слишком большие классы с богатым набором функционала, то, скорее всего, будет нарушаться SRP. Основная проблема возникнет при дальнейшей работе над классом, при внесении в него изменений. Кроме того, такой класс будет иметь множество связей и при изменениях требований потребуется вносить много изменений в связанные участки программы. Неправильное использование наследования. Наиболее сложной стороной ООП для новичков выступают вопросы, связанные с использованием наследования и полиморфизма. Одна из наиболее часто встречающихся ошибок: Подмена агрегации (композиции) наследованием на рисунке 3.2.

Рисунок 3.2 – подмена агрегации наследованием

Если в классе cтудент имеется расчет среднего балла и такая же цель определяется для группы, то начинающие связывают эти классы наследованием, полагая, что в таком случае внутри группы удобно вызвать метод Student::calcGPA(). - Игнорирование виртуальных деструкторов. При отсутствии виртуальных деструкторов проблемы будут возникать при освобождении ресурсов, занимаемыми различными объектами в рамках одной иерархии. - Наследование вместо делегирования. Довольно популярная ошибка, связанная с неправильным подходом к наследованию и имеющая специальное название «антипаттерн BaseBean».