Java for Beginner
743 subscribers
708 photos
196 videos
12 files
1.14K links
Канал от новичков для новичков!
Изучайте Java вместе с нами!
Здесь мы обмениваемся опытом и постоянно изучаем что-то новое!

Наш YouTube канал - https://www.youtube.com/@Java_Beginner-Dev

Наш канал на RUTube - https://rutube.ru/channel/37896292/
Download Telegram
Основы ООП в Java

Глава 7. Принципы проектирования и хорошего кода

Композиция vs Наследование

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

Наследование:
Механизм, при котором класс (подкласс) наследует поля, методы и поведение от другого класса (суперкласса) с помощью ключевого слова extends. Это воплощает отношение "является" (is-a). Например, Dog является Animal.

Композиция:
Механизм, при котором класс содержит объекты других классов как поля, используя их функциональность. Это воплощает отношение "имеет" (has-a). Например, Car имеет Engine.

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



Наследование: Когда использовать и проблемы

Наследование удобно, когда классы имеют четкое отношение "является", и подкласс естественным образом расширяет поведение суперкласса.

Пример наследования:
public class Animal {
protected String name;

public Animal(String name) {
this.name = name;
}

public void makeSound() {
System.out.println(name + " издает звук.");
}
}

public class Dog extends Animal {
public Dog(String name) {
super(name);
}

@Override
public void makeSound() {
System.out.println(name + " лает: Гав!");
}
}


Когда использовать:
Есть четкое is-a отношение: Dog — это Animal.
Нужно переопределить или расширить поведение (через override).
Полиморфизм: Работа с подклассами через ссылку на суперкласс (Animal[] animals).


Преимущества:
Простота: Код автоматически наследуется.
Полиморфизм: Легко реализовать через override.
Иерархия: Удобно для моделирования реальных сущностей (например, Животное → Млекопитающее → Собака).


Недостатки:
Жесткая связь: Подкласс привязан к суперклассу — изменения в суперклассе могут сломать подклассы.
Хрупкий базовый класс: Изменение метода в суперклассе может неожиданно повлиять на подклассы.
Ограничение Java: Только одиночное наследование (один extends).
Сложность в глубоких иерархиях: Много уровней наследования трудно поддерживать.
Нарушение инкапсуляции: Подклассы могут зависеть от внутренней реализации.

Пример проблемы: Если добавить метод в Animal, который не подходит для всех подклассов (например, fly()), подклассы вроде Dog окажутся в неудобном положении.


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

Композиция — это включение объектов других классов как полей, чтобы использовать их функциональность. Это отношения, где класс владеет компонентами.


Пример композиции:
public class Engine {
private int horsepower;

public Engine(int horsepower) {
this.horsepower = horsepower;
}

public void start() {
System.out.println("Двигатель с " + horsepower + " л.с. запущен.");
}
}

public class Car {
private String model;
private Engine engine; // Композиция: Car "имеет" Engine

public Car(String model, Engine engine) {
this.model = model;
this.engine = engine;
}

public void drive() {
engine.start();
System.out.println(model + " едет.");
}
}


Использование:
public class Main {
public static void main(String[] args) {
Engine engine = new Engine(200);
Car car = new Car("Toyota", engine);
car.drive(); // Двигатель с 200 л.с. запущен. Toyota едет.
}
}


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


Преимущества:
Гибкость: Легко заменить компонент (например, Engine на ElectricEngine).
Слабая связь: Изменения в Engine не ломают Car, если интерфейс сохранен.
Инкапсуляция: Car скрывает детали Engine, управляя доступом.
Модульность: Компоненты можно использовать в других классах.
Тестирование: Легче подменять компоненты (mocking) для тестов.


#Java #для_новичков #beginner #OOP #Composition #Extends
👍4
Недостатки:
Больше кода: Нужно явно создавать и передавать компоненты.
Сложнее полиморфизм: Требуются интерфейсы для единообразия.



Композиция vs Наследование: Как выбирать

Выбирайте композицию, если:
Нет четкого is-a отношения, а скорее has-a.
Нужна гибкость: Компоненты могут меняться (например, разные реализации Engine).
Хотите избежать жесткой связи или хрупкого базового класса.
Нужно реализовать поведение, которое не должно быть частью иерархии.

Выбирайте наследование, если:
Есть четкое is-a отношение, и подкласс логично расширяет суперкласс.
Нужен полиморфизм через ссылки на суперкласс.
Иерархия простая (1-2 уровня).


Пример: Композиция вместо наследования:

Наследование (проблема):
public class Vehicle {
public void move() {
System.out.println("Движется с помощью двигателя.");
}
}

public class Car extends Vehicle {
// Проблема: А если электромобиль или велосипед?
}

Композиция (решение):
public interface Movable {
void move();
}

public class EngineMovable implements Movable {
@Override
public void move() {
System.out.println("Движется с помощью двигателя.");
}
}

public class Car {
private Movable movement;

public Car(Movable movement) {
this.movement = movement;
}

public void drive() {
movement.move();
}
}

Теперь Car может использовать любой Movable (двигатель, электромотор), без привязки к иерархии.


Все нюансы

Наследование:
Жесткая связь: Подкласс зависит от реализации суперкласса.
Проблема ромба: В Java избежана (одиночное наследование), но множественное через интерфейсы.
Нюанс: Переопределение может нарушить контракт (Liskov Substitution Principle).


Композиция:
Требует интерфейсов для полиморфизма (например, Movable).
Нюанс: Больше кода для передачи компонентов (конструктор, сеттеры).
Делегирование: Car вызывает методы Engine — это нормально.


Когда комбинировать:
Используйте наследование для базовой структуры, композицию для поведения.
Пример: extends AbstractVehicle, но Engine как поле.


Ошибки:
Наследование вместо композиции: Глубокие иерархии или неподходящие is-a.
Композиция без интерфейсов: Теряется полиморфизм.


Дизайн:
Композиция соответствует принципу KISS (меньше связей).
Наследование — YAGNI: Не создавайте иерархии "на будущее".
DRY: Оба подхода помогают повторно использовать код.


Java-специфика:
Интерфейсы + default методы часто заменяют наследование.
Dependency Injection (Spring) — пример композиции.



Как создать это в IntelliJ IDEA

Композиция: Создайте интерфейс (New → Interface), класс с полем и методами.
Наследование: New → Class, укажите extends, IDE поможет override.
Рефакторинг: Extract Interface для перехода от наследования к композиции.



Полезные советы для новичков

Композиция по умолчанию: Если сомневаетесь, начните с has-a.
Проверяйте is-a: Dog is-an Animal? Да. Car is-an Engine? Нет.
Интерфейсы для гибкости: Комбинируйте композицию с интерфейсами.
Избегайте глубоких иерархий: Не больше 2-3 уровней.



#Java #для_новичков #beginner #OOP #Composition #Extends
👍4