concepto · fundamentos · 6 min de lectura
Composición
La composición construye comportamiento combinando objetos colaboradores, delegando responsabilidades en partes más pequeñas en vez de heredar todo desde una clase base.
Antes de leer esto, conviene conocer: Clase.
Alcance en breve
Cubre
- object composition as building behavior from collaborating parts
- delegation through fields, constructor injection, and small contracts at a conceptual level
- practical trade-offs between composition and inheritance
No cubre
- the Composite design pattern as a separate pattern
- dependency injection frameworks and containers
- functional composition and algebraic composition
Supone
- The reader understands classes, objects, methods, and basic inheritance vocabulary.
Resumen
Composición es diseñar un objeto a partir de otros objetos que colaboran con él. En vez de decir "esta clase es un tipo de otra", composición dice "esta clase tiene o usa otro objeto para hacer una parte del trabajo".
Importa porque reduce acoplamiento. Una clase puede delegar una responsabilidad específica en un colaborador y cambiar ese colaborador sin reescribir su identidad completa. Por eso suele ser una alternativa más flexible que herencia cuando la relación real es "tiene un", "usa un" o "se comporta con".
Alcance y supuestos
Este paquete cubre composición de objetos a nivel práctico: campos que guardan colaboradores, delegación de comportamiento y contratos pequeños que permiten intercambiar partes. Usa TypeScript como ejemplo, pero el razonamiento aplica a lenguajes orientados a objetos y a diseños modulares en general.
No cubre el patrón Composite como patrón estructural específico, contenedores de dependency injection, composición funcional, álgebra, traits ni mixins. Asume que ya entiendes clases, objetos, métodos y la diferencia básica entre composición y herencia.
Modelo mental
Piensa en armar un computador. El computador no hereda de una placa madre, un disco y una fuente de poder. Tiene esas partes y coordina cómo trabajan juntas. Si cambias el disco por uno más rápido, el computador sigue siendo computador; solo reemplazaste un componente.
Esa es la idea central de composición: construir comportamiento conectando piezas. Cada pieza tiene una responsabilidad acotada, y el objeto principal delega en ellas cuando necesita esa capacidad.
La ventaja aparece cuando una variación no cambia la identidad del objeto. Un carrito no es un calculador de descuentos; tiene o usa uno. Una notificación no es un transportador SMTP; usa uno. Si modelas esas relaciones con herencia, la clase queda atada a una jerarquía rígida. Con composición, puedes reemplazar el colaborador correcto.
Uso práctico
Usa composición cuando una clase necesita una capacidad, estrategia o servicio que puede variar independientemente:
- ✅
Cartpuede usar unDiscountPolicypara calcular descuentos sin heredar de cada tipo de descuento. - ✅
ReportExporterpuede usar unFormatterpara producir HTML, PDF o CSV sin convertirse en una subclase por formato. - ❌
PdfReport extends ReportExporterpuede crear una jerarquía frágil si el formato es solo una pieza intercambiable. - ❌ Componer demasiados objetos minúsculos sin una razón de variación real agrega indirección innecesaria.
Ejemplo trabajado: calcular descuentos con una política intercambiable
Supón que una tienda calcula descuentos de distintas formas. Si el carrito hereda de SeasonalDiscountCart, VipDiscountCart y luego CouponDiscountCart, la identidad del carrito se mezcla con reglas comerciales que cambian con frecuencia. La composición separa ambas cosas: el carrito guarda ítems y delega el descuento a una política.
interface DiscountPolicy {
apply(subtotal: number): number;
}
class NoDiscount implements DiscountPolicy {
apply(subtotal: number) {
return subtotal;
}
}
class PercentageDiscount implements DiscountPolicy {
constructor(private percent: number) {}
apply(subtotal: number) {
return subtotal * (1 - this.percent / 100);
}
}
class Cart {
private items: number[] = [];
constructor(private discountPolicy: DiscountPolicy) {}
addItem(price: number) {
this.items.push(price);
}
total() {
const subtotal = this.items.reduce((sum, price) => sum + price, 0);
return this.discountPolicy.apply(subtotal);
}
}
Cart no sabe qué descuento concreto está usando. Solo sabe que tiene un colaborador que cumple DiscountPolicy. La regla de descuento puede cambiar sin crear otra subclase de carrito.
const cart = new Cart(new PercentageDiscount(10));
cart.addItem(100);
cart.addItem(50);
console.log(cart.total()); // 135
El carrito suma los ítems y delega el ajuste final. Si mañana no hay descuento, puedes construir new Cart(new NoDiscount()). Si aparece un descuento por cupón, agregas otra implementación de DiscountPolicy sin cambiar el código que administra ítems.
La frontera está en la responsabilidad. Composición funciona bien cuando el colaborador representa una parte reemplazable del comportamiento. No significa partir todo en objetos por reflejo. Si una regla es estable, pequeña y pertenece completamente al objeto principal, dejarla dentro puede ser más simple.
Antes de elegir composición, revisa cuatro preguntas:
- ¿La relación real es "tiene un" o "usa un" en vez de "es un tipo de"?
- ¿Esa parte puede cambiar sin cambiar la identidad del objeto principal?
- ¿El colaborador tiene una responsabilidad clara y nombrable?
- ¿La flexibilidad ganada compensa la indirección agregada?
Si las respuestas son sí, composición suele mantener el diseño más flexible. Si la subclase realmente conserva el contrato de la superclase y solo lo especializa, herencia puede ser más directa.
Evidencia
- Refactoring.Guru, Replace Inheritance with Delegation presenta la delegación a un objeto colaborador como alternativa cuando una subclase usa solo parte de la superclase o viola la sustitución esperada.
- Microsoft Learn, F# coding conventions documenta "composition over inheritance" como una convención de diseño para favorecer composición y evitar exponer jerarquías innecesarias.
Conexiones
Requisitos
Requiere
Relacionados y alternativas
Relacionado
Contrasta con
Caminos que incluyen este nodo
Siguiente paso
Relacionado: EncapsulamientoFuentes citadas
- Refactoring.Guru, Replace Inheritance with Delegation (Práctica, 20-07-2026)
- Microsoft Learn, F# coding conventions (Oficial, 20-07-2026)