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 Уровень 36
9 февраля 2024
ощущение недопонимания после прочтения
SobolenkoE Уровень 30
22 января 2024
композиция лучше наследования.
ElenaN Уровень 37
6 ноября 2023
Читала Брюса Эккеля и решила поискать более развернутую информацию с примерами composition, delegation и синтеза composition+inheritance, так как синтаксис там совсем другой, это не обычное наследование... В этой статье, к сожалению, примеров не нашла. Очень хорошо объяснено, про отношение has-a, но как оно воплощается в коде? UPD: Если кому-то интересно, то вот здесь нашла пример Пример Композиции По сути это просто использование методов/переменных другого класса через instance variable (поправьте, если я не права). Ну и вот тут еще мистер Кумар в деле показывает, как это работает) Видео о композиции
Denis Gritsay Уровень 37
19 октября 2023
спорно - " Самый простой пример — двигатель автомобиля. Двигатель является частью автомобиля, но не может быть частью другого автомобиля. ?" вы никогда не изучали как устроен двигатель? и двигатель, и пассажир, может быть выведен в отдельный класс или интерфейс и наследоватся в разных автомобилях. У любого двигателя могут быть общие с другими двигателями поля и методы - мощность, количество цилиндров и т.д. спорная часть статьи про агрегирование и композицию ....
11 октября 2023
Хорошо, нам понятно объяснили что такое композиция и агрегация, но почему же это важно?? Это настолько важно, что они даже придумали термины для обозначения этого, но почему же???
Anonymous #3336441 Уровень 45
4 октября 2023
Я так понимаю что композиция и агрегирование никак не относятся к наследованию, и являются отдельными взаимоотношениями между классами. По идеи при композиции классы должны быть вложенными, а при агрегации нет. Поправьте меня если не прав) И как при агрегации, объект является частью другого объекта?????
дмитрий Уровень 22
31 августа 2023
Статья без приведения примера чистого и ясного кода иллюстрирующего сказанное треп ни о чем. Чтоб до конца понять тему нужно читать другие статьи (в сторонних источниках). Кстати, во всех качественных источниках (с кодом) написание кода предваряется вышеприведенными в статье рассуждениями. То есть, чтение этой статьи потеря времени. Идем на хабр. Да, не обязательно - если сениор, то умеешь обьяснять. А вообще статья напоминает старый одесский анекдот: "Изя вам Моцарт нравиться? Нет. Вы что слушали Моцарта. Нет мне Моня напел."
chess.rekrut Уровень 25
18 августа 2023
easy
Ingenieur Уровень 22
11 августа 2023
переживаю за отношения классов больше, чем за свои
Alexander Rozenberg Уровень 32
25 июля 2023
fine