Ya sabes crear objetos con datos y métodos. El siguiente paso no es hacer clases más grandes, sino conseguir que colaboren sin mezclar responsabilidades.
Hay dos relaciones que conviene separar desde el principio: «tiene un» suele apuntar a composición; «es un» puede justificar herencia.
Justificar una relación como “es un” o “tiene un” antes de elegir herencia o composición.
«Tiene un»: une piezas que pueden vivir por separado
class CalculadorEnvio:
def coste(self, urgente):
return 9 if urgente else 4
class Pedido:
def __init__(self, calculador):
self.calculador = calculador
def coste_envio(self, urgente):
return self.calculador.coste(urgente)
pedido = Pedido(CalculadorEnvio())
print(pedido.coste_envio(True))Pedido tiene un CalculadorEnvio. Cada clase conserva una tarea concreta y el pedido delega el cálculo.
Pregunta útil: si mañana aparece otra forma de calcular el envío, ¿puedes sustituir esa pieza sin convertir el pedido en otra cosa? Si la respuesta es sí, la composición encaja bien.
«Es un»: hereda solo cuando el subtipo sigue siendo el mismo concepto
class Pago:
def descripcion(self):
return "pago"
class Transferencia(Pago):
def descripcion(self):
return "transferencia"
pago = Transferencia()
print(pago.descripcion())Transferencia puede tratarse como un Pago, pero especializa su comportamiento. La herencia tiene sentido porque la frase «una transferencia es un pago» describe el modelo.
No necesitas memorizar términos como polimorfismo para usar esta idea: basta con preguntar si el subtipo puede ocupar razonablemente el lugar del tipo base.
La reutilización de código no basta para justificar una jerarquía
Un error frecuente es heredar solo porque otra clase ya contiene un método que te interesa. Eso puede crear relaciones absurdas: un Informe no debería heredar de Impresora solo para reutilizar imprimir().
Si la relación «es un» suena forzada, prueba primero con composición: guarda el objeto colaborador como atributo y úsalo cuando haga falta.
Decide la relación antes de programarla
Coche — motor
¿“Coche es un motor” o “Coche tiene un motor”?
Comprobar razonamiento
Tiene un → composición. El motor es una pieza que el coche utiliza.
Perro — animal
¿“Perro es un animal” o “Perro tiene un animal”?
Comprobar razonamiento
Es un → herencia puede tener sentido si el subtipo respeta el contrato de Animal.
Informe — exportador PDF
¿El informe “es” un exportador o utiliza uno?
Comprobar razonamiento
Normalmente tiene/usa un exportador → composición. Heredar solo para reutilizar código suele modelar mal la relación.
class Animal:
def sonido(self):
return "?"
class Perro(Animal):
def sonido(self):
return "guau"Aquí “Perro es un Animal” sigue siendo verdadero. No necesitas construir una jerarquía mayor para entender la idea.
Completa una relación “tiene un”: el reproductor delega en su altavoz en vez de heredar de él.
sonido listo
La salida aparecerá aquí.
Necesito una pista
- El reproductor ya guarda el objeto en
self.altavoz. - Haz que
sonar()llame aself.altavoz.emitir().
Ver una solución razonada
class Altavoz:
def emitir(self):
return "sonido listo"
class Reproductor:
def __init__(self, altavoz):
self.altavoz = altavoz
def sonar(self):
return self.altavoz.emitir()
reproductor = Reproductor(Altavoz())
print(reproductor.sonar())Una brújula de dos preguntas evita la mayoría de decisiones confusas
- ¿La frase natural es «A tiene un B»? Empieza pensando en composición.
- ¿La frase natural es «A es un B» y A puede usarse donde se espera B? La herencia puede tener sentido.
- ¿Solo quieres reutilizar unas líneas? Extraer una función o componer objetos suele ser más simple que heredar.
te muestran dos clases y puedes explicar, sin mirar la sintaxis, si deberían colaborar mediante un atributo o formar una jerarquía.