Command Palette

Search for a command to run...

ES·EN

Nivel 2 · 20 min

Composición vs Herencia

''Favorecé composición sobre herencia'' — principio de Effective Java. La composición (tener una referencia a otro objeto) es más flexible que la herencia (ser un subtipo de otro objeto). No es que la herencia sea mala — es que la composición resuelve muchos casos mejor y con menos acoplamiento.

Los problemas de la herencia

La herencia es inflexible — la relación se define en compile-time y no puede cambiar en runtime. El Fragile Base Class problem: cambios en la superclase pueden romper subclases de formas inesperadas. La herencia expone los internos de la superclase a las subclases — viola encapsulación. La herencia de implementación crea acoplamiento estrecho entre superclase y subclases.

Composición y sus ventajas

Con composición, un objeto contiene una referencia a otro y delega operaciones en él. Las ventajas: podés cambiar el comportamiento en runtime (estrategia inyectada), la clase que compone no conoce los internos de la clase compuesta (encapsulación respetada), podés combinar múltiples comportamientos (múltiples campos de estrategia), y los cambios en el objeto compuesto no rompen al que compone.

Decorator como composición

El patrón Decorator es composición en acción: envolvés un objeto con otro que implementa la misma interfaz y delega, agregando comportamiento. Podés apilar Decorators para combinar comportamientos. Java I/O lo usa extensivamente: FileInputStream → BufferedInputStream → GZIPInputStream. Cada capa agrega funcionalidad sin subclasificar.

Puntos clave

  • Usá herencia cuando la relación es genuinamente ''es-un'' y la subclase es realmente una especialización de la superclase.
  • Usá composición cuando querés reutilizar comportamiento sin crear una relación de subtipo — ''tiene-un'' es más flexible que ''es-un''.
  • Si encontrás que subclasificás solo para reutilizar código (no para ser un subtipo), es señal de cambiar a composición.

Code example

// Composicion: Logger flexible
class OrderService {
  private final OrderRepository repo;
  private final EventPublisher events;
  private final MetricsCollector metrics; // compuesto, no heredado
  
  void createOrder(Order o) {
    repo.save(o);
    events.publish(new OrderCreated(o));
    metrics.increment("orders.created");
  }
}