Урок №149. Ассоциация

  Юрий  | 

  |

  Обновл. 13 Сен 2021  | 

 38223

 ǀ   10 

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

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

Ассоциация

В ассоциации два несвязанных объекта должны соответствовать следующим отношениям:

   Первый объект (член) не связан со вторым объектом (классом).

   Первый объект (член) может принадлежать одновременно сразу нескольким объектам (классам).

   Первый объект (член) существует, не управляемый вторым объектом (классом).

   Первый объект (член) может знать или не знать о существовании второго объекта (класса).

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

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

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

Реализация ассоциаций


Ассоциации реализованы по-разному. Однако чаще всего они реализованы через указатели, где классы указывают на объекты друг друга.

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

Результат выполнения программы:

John is seeing patients: Anton
Tom is seeing patients: Anton Derek
Anton is seeing doctors: John Tom
Ivan has no doctors right now
Derek is seeing doctors: Tom

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

Рефлексивная ассоциация

Иногда объекты могут иметь отношения с другими объектами того же типа. Это называется рефлексивной ассоциацией. Хорошим примером рефлексивной ассоциации являются отношения между университетским курсом и его минимальными требованиями для студентов.

Рассмотрим упрощенный случай, когда Курс может иметь только одно Требование:

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

Ассоциации могут быть косвенными


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

Результат выполнения программы:

Ivan is driving a Focus

В примере, приведенном выше, у нас есть CarLot (Гараж) в котором находятся наши автомобили. Водитель, которому нужен Автомобиль, не имеет указателя на этот Автомобиль — вместо этого у него есть Идентификатор Автомобиля, который он может использовать для получения Автомобиля из Гаража, когда ему это нужно.

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

Композиция vs. Агрегация vs. Ассоциация

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

Свойства Композиция Агрегация Ассоциация
Отношения Части-целое Части-целое Объекты не связаны между собой
Члены могут принадлежать одновременно сразу нескольким классам Нет Да Да
Существование членов управляется классами Да Нет Нет
Вид отношений Однонаправленные Однонаправленные Однонаправленные или Двунаправленные
Тип отношений «Часть чего-то» «Имеет» «Использует»

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (224 оценок, среднее: 4,95 из 5)
Загрузка...

Комментариев: 10

  1. Юрий:

    Зачем мы удаляем конструктор по умолчанию CarLot?

    1. Ильдар:

      Над классом CarLot комментарии с ответом на вопрос

  2. Danny:

    // Примечание: Мы бы хотели сделать дружественным только один метод addDoctor(), но мы не можем это сделать, так как Doctor предварительно объявлен

    Мне не понятен этот комментарий из первого кода.
    Во-первых, метод addDoctor() — приватный, сделав его friend классу Docrtor, мы получим доступ к приватным членам класса, но как мы его вызовем из класса Doctor, если он приватный ?

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

    Метод addDoctor, который использует адрес объекта типа Doctor, не является дружественным классу не из-за того, что класс Doctor объявлен предварительно, как указанно в комментарии.
    Дублирую комментарий из первого кода еще раз :
    // Примечание: Мы бы хотели сделать дружественным только один метод addDoctor(), но мы не можем это сделать, так как Doctor предварительно объявлен

  3. Станислав:

    В первом коде, кстати, совсем не обязательно выносить определение метода Patient::addDoctor за пределы класса. Это понадобилось бы, если бы в определении метода использовались переменные-члены класса Doctor или методы класса Doctor. А т.к. в определении мы используем только лишь сам объект doc, то компилятору не нужно будет заглядывать в определение класса Doctor, чтобы понять, что от него хотят. Проверьте, мой вариант тоже будет работать=)

    1. Станислав:

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

      Компилятор в таком варианте ещё не знает про перегрузку

      поэтому мы бы получили ошибку

  4. Alcatraz:

    Юрий, пожалуйста ответьте!!!Скажите как в рефлексивной ассоциации создать объект, в котором второй параметр НЕ будет указывать на nullptr??? То есть как сделать так, чтобы часть(переменная-член) класса Course указывала на объект не инициализированный нулем.На что должна указывать часть???Надеюсь вы меня поймете…

  5. Тарас:

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

    Что значит:

    Почему не просто

    1. Apriori:

      Тарас,можете перечитать урок №124,но если вкратце,то для статических переменных-членов нужно предоставлять инициализатор(тип) для ЯВНОГО определения,так как переменная не принадлежит объектам класса ,а самому классу.

  6. Константин:

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

    1. Flicky:

      Да. Ты прав. В принцепе так можно, но пример выше служит исключительно для наглядности.

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *