Типы отношений между классами
Сдам Сам

ПОЛЕЗНОЕ


КАТЕГОРИИ







Типы отношений между классами





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

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

* агрегация ( Aggregation );

* ассоциация ( Association );

* наследование ( Inheritance );

* метаклассы ( Metaclass ).

Агрегация

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

 

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

В нашем примере отношение contain является двунаправленным. Объект класса Aquarium содержит несколько объектов Fish. В то же время каждая рыбка "знает", в каком именно аквариуме она живет. Каждый класс имеет свою роль в агрегации, которая указывает, какое место занимает класс в данном отношении. Имя роли не является обязательным элементом обозначений и может отсутствовать на диаграмме. В примере можно видеть роль home класса Aquarium (аквариум является домом для рыбок), а также роль inhabitants класса Fish (рыбки являются обитателями аквариума). Название роли обычно совпадает с названием соответствующего поля в классе. Изображение такого поля на диаграмме излишне, если уже указано имя роли. Т.е. в данном случае класс Aquarium будет иметь свойство (поле) inhabitants, а класс Fish - свойство home.



Число объектов, участвующих в отношении, записывается рядом с именем роли. Запись " 0..n " означает "от нуля до бесконечности". Приняты также обозначения:

" 1..n " - от единицы до бесконечности;

" 0 " - ноль;

" 1 " - один;

" n " - фиксированное количество;

" 0..1 " - ноль или один.

Код, описывающий рассмотренную модель и явление агрегации, может выглядеть, например, следующим образом:

// определение класса Fish

public class Fish {

// определения поля home

// (ссылка на объект Aquarium)

private Aquarium home;

public Fish() {

}

}

// определение класса Aquarium

public class Aquarium {

// определения поля inhabitants

// (массив ссылок на объекты Fish)

private Fish inhabitants[];

public Aquarium() {

}

}

Ассоциация

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

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

 

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

Код, соответствующий рассмотренному примеру, будет, например, следующим:

public class Programmer {

private Computer computers[];

public Programmer() {

}

}

public class Computer {

private Programmer programmers[];

public Computer() {

}

}

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

Наследование является важным случаем отношений между двумя или более классами. Подробно оно рассматривалось выше.

Метаклассы

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

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

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

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

Итак, объекты порождаются от классов, а классы - от метакласса. Он, как правило, в системе только один. Но существуют языки программирования, в которых можно создавать и использовать собственные метаклассы, например язык Python. В частности, функциональность метакласса может быть следующая: при формировании класса он будет просматривать список всех методов в классе и, если имя метода имеет вид set_XXX или get_XXX, автоматически создавать поле с именем XXX, если такого не существует.

Поскольку метакласс сам является классом, то нет никакого смысла в создании "мета-мета-классов".

В языке Java также есть метакласс. Это класс, который так и называется - Class (описывает классы ), он располагается в основной библиотеке java.lang. Виртуальная машина использует его по прямому назначению. Когда загружается очередной .class -файл, содержащий описание нового класса, JVM порождает объект класса Class, который будет хранить его структуру. Таким образом, Java использует концепцию метакласса в самых практических целях. С помощью Class реализована поддержка статических ( static ) полей и методов. Наконец, этот класс содержит ряд методов, полезных для разработчиков. Они будут рассмотрены в следующих лекциях.

Достоинства ООП

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

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

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

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

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

Полиморфизм оказывается полезным преимущественно в следующих ситуациях.

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

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

* Реализация работы с наследниками. Алгоритмы можно обобщить настолько, что они уже смогут работать более чем с одним видом объектов.

* Создание "каркаса" (framework). Независимые от приложения части предметной области могут быть реализованы в виде набора универсальных классов, или каркаса (framework), и в дальнейшем расширены за счет добавления частей, специфичных для конкретного приложения.

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

* Сокращается время на разработку, которое может быть отдано другим задачам.

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

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

* Если программа опирается на стандартные компоненты, ее структура и пользовательский интерфейс становятся более унифицированными, что облегчает ее понимание и упрощает использование.

Недостатки ООП

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

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

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

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

Многие считают, что ООП является неэффективным. Как же обстоит дело в действительности? Мы должны проводить четкую грань между неэффективностью на этапе выполнения, неэффективностью в смысле распределения памяти и неэффективностью, связанной с излишней универсализацией.

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

В гибридных языках типа Oberon-2, Object Pascal и C++ отправка сообщения приводит лишь к вызову через указатель процедурной переменной. На некоторых машинах сообщения выполняются лишь на 10% медленнее, чем обычные процедурные вызовы. И поскольку сообщения встречаются в программе гораздо реже других операций, их воздействие на время выполнения влияния практически не оказывает.

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

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

3. Излишняя универсальность. Неэффективность также может означать, что в программе реализованы избыточные возможности. В библиотечном классе часто содержится больше методов, чем это реально необходимо. А поскольку лишние методы не могут быть удалены, они становятся мертвым грузом. Это не влияет на время выполнения, но сказывается на размере кода.

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

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

Заключение

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

 

Введение. 5

1Объектно-ориентированный подход к разработке ПО.. 7

1.1Понятия объекта и класса. 7

1.1.1Природа объектов. 7

1.1.2Отношения между объектами. 9

1.1.3Природа классов. 12

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

1.2Объектная модель. 18

1.2.1Абстрагирование. 18

1.2.2Инкапсуляция. 20

1.2.3Модульность. 21

1.2.4Иерархия. 23

1.2.5Типизация. 25

1.2.6Параллелизм.. 27

1.2.7Сохраняемость. 29

1.2.8Преимущества объектной модели. 31

1.3Теории классификации. 32

1.3.1Классическая категоризация. 36

1.3.2Концептуальная кластеризация. 37

1.3.3Теория прототипов. 37

2Паттерны проектирования классов и объектов. 39

2.1Механизмы повторного использования. 39

2.2Порождающие паттерны.. 43

2.2.1Паттерн Singleton. 43

2.2.2Паттерн Prototype. 45

2.2.3Паттерн Factory method. 46

2.2.4Паттерн Reflection. 49

2.2.5Паттерн Creator 52

2.3Структурные паттерны.. 52

2.3.1Паттерн Adapter 52

2.3.2Паттерн Composite. 54

2.3.3Паттерн Decorator 58

2.3.4Паттерн Facade. 62

2.3.5Паттерн Low Coupling. 64

2.3.6Паттерн Information Expert 66

2.4Паттерны поведения. 70

2.4.1Паттерн Iterator 70

2.4.2Паттерн Mediator 74

2.4.3Паттерн Observer 78

2.4.4Паттерн State. 82

2.4.5Паттерн Strategy. 85

2.4.6Паттерн Template Method. 90

2.4.7Паттерн Chain Of Responsibility. 95

2.4.8Паттерн Command. 99

2.4.9Паттерн High Cohesion. 105

2.4.10Паттерн Don’t Talk To Strangers. 109

2.4.11Паттерн Polymorphism.. 111

2.4.12Паттерн Pure Fabrication. 113

2.4.13Паттерн Controller 114

3Архитектурные паттерны.. 117

3.1Расслоение системы.. 117

Обращение к базе данных, обмен сообщениями, управление транзакциями и т.д. 122

3.2Базовые типовые решения. 129

3.2.1Паттерт Layer Supertype. 129

3.2.2Паттерн Separated Interface. 130

3.2.3Паттерн Lazy Load. 133

3.2.4Паттерн Record Set 135

3.2.5Паттерн Unit of Work. 138

3.3Паттерны организация бизнес-логики. 142

3.3.1Паттерн Transaction Script148

3.3.2Паттерн Domain Model 151

3.3.3Паттерн Table Module. 154

3.4Паттерны организации источников данных. 159

3.4.1Объектные модели и реляционные базы данных. 159

3.4.2Архитектурные решения. 159

3.4.3Паттерн Table Data Gateway. 166

3.4.4Паттерн Row Data Gateway. 170

3.4.5Паттерн Active Record. 173

3.4.6Паттерн Data Mapper 176

3.4.7Функциональные проблемы.. 184

3.4.8Считывание данных. 187

3.4.9Взаимное отображение объектов и реляционных структур 189

3.4.10Соединение с базой данных. 199

3.5Паттерны представления данных в WEB.. 203

3.5.1Паттерн Model-View-Controller 206

3.5.2Паттерн Page Controller 210

3.5.3Паттерн Front Controller213

3.5.4Паттерн Template View.. 216

3.5.5Паттерн Transform View.. 224

3.5.6Паттерн Two Step View.. 226

Список используемой (рекомендуемой) литературы.. 231

Основная литература. 231

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

 


Введение

Процесс разработки больших программных систем чрезвычайно сложен и непредсказуем. Программные проекты часто прерываются, выходят за рамки сроков и бюджета или приводят к некачественным результатам, отделяя технологию программирования от установившихся инженерных дисциплин. Озадачивающие на первый взгляд недостатки программной "инженерии" легко объясняются тем фактом, что разработка программного обеспечения является ремеслом, а не инженерной дисциплиной. Чтобы стать наукой, разработка ПО должна подвергнуться сдвигу парадигмы от метода проб и ошибок к системе основных принципов. Хотя, естественно, некоторые принципы существуют благодаря таким выдающимся личностям как Кнут, Дейкстра, Вирт.… Но эти люди сформулировали лишь некоторые локальные правила. Они сформулировали алгоритмы решения тех или иных математических задач. К сожалению, в реальном мире разработчику очень часто приходится иметь дело далеко не с математикой, физикой, химией и другими точными науками. Разработчику приходится иметь дело с процессами, которые сформированы не законами физики, а человеческими отношениями. И если физик, с чем бы он не работал, имеет твердое убеждение, что можно найти общие принципы, будь то кварки или теория поля, у разработчика ПО нет такой утешительной веры.

Кони Бюрер, один из ведущих специалистов компании IBM, сравнивает сегодняшнее положение разработчика ПО с положением архитектора средневекового европейского готического собора. Эти люди, ничего не знали о законах физики, но тем не менее возводили величайшие сооружения. Они были классными мастерами. Но они не были не инженерами, не учеными. Возможно, через много лет разработчики будут создавать свои программы, имея строгую систему определений и теорем. Сегодня же приходится довольствоваться только некоторыми оптимальными методиками, полученными на основании опыта всего лишь одного поколения классных программистов. Причем эти методики отнюдь не гарантируют 100% правильности создаваемых программ. Часто приходится действовать методом проб и ошибок.

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

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









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


©2015- 2018 zdamsam.ru Размещенные материалы защищены законодательством РФ.