es una relación de tipo — modelada con ( ). es una relación de tipo — modelada con ( ). Elegir la correcta es una decisión fundamental de modelado.
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
}
Pregúntate: "¿Es X una clase de Y, o tiene X una/usa 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) ✅
Las personas recurren a la herencia para reutilizar código, incluso cuando la relación es realmente has-a. Si nunca podrías sustituir la subclase por la base en todas partes, probablemente no sea is-a — usa composición.
Esta distinción es la regla de decisión práctica detrás de "favorece composición sobre herencia": elige la relación que sea verdadera, no la que ahorre algunas líneas.
Hacerlo correctamente mantiene las jerarquías poco profundas y honestas, y previene violaciones de Liskov donde un "subtipo" no pueda realmente reemplazar a su padre.