Коротка відповідь
juniorКомпозиція — це створення об’єкта шляхом вбудовування інших об’єктів, що надають функціональність; наслідування — це створення нового класу, що успадковує властивості та методи базового класу. У композиції залежність «має» (has‑a), у наслідуванні — «є» (is‑a).
Повне пояснення
Що це і навіщо
Композиція та наслідування – два фундаментальних патерни об’єктно‑орієнтованого дизайну. Вони допомагають організувати код, забезпечуючи реюзабельність і розширюваність.
Ключові принципи
- Наслідування (inheritance): клас‑потомок отримує всі члени базового класу. Це «is‑a» зв’язок.
- Композиція (composition): клас містить інші об’єкти як поля. Це «has‑a» зв’язок.
Як це працює
- Наслідування: під час компіляції створюється ієрархія, а методи викликаються через віртуальний таблицю (vtable). Перевага – простота, недосяжність поліморфізму.
- Композиція: об’єкт створюється, а потім «передає» відповідальність іншим об’єктам. Перевага – більша гнучкість, менше залежностей.
Практика і реалізація
// Наслідування
class Vehicle { move() {} }
class Car extends Vehicle { honk() {} }
// Компонування
class Engine { start() {} }
class Car {
constructor(private engine: Engine) {}
drive() { this.engine.start(); }
}
Тестування
import { describe, it, expect } from 'vitest';
describe('Car', () => {
it('uses engine to drive', () => {
const mockEngine = { start: vi.fn() };
const car = new Car(mockEngine as any);
car.drive();
expect(mockEngine.start).toHaveBeenCalled();
});
});
Дизайн‑рішення
- Якщо поведінка є загальною і не змінюється, використовуйте наслідування.
- Якщо поведінка змінювана або потрібна мульти‑параметрична конфігурація, використовуйте композицію.
Оптимізація та edge‑cases
- Наслідування створює тверді зв’язки, що ускладнюють тестування.
- Композиція дозволяє легко мокати залежності, але потребує більше коду для проксування.
Диагностика
- Симптом: «під час зміни базового класу падає багато тестів» → причина: сильна залежність через наслідування.
- Дія: розділити поведінку на окремі сервіси і використати композицію.
Альтернативи
- Патерн «Strategy» – композиція з функціональними об’єктами.
- Патерн «Decorator» – композиція для розширення функціональності без наслідування.
Часті помилки
- Перевантаження наслідування там, де потрібна композиція.
- Використання protected полів замість інтерфейсів.
- Залежність від конкретних реалізацій у базовому класі.
- Необхідність змінювати базовий клас для додавання нових функцій.
- Переважання «extends» над «implements» у TypeScript.
- Втрата типобезпеки через
anyв композиції. - Неправильне використання
super()у конструкторі. - Занадто глибока ієрархія класів, що ускладнює розуміння коду.