JavaRush/Java блог/Java Developer/Отношения между классами. Наследование, композиция и агре...
Автор
Василий Малик
Senior Java-разработчик в CodeGym

Отношения между классами. Наследование, композиция и агрегирование

Статья из группы Java Developer
участников
Привет! Сегодня мы подробно рассмотрим еще один принцип объектно-ориентированного программирования (ООП) — наследование. Заодно изучим другие типы отношений между классами — композицию и агрегирование. Эта тема не будет сложной: ты уже много раз сталкивался с наследованием и его примерами в прошлых лекциях. Сегодня главным будет закрепить твои знания, подробнее рассмотреть механизм наследования и еще раз пробежаться по примерам :) Итак, поехали!
Отношения между классами. Наследование, композиция и агрегирование  - 1

Наследование в Java и его преимущества

Как ты наверняка помнишь, наследование (inheritance) — механизм, который позволяет описать новый класс на основе существующего (родительского). При этом свойства и функциональность родительского класса заимствуются новым классом. Давай вспомним пример наследования из предыдущих лекций:
public class Car {

   private String model;
   private int maxSpeed;
   private int yearOfManufacture;

   public Car(String model, int maxSpeed, int yearOfManufacture) {
       this.model = model;
       this.maxSpeed = maxSpeed;
       this.yearOfManufacture = yearOfManufacture;
   }


public void gas() {
       //...газ
   }

public void brake() {
       //...тормоз
   }
}


public class Truck extends Car {

   public Truck(String model, int maxSpeed, int yearOfManufacture) {
       super(model, maxSpeed, yearOfManufacture);
   }
}



public class Sedan extends Car {
   public Sedan(String model, int maxSpeed, int yearOfManufacture) {
       super(model, maxSpeed, yearOfManufacture);
   }
}
Есть некая программа, в рамках которой мы работаем с различными типами автомобилей. Даже если ты не автолюбитель, наверняка знаешь, что типов этих самых автомобилей на свете великое множество :) Поэтому общие свойства автомобилей выделяем в общий класс-родитель — Car. А что общего у всех автомобилей вне зависимости от типа? У любой машины есть год выпуска, название модели и максимальная скорость. Эти свойства выносим в поля model, maxSpeed, yearOfManufacture. Что касается поведения, любая машина может газовать и тормозить :) Это поведение мы определяем в методах gas() и brake(). Какие выгоды это нам дает? Прежде всего — сокращение объема кода. Конечно, можем обойтись и без родительского класса. Но поскольку каждая машина должна уметь газовать и тормозить, нам придется создавать методы gas() и brake() в классе Truck, в классе Sedan, в классе F1Car, в классе Sportcar и во всех остальных классах машин. Представь, сколько лишнего кода мы при этом напишем. Не забывай и о полях model, maxSpeed и yearOfManufacture: если откажемся от родительского класса, будем создавать их в каждом из классов-машин! Отношения между классами. Наследование, композиция и агрегирование  - 2 Когда у нас наберется пара десятков классов-машин, объем повторяющегося кода станет действительно серьезным. Вынесение общих полей и методов (еще говорят — «состояния» и «поведения») в класс-родитель позволит нам сэкономить кучу времени и места. Если же у какого-то типа есть свойства или методы, уникальные только для него и отсутствующие у других типов машин, — не беда. Их всегда можно создать в классе-потомке, отдельно от всех остальных.
public class F1Car extends Car {

   public void pitStop() {

       //...пит-стоп делают только гоночные автомобили
   }

   public static void main(String[] args) {

       F1Car formula1Car = new F1Car();
       formula1Car.gas();
       formula1Car.pitStop();
       formula1Car.brake();
   }
}
Возьмем случай с гоночными машинами Формулы-1. У них, в отличие от «сородичей», есть уникальное поведение — время от времени они заезжают на пит-стоп. Нам это не мешает. Общее поведение мы уже описали в родительском классе Car, а специфическое поведение классов-потомков можем добавить внутри классов. Отношения между классами. Наследование, композиция и агрегирование  - 3 Это касается и полей: если у дочернего класса есть уникальные свойства, спокойно объявляем эти поля внутри него и не переживаем :) Возможность повторного использования кода — главное преимущество наследования. Для программиста очень важно не писать лишний объем кода. Ты не раз столкнешься с этим в работе. Пожалуйста, запомни еще одну крайне важную вещь: в Java нет множественного наследования. Каждый класс наследуется только от одного класса. О причинах этого подробнее поговорим в будущих лекциях, пока просто запомни. Этим Java, кстати, отличается от некоторых других ООП-языков. Например, в С++ множественное наследование есть. С наследованием все более-менее ясно — идем дальше.

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

Классы и объекты могут быть связаны друг с другом. Наследование описывает связь «является» (или по-английски «IS A»). Лев является Животным. Такое отношение легко выразить с помощью наследования, где Animal будет родительским классом, а Lion — потомком. Однако не все связи отношения в мире описываются таким образом. К примеру, клавиатура определенно как-то связана с компьютером, но она не является компьютером. Руки как-то связаны с человеком, но они не являются человеком. В этих случаях в его основе лежит другой тип отношения: не «является», а «является частью» («HAS A»). Рука не является человеком, но является частью человека. Клавиатура не является компьютером, но является частью компьютера. Отношения HAS A можно описать в коде, используя механизмы композиции и агрегирования. Разница между ними заключается в «строгости» этих связей. Приведем простой пример: У нас есть наш Car — машина. У каждой машины есть двигатель. Кроме того, у каждой машины есть пассажиры внутри. В чем же принципиальная разница между полями Engine engine и Passenger [] passengers? Если у машины внутри сидит пассажир А, это не значит, что в ней не могут находиться пассажиры B и C. Одна машина может соответствовать нескольким пассажирам. Кроме того, если всех пассажиров высадить из машины, она продолжит спокойно функционировать. Связь между классом Car и массивом пассажиров Passenger [] passengers менее строгая. Она называется агрегацией. Есть неплохая статья на эту тему: Отношения между классами (объектами). В ней приведен еще один хороший пример агрегации. Допустим, у нас есть класс Student, обозначающий студента, и класс StudentsGroup (группа студентов). Студент может входить и в клуб любителей физики, и в студенческий фан-клуб «Звездных войн» или команду КВН. Композиция — более строгий тип связи. При использовании композиции объект не только является частью какого-то объекта, но и не может принадлежать другому объекту того же типа. Самый простой пример — двигатель автомобиля. Двигатель является частью автомобиля, но не может быть частью другого автомобиля. Как видишь, их связь гораздо более строгая, чем у Car и Passengers. Отношения между классами. Наследование, композиция и агрегирование  - 4
Комментарии (203)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Codemilex Backend Developer
9 февраля, 06:41
ощущение недопонимания после прочтения
SobolenkoE Python Developer
22 января, 13:38
композиция лучше наследования.
awaithonley
Уровень 19
8 марта, 16:53
Как я понял это лишь одна из реализация наследования , не ?
ElenaN
Уровень 37
6 ноября 2023, 01:52
Читала Брюса Эккеля и решила поискать более развернутую информацию с примерами composition, delegation и синтеза composition+inheritance, так как синтаксис там совсем другой, это не обычное наследование... В этой статье, к сожалению, примеров не нашла. Очень хорошо объяснено, про отношение has-a, но как оно воплощается в коде? UPD: Если кому-то интересно, то вот здесь нашла пример Пример Композиции По сути это просто использование методов/переменных другого класса через instance variable (поправьте, если я не права). Ну и вот тут еще мистер Кумар в деле показывает, как это работает) Видео о композиции
Denis Gritsay
Уровень 35
19 октября 2023, 10:40
спорно - " Самый простой пример — двигатель автомобиля. Двигатель является частью автомобиля, но не может быть частью другого автомобиля. ?" вы никогда не изучали как устроен двигатель? и двигатель, и пассажир, может быть выведен в отдельный класс или интерфейс и наследоватся в разных автомобилях. У любого двигателя могут быть общие с другими двигателями поля и методы - мощность, количество цилиндров и т.д. спорная часть статьи про агрегирование и композицию ....
prottobone
Уровень 28
Expert
27 октября 2023, 12:48
Как я это понял: машина не может существовать и правильно функционировать отдельно, без двигателя. Без пассажиров же - может. Пассажиры могут быть в разных машинах одновременно. Двигатель может быть только в одной машине одновременно. В двух машинах будут два разных двигателя. Пока мне тоже не все понятно по этой теме, разбираюсь дальше
Anonymous #3285315
Уровень 18
26 ноября 2023, 22:26
не совсем понятно, как один пассажир может быть в двух разных машинах одновременно? Может не совсем удачный пример. В ссылке в статье есть более удачный пример про агрегацию : один студент и много групп в которых он состоит
Дмитрий
Уровень 110
Expert
вчера, 08:20
Ничего спорного нет. Да, двигатель не может быть частью другого автомобиля. Всё зависит от контекста использования объекта. С точки зрения пользователя, двигатель - неотъемлемая часть машины. Замена и ремонт двигателя - инкапсулированы, к этой группе операций имеют доступ пользователи с ролью автосервис. Пользователь да, может и сам разобрать двигатель, но тогда он, например, слетает с гарантии, и самостоятельно несёт последствия своего ремонта без соблюдения технологий. Он не авторизован для выполнения данных операций.
11 октября 2023, 10:51
Хорошо, нам понятно объяснили что такое композиция и агрегация, но почему же это важно?? Это настолько важно, что они даже придумали термины для обозначения этого, но почему же???
Anonymous #3336441
Уровень 44
4 октября 2023, 14:03
Я так понимаю что композиция и агрегирование никак не относятся к наследованию, и являются отдельными взаимоотношениями между классами. По идеи при композиции классы должны быть вложенными, а при агрегации нет. Поправьте меня если не прав) И как при агрегации, объект является частью другого объекта?????
дмитрий
Уровень 22
31 августа 2023, 19:14
Статья без приведения примера чистого и ясного кода иллюстрирующего сказанное треп ни о чем. Чтоб до конца понять тему нужно читать другие статьи (в сторонних источниках). Кстати, во всех качественных источниках (с кодом) написание кода предваряется вышеприведенными в статье рассуждениями. То есть, чтение этой статьи потеря времени. Идем на хабр. Да, не обязательно - если сениор, то умеешь обьяснять. А вообще статья напоминает старый одесский анекдот: "Изя вам Моцарт нравиться? Нет. Вы что слушали Моцарта. Нет мне Моня напел."
Jumper
Уровень 23
26 сентября 2023, 20:18
Ну вы даете Дмитрий. В статье есть несколько примеров на основе которых отлично всё разжёвано. В статье речь не про код.
Павло java newcomer
27 сентября 2023, 04:01
Несправедливо!!!! Не нравится - пропускаем.
DOTA Funny
Уровень 10
Expert
4 октября 2023, 07:25
как по мне - довольно понятно написано)
17 октября 2023, 09:17
Согласен. Статья без примеров малополезна.
chess.rekrut
Уровень 25
18 августа 2023, 13:09
easy
Ingenieur
Уровень 22
11 августа 2023, 09:17
переживаю за отношения классов больше, чем за свои
Alexander Rozenberg
Уровень 32
25 июля 2023, 12:59
fine