Урок №148. Агрегация

  Юрий  | 

  Обновл. 24 Июн 2019  | 

 9246

 ǀ   8 

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

В этом уроке мы рассмотрим второй подтип композиции объекта — агрегацию.

Агрегация

Для реализации агрегации целое и его части должны соответствовать следующим отношениям:

   Часть (член) является частью целого (класса).

   Часть (член) может принадлежать более чем одному целому (классу) за раз.

   Часть (член) существует, не управляемая целым (классом).

   Часть (член) не знает о существовании целого (класса).

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

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

В качестве альтернативы, рассмотрим автомобиль и двигатель. Двигатель является частью автомобиля. И хотя двигатель принадлежит автомобилю, он может принадлежать и другим объектам, например, человеку, которому принадлежит автомобиль. Автомобиль не несёт ответственности за создание или уничтожение двигателя. И в то же время автомобиль знает, что у него есть двигатель (ведь благодаря ему он двигается), но сам двигатель не знает, что он является частью автомобиля.

Когда дело доходит до моделирования физических объектов, использование термина «уничтожение» может быть немного расплывчатым. Один может утверждать: «Если бы метеорит упал с неба и раздавил машину, то можно ли считать, что и все части машины также были бы уничтожены?» Да, конечно. Но это вина метеорита, а не автомобиля. Важным моментом является то, что автомобиль не несёт ответственности за уничтожение своих частей (но есть и внешняя сила, которая может этому способствовать).

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

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

Реализация агрегации


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

В агрегации мы также добавляем части к целому, используя переменные-члены. Однако этими переменными-членами обычно являются либо ссылки, либо указатели, которые указывают на объекты, созданные за пределами класса. Следовательно, агрегация принимает части, на которые она будет указывать, в качестве параметров конструктора или, если параметров нет, части добавляются позже через функции доступа или через перегруженные операторы.

Поскольку эти части существуют вне области видимости класса, то при уничтожении класса, переменные-члены в виде ссылок или указателей также уничтожаются (но не удаляются значения, на которые они указывают). Следовательно, сами части продолжают существовать дальше.

Рассмотрим пример Работника и Отдела более подробно. Чтобы было проще, в Отделе работает только один Работник и он не знает, Работником какого именно Отдела он является:

Здесь Работник создаётся независимо от Отдела, а затем переходит в параметр конструктора класса Отдела. Когда department уничтожается, то указатель m_worker уничтожается также, но сам Работник то не удаляется, он существует и дальше до тех пор, пока не будет уничтожен в main().

Выбирайте правильные отношения

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

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

Правило: Реализовывайте самые простые отношения, которые соответствует потребностям вашей программы, а не то, что, как вам кажется, будет лучшим.

Композиция и агрегация


В композиции:

   Используются обычные переменные-члены.

   Используются указатели, если класс реализовывает собственное управление памятью (происходит динамическое выделение/освобождение памяти).

   Класс ответственный за создание/уничтожение своих частей.

В агрегации:

  Используются указатели/ссылки, которые указывают/ссылаются на части вне класса.

  Класс не несёт ответственности за создание/уничтожение своих частей.

Стоит отметить, что идеи композиции и агрегации не являются взаимоисключающими и могут свободно смешиваться в одном классе. Вполне возможно реализовать класс, который отвечает за создание/уничтожение только определённых частей. Например, наш класс Department мог бы иметь и Имя, и Работника. Имя было бы добавлено в класс через композицию и создавалось/уничтожалось бы вместе с объектами класса Department. А Работник был бы добавлен в Department через агрегацию и создавался/уничтожался бы независимо/отдельно.

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

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

Тест

Задание №1

Что вы бы использовали: агрегацию или композицию, для реализации следующего?

   Красный шар.

   Работодатель, который нанимает людей.

   Факультет в университете.

   Ваш возраст.

   Мешок с шариками.

Ответ №1

   Композиция: Цвет является неотъемлемым свойством шара.

   Агрегация: Работодатель в начале не имеет никаких работников и, надеемся, не уничтожит всех своих сотрудников, когда обанкротится.

   Композиция: Факультеты не могут существовать отдельно от университета.

   Композиция: Ваш возраст является неотъемлемым свойством Вас.

   Агрегация: Мешок и шарики внутри являются независимыми объектами и могут существовать раздельно.

Задание №2

Обновите пример выше с Работником/Отделом, чтобы Отдел мог состоять из нескольких Работников. Следующий код:

Должен производить следующий результат:

Department: Anton Ivan Max
Anton still exists!
Ivan still exists!
Max still exists!

Подсказка: Используйте std::vector для хранения Работников. Используйте std::vector::push_back() для добавления Работника. Используйте std::vector::size() для получения длины std::vector на вывод.

Ответ №2


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

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

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

  1. Аватар Victor:

    Ну тут относительно всё, большинство можно описать и агрегацией, и композицией. В зависимости от задачи… И вычислительных особенностей. Мне кажется не так надо передавать свойства реальных обьектов как просто подобрать оптимальный вариант к задаче, с т.з производительности, памяти, и интерфейса взаимодействия. Так ведь?

    1. Аватар Victor:

      И ещё. Если например у класса который реализует агрегацию, есть методы которые управляют жизнью внешнего обьекта?
      Ну например у Car
      есть метод destroyEngine(Engine e);
      Который уничтожает внешний обьект, это будет композиция уже?( ну если он будет вызываться в деструкторе, то точно )..
      В предыдущем уроке ведь указано что может в композиции создаваться внутренний обьект вне обьекта родителя. И насколько это вообще адекватно?
      А если destroy метод вызывается только пользователем? Это уже агрегация?
      И в первом случае, когда обьект управляет временем жизни дочернего обьекта, как предупредить пользователя об этом? средствами яп. Допутим он создал энжин, передал его в кар, кар отработал, умер, и разрушил энжин, пользователь об этом не знает, и вызывает engine->upgrade(…); как избежать таких случаев??? Или хотя бы предупредить о не возможности таких случаев, того кто будет использовать Car.

  2. Аватар somebox:

    Не могу до конца понять, обязательно ли каждый раз создавать функцию перегрузки оператора вывода?

    1. Аватар Анастасия:

      Если есть потребность выводить данные класса по имени объекта — да

  3. Аватар Nikita:

    А как понять, что и когда использовать, агрегацию по ссылке или по указателю?

    1. Аватар Александр:

      ссылки безопасней, но менее гибкие… отсюда и выбор

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

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

  4. Аватар Игорь:

    Всем привет.Моё решение:

  5. Аватар Anastasia:

    Для вывода вектора необязательно использовать size:

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

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

telegram канал
НОВОСТИ RAVESLI