é uma relação de — modelada com (um ). é uma relação de — modelada com (um ). Escolher a correta é uma decisão de modelagem essencial.
CarVehicleCarEngine// IS-A → inheritance
class Vehicle { void move() {} }
class Car extends Vehicle { } // a Car IS A Vehicle
// HAS-A → composition
class Engine { void start() {} }
class Car2 {
private Engine engine = new Engine(); // a Car HAS AN Engine
void start() { engine.start(); } // delegate to the part
}
Pergunte a si mesmo: "X é um tipo de Y, ou X possui um Y?"
A Dog IS-A Animal → inheritance ✅
A Car HAS-A Engine → composition ✅
A Square IS-A Shape → inheritance ✅
A Manager HAS Employees → composition (a list) ✅
A Stack HAS-A list (not IS-A) → composition (see earlier pitfall) ✅
As pessoas recorrem à inheritance para reutilizar código, mesmo quando o relacionamento é realmente has-a. Se você nunca substituiria a subclasse pela base em todos os lugares, provavelmente não é is-a — use composition.
Esta distinção é a regra prática de decisão por trás de "prefira composition em vez de inheritance": escolha o relacionamento que é verdadeiro, não aquele que poupa algumas linhas.
Fazer isso corretamente mantém as hierarquias rasas e honestas, e previne violações de Liskov onde um "subtipo" não pode realmente se substituir ao seu pai.