Tema 9 · Composición & Mixins

Composición sobre Herencia y Patrón Mixins con Object.assign()

Aprende por qué la composición suele ser superior a jerarquías complejas de herencia y cómo inyectar habilidades modulares con Mixins y Object.assign().

El Problema de la Herencia Rígida

El famoso científico de la computación Joe Armstrong (creador de Erlang) resumió el problema de las jerarquías profundas de herencia con una frase célebre:

"El problema con los lenguajes orientados a objetos tradicionales es todo ese entorno implícito que arrastran consigo. Tú solo querías un plátano, pero terminaste con un gorila sosteniendo el plátano y toda la selva entera detrás."

Si creas una clase Ave con el método volar(), ¿qué haces cuando debes modelar un Pingüino o un Avestruz (aves que no vuelan)? ¿O un Murciélago (mamífero que sí vuela)? Forzar todo en un árbol de herencia genera código roto.

La Máxima: "Favorece la Composición sobre la Herencia"

La Composición cambia la pregunta fundamental:

  • En lugar de preguntarte qué ES tu objeto (is-a: un pato es un ave).
  • Pregúntate qué HACE tu objeto (can-do: este objeto sabe nadar, sabe volar y sabe emitir sonido).

Patrón Mixin en JavaScript con `Object.assign()`

Un Mixin es un objeto que encapsula una habilidad o comportamiento concreto. Puedes mezclar múltiples Mixins en cualquier clase o prototipo utilizando Object.assign(Destino, ...Orígenes):

const volador = { volar() { return "Volando por los cielos..."; } };
const nadador = { nadar() { return "Nadando bajo el agua..."; } };

// ¡Inyección horizontal de habilidades!
Object.assign(Pato.prototype, volador, nadador);

⚡ Creación de Personajes de Videojuego Modulares con Mixins

Ejecuta el código en el Sandbox y observa cómo los personajes componen habilidades sin jerarquías rígidas:

MixinsDemo.js
Solo lectura
// 1. Catálogo de Habilidades Modulares (Mixins)
const conAtaqueEspada = {
    atacarConEspada() {
        return `⚔️ ${this.nombre} ataca con su espada causando 45 puntos de daño.`;
    }
};

const conLanzamientoHechizo = {
    lanzarFuego() {
        return `🔥 ${this.nombre} lanza una bola de fuego mágica causando 80 puntos de daño.`;
    }
};

const conCuracion = {
    curarAliado(aliado) {
        return `💚 ${this.nombre} cura las heridas de ${aliado.nombre} restaurando 50 HP.`;
    }
};

const conSigilo = {
    ocultarse() {
        return `👥 ${this.nombre} se desvanece en las sombras haciéndose invisible.`;
    }
};

// 2. Clase Base Sencilla
class Personaje {
    constructor(nombre, nivel = 1) {
        this.nombre = nombre;
        this.nivel = nivel;
        this.vida = 100;
    }

    describir() {
        return `[Nivel ${this.nivel}] ${this.nombre} (HP: ${this.vida}/100)`;
    }
}

// 3. Composición de Clases Especializadas con Object.assign
// Un Guerrero solo ataca con espada
class Guerrero extends Personaje {}
Object.assign(Guerrero.prototype, conAtaqueEspada);

// Un Mago lanza hechizos y cura
class MagoBlanco extends Personaje {}
Object.assign(MagoBlanco.prototype, conLanzamientoHechizo, conCuracion);

// Un Paladín es híbrido: Ataca con espada Y sabe curar
class Paladin extends Personaje {}
Object.assign(Paladin.prototype, conAtaqueEspada, conCuracion);

// Un Asesino ataca con espada y tiene sigilo
class Asesino extends Personaje {}
Object.assign(Asesino.prototype, conAtaqueEspada, conSigilo);

// ==========================================
// PRUEBA EN EL SANDBOX
// ==========================================
const aragorn = new Guerrero("Aragorn", 10);
const gandalf = new MagoBlanco("Gandalf", 20);
const uther   = new Paladin("Uther", 15);

console.log("=== DESCRIPCIÓN DE HÉROES ===");
console.log(aragorn.describir());
console.log(gandalf.describir());
console.log(uther.describir());

console.log("\n=== COMBATE MODULAR (HABILIDADES COMPUESTAS) ===");
console.log(aragorn.atacarConEspada());
console.log(gandalf.lanzarFuego());
console.log(gandalf.curarAliado(aragorn));

// El Paladín disfruta de ambas habilidades sin duplicar código
console.log("\n=== HABILIDADES DEL PALADÍN HÍBRIDO ===");
console.log(uther.atacarConEspada());
console.log(uther.curarAliado(gandalf));
Pruébalo tú mismo » Se abre en una nueva pestaña con ejecución en vivo
🧠
Mini-Quiz de Retención
Comprueba tu comprensión antes de pasar a la siguiente lección
1 Pregunta
Pregunta 1
¿Por qué la regla arquitectónica "Favorece la composición sobre la herencia" es tan recomendada en JavaScript?
🦉
¿Tienes dudas con este tema?
Sócrates AI puede darte analogías y explicarte este código paso a paso.

🚀 ¿Quieres dominar Manual de Programación Orientada a Objetos en JavaScript (POO) y crear proyectos reales?

Aprende arquitectura limpia, desarrollo web moderno, buenas prácticas y despliegue en producción con nuestro Bootcamp Fullstack Developer con mentorías en vivo.

🏛️ Autoría y Propiedad Intelectual: Obra educativa original de David Amador para Kilopolis Academia (Solucionáticos), estructurada sobre las especificaciones y estándares abiertos del W3C, el estándar vivo de WHATWG y la documentación oficial de código abierto. © Todos los derechos reservados.