← dev-notes
POO · PARTE 2 DE 2

Composición, Cohesión
y Acoplamiento

Cómo ensamblar objetos bien — las decisiones que más impactan en mantenibilidad

POO · P2
🧩 Composición vs Herencia 🔗 Cohesión 🔓 Acoplamiento 🗣 Tell, Don't Ask 🚶 Law of Demeter
01 Composición vs Herencia — la decisión de diseño más importante
FAVOR COMPOSITION OVER INHERITANCE · GoF
CONCEPTO 01
🧩 Composición sobre Herencia
Los GoF lo enunciaron en 1994: "Prefer object composition over class inheritance". La herencia crea acoplamiento rígido en tiempo de compilación. La composición crea flexibilidad en tiempo de ejecución — puedes cambiar el comportamiento intercambiando la parte compuesta sin tocar la clase contenedora.
HAS-A en lugar de IS-A inyección de dependencias Strategy pattern
herencia para reutilizar — acoplamiento rígido❌ HERENCIA INAPROPIADA
// OrderService hereda logging "gratis"
class OrderService extends BaseLoggingService {
  void placeOrder(Order order) {
    log("Placing: " + order.getId()); // heredado
    /* lógica de negocio */
  }
}
// OrderService NO ES-UN BaseLoggingService
// Acoplado a la jerarquía para siempre
// Cambiar el logger → modificar herencia
// No puedes tener dos loggers distintos
composición — flexible e intercambiable✅ COMPOSICIÓN
class OrderService {  // no extiende nada
  private final Logger logger;      // TIENE-UN
  private final OrderRepository repo;

  OrderService(Logger logger,
               OrderRepository repo) {
    this.logger = logger;
    this.repo = repo;
  }

  void placeOrder(Order order) {
    logger.info("Placing: " + order.getId());
    /* lógica de negocio */
  }
  // Logger es intercambiable en tests y prod
}
Regla práctica: Si puedes decir "A ES-UN B" con convicción semántica → herencia. Si dices "A USA-UN B" o "A TIENE-UN B" → composición. En caso de duda, siempre composición.
02 Cohesión — qué pertenece junto
LCOM · SINGLE RESPONSIBILITY · RAZÓN DE CAMBIO
CONCEPTO 02
🔗 Cohesión
Medida de cuánto los elementos de una clase (campos + métodos) están relacionados entre sí. Alta cohesión = una clase, una responsabilidad clara. La prueba de Fowler: si al describir la clase necesitas la palabra "y", tiene baja cohesión. SRP es la formalización de cohesión en SOLID.
alta cohesión una razón de cambio SRP
baja cohesión — clase que hace de todo❌ SRP ROTO
// UserManager hace demasiado
class UserManager {
  void createUser(User u) { /* DB */ }
  void sendWelcomeEmail(User u) { /* SMTP */ }
  void generatePDFReport() { /* PDF */ }
  void connectToDatabase() { /* JDBC */ }
  void validateTaxId(String rut) { /* regex */ }
}
// Cambia el PDF → toca UserManager
// Cambia el SMTP → toca UserManager
// 5 razones de cambio → baja cohesión
alta cohesión — cada clase, una razón✅ SRP CORRECTO
class UserRepository {
  void save(User u) { /* solo DB */ }
}
class WelcomeEmailSender {
  void send(User u) { /* solo SMTP */ }
}
class UserReportGenerator {
  void generate() { /* solo PDF */ }
}
// Cada clase cambia por una sola razón
// Fácil de testear de forma aislada
// Fácil de reutilizar en otros contextos
03 Acoplamiento — qué depende de qué
TIGHT vs LOOSE COUPLING · DIP · INYECCIÓN DE DEPENDENCIAS
CONCEPTO 03
🔓 Acoplamiento
Grado en que una clase depende de los detalles internos de otras. Alto acoplamiento = un cambio en A rompe B. El objetivo no es cero acoplamiento (imposible) sino acoplamiento a abstracciones estables, no a implementaciones volátiles. DIP es la formalización de esto.
depender de interfaces inyección de dependencias DIP
acoplamiento fuerte — depende del concreto❌ DIP ROTO
class OrderService {
  // instancia directa = acoplamiento máximo
  private MySQLOrderRepository repo =
    new MySQLOrderRepository();

  void place(Order o) { repo.save(o); }
}
// Cambiar a PostgreSQL → modificar aquí
// Imposible de testear con doble de prueba
// Viola DIP: depende de un detalle volátil
acoplamiento débil — depende de la abstracción✅ DIP CORRECTO
class OrderService {
  private final OrderRepository repo; // interfaz

  // dependencia inyectada desde fuera
  OrderService(OrderRepository repo) {
    this.repo = repo;
  }

  void place(Order o) { repo.save(o); }
}
// MySQL, PostgreSQL, InMemory → mismo código
// Tests: inyectar FakeOrderRepository
Alta cohesión + bajo acoplamiento es el objetivo de diseño OO. Son las dos caras de la misma moneda: una clase cohesionada tiene pocos motivos para depender de muchas cosas; una clase poco acoplada es fácil de mantener cohesionada.
04 Tell, Don't Ask y Law of Demeter
PRINCIPIOS PRÁCTICOS DE DISEÑO OO
PRINCIPIO 01
🗣 Tell, Don't Ask
Dile a los objetos qué hacer, no les preguntes su estado para decidir tú. Si preguntas el estado de un objeto para tomar una decisión en su nombre, estás violando el encapsulamiento: la lógica que pertenece al objeto se está escapando al llamador.
Ask — decisión fuera del objeto
// Le pregunto el estado y decido yo
if (account.getBalance() > amount) {
  account.setBalance(
    account.getBalance() - amount);
}
// Lógica de negocio fuera del objeto
Tell — el objeto decide
// Le digo qué hacer, él decide cómo
account.withdraw(amount);

// BankAccount.withdraw() contiene
// la regla de negocio — donde pertenece
PRINCIPIO 02
🚶 Law of Demeter
"No hables con extraños." Un método solo debe llamar a métodos de: sí mismo, sus parámetros directos, objetos que creó, sus campos directos. Las cadenas de llamadas (train wreck) acoplan al llamador con toda la jerarquía interna.
train wreck — viola Demeter
// Conoce Order, Customer, Address, City
double tax = order
  .getCustomer()
  .getAddress()
  .getCity()
  .getTaxRate();
// Cambiar Address → rompe aquí
delegación — solo habla con vecinos
// Order sabe cómo obtener su tasa
double tax = order.getTaxRate();

// Cambios internos en Address
// no afectan a este código
// Menos acoplamiento → más mantenible
05 Métricas de diseño — cómo medir la calidad OO
Métrica
Mide
Señal de problema
Remedio
LCOM
Falta de cohesión en métodos
Clase grande donde los métodos no comparten campos
Extract Class
CBO
Acoplamiento entre objetos
Clase depende de demasiadas otras clases concretas
DIP + interfaces
DIT
Profundidad del árbol de herencia
Jerarquía de más de 3-4 niveles
Composición
RFC
Respuesta de la clase (métodos alcanzables)
Clase con demasiadas responsabilidades externas
SRP
WMC
Complejidad ciclomática total
Demasiados métodos o métodos muy complejos
Extract Method
🗺
La cadena completa — de POO a arquitectura
POO (pilares) → define los mecanismos básicos. Cohesión y acoplamiento → miden la calidad del diseño. SOLID → formaliza las reglas para lograr alta cohesión y bajo acoplamiento. GoF Patterns → soluciones probadas para problemas recurrentes de diseño OO. DDD → aplica todos estos principios al modelado del dominio. Hexagonal / Clean Architecture → organiza todo en capas con dependencias controladas. Es la misma idea en escalas distintas.