Ответ
Наследование создает жесткую связь «является» (is-a). Альтернативы реализуют отношение «имеет» (has-a) или полиморфизм без наследования реализации, что повышает гибкость и тестируемость кода.
1. Композиция (Composition) Класс содержит (владеет) экземпляры других классов, делегируя им часть обязанностей. Это самый мощный и рекомендуемый подход.
class Engine {
public void start() { System.out.println("Engine started"); }
}
// Car HAS-A Engine (композиция)
class Car {
private final Engine engine; // Контроль жизненного цикла
public Car(Engine engine) { this.engine = engine; }
public void start() { engine.start(); } // Делегирование
}
// Использование: Car car = new Car(new Engine());
Почему лучше: Позволяет изменять поведение во время выполнения, легче тестировать (можно передать mock-объект Engine).
2. Агрегация (Aggregation) Частный случай композиции, где содержащий объект не управляет жизненным циклом содержащихся объектов (связь слабее).
3. Интерфейсы (Interfaces) Определяют контракт (что можно делать), не предоставляя реализацию. Класс может реализовывать множество интерфейсов.
interface Flyable {
void fly();
}
interface Swimmable {
void swim();
}
// Класс реализует поведение, не будучи наследником
class Duck implements Flyable, Swimmable {
@Override public void fly() { /* ... */ }
@Override public void swim() { /* ... */ }
}
4. Паттерны проектирования:
- Стратегия (Strategy) – инкапсуляция семейства алгоритмов в отдельные классы для взаимозаменяемости.
- Декоратор (Decorator) – динамическое добавление обязанностей объекту через обертки.
Принцип: «Предпочитайте композицию наследованию» (Favor composition over inheritance, GoF).