Saltar al contenido
Explorar conocimiento

concepto · fundamentos · 6 min de lectura

Herencia

La herencia permite definir una clase especializada a partir de otra, reutilizando comportamiento común y cambiando solo lo que la subclase necesita adaptar.

Antes de leer esto, conviene conocer: Clase.

Alcance en breve

Cubre

  • inheritance as a reuse and specialization relationship between classes
  • superclass, subclass, method overriding, and constructor delegation at a conceptual level
  • basic trade-offs between inheritance and composition

No cubre

  • multiple inheritance and trait systems
  • prototype-chain internals beyond the high-level idea
  • framework-specific inheritance models

Supone

  • The reader understands classes, instances, fields, and methods at a foundation level.

Resumen

Herencia es una relación entre clases donde una clase más específica reutiliza y especializa el comportamiento de una clase más general. La clase base, o superclase, define miembros comunes. La subclase hereda esos miembros y puede agregar datos, agregar métodos o redefinir métodos existentes.

Importa porque permite expresar una relación "es un tipo de" dentro del diseño. Si PremiumUser realmente es un tipo de User, puede compartir reglas de usuario y especializar solo la parte premium. Si la relación real es "usa un" o "tiene un", herencia suele acoplar demasiado y composición suele ser más clara.

Alcance y supuestos

Este paquete cubre herencia de clases a nivel conceptual: superclase, subclase, reutilización, especialización, override y delegación al constructor base. Usa TypeScript como ejemplo por claridad sintáctica, pero el razonamiento aplica a lenguajes orientados a objetos con diferencias propias.

No cubre herencia múltiple, traits, mixins, metaprogramación, jerarquías profundas, polimorfismo avanzado ni la cadena de prototipos de JavaScript en detalle. Asume que ya entiendes qué es una clase, una instancia, un método y un campo.

Modelo mental

Piensa en una plantilla de contrato laboral general y una plantilla para contrato de desarrollador senior. El contrato general ya contiene datos comunes como nombre, fecha de inicio y sueldo base. La versión de desarrollador senior reutiliza esa base y agrega condiciones específicas, como bono por liderazgo técnico o responsabilidades de mentoría.

La plantilla especializada no debería copiar todo el contrato general a mano. Debería apoyarse en la base y declarar solo la diferencia. Esa es la promesa de la herencia: compartir una definición común y expresar variaciones legítimas sin duplicar cada regla.

Pero la analogía también muestra el riesgo. Si el contrato especializado empieza a tachar media plantilla base, la relación ya no calza bien. En código pasa lo mismo: si una subclase debe desactivar o pelearse con gran parte de la superclase, probablemente la jerarquía está modelando la relación equivocada.

Uso práctico

Usa herencia cuando la subclase conserva el contrato conceptual de la superclase y solo lo especializa:

  • AdminUser extends User puede tener sentido si un admin sigue siendo un usuario válido en todos los lugares donde se espera User.
  • EmailNotification extends Notification puede tener sentido si todas las notificaciones comparten ciclo de vida y cada canal cambia solo el envío.
  • Printer extends Report no modela "es un tipo de"; una impresora usa un reporte, no es un reporte.
  • DiscountedPrice extends Money suele mezclar política comercial con valor monetario; composición puede separar mejor esas reglas.

Ejemplo trabajado: especializar una notificación por canal

Supón que una aplicación registra notificaciones en un formato común, pero cada canal sabe entregar el mensaje de una forma distinta. La clase base puede guardar el destinatario y construir el texto común. La subclase puede reutilizar eso y especializar el envío.

class Notification {
  constructor(
    protected recipient: string,
    protected message: string,
  ) {}

  preview() {
    return `${this.recipient}: ${this.message}`;
  }
}

class EmailNotification extends Notification {
  constructor(
    recipient: string,
    message: string,
    private subject: string,
  ) {
    super(recipient, message);
  }

  send() {
    return `Email "${this.subject}" -> ${this.preview()}`;
  }
}

Notification concentra lo común: destinatario, mensaje y vista previa. EmailNotification hereda esa base, delega la inicialización común con super(...) y agrega subject, que solo existe para email.

const notification = new EmailNotification(
  "felipe@example.com",
  "Tu reporte está listo",
  "Reporte semanal",
);

console.log(notification.preview());
console.log(notification.send());

La salida esperada muestra que la instancia especializada puede usar comportamiento heredado y comportamiento propio:

felipe@example.com: Tu reporte está listo
Email "Reporte semanal" -> felipe@example.com: Tu reporte está listo

La decisión clave no es si puedes escribir extends; es si la relación se mantiene verdadera cuando el programa crece. Si mañana aparece SmsNotification, probablemente puede compartir la misma base y cambiar solo el envío. Si aparece NotificationLogger, eso no debería heredar de Notification: un logger registra notificaciones, no es una notificación.

Antes de usar herencia, haz cuatro preguntas:

  1. ¿La subclase es sustituible donde se espera la superclase?
  2. ¿La subclase reutiliza la mayoría del contrato base sin desactivarlo?
  3. ¿La diferencia es una especialización estable y no una configuración temporal?
  4. ¿Composición con un objeto colaborador sería más simple?

Si la respuesta fuerte está en "es un tipo de", herencia puede ser expresiva. Si la respuesta real es "tiene un", "usa un" o "se comporta con una estrategia", composición suele reducir acoplamiento.

Evidencia

Conexiones

Requisitos

Requiere

Qué habilita

Requerido por

Relacionados y alternativas

Relacionado

Contrasta con

Ver grafo local

Fuentes citadas