Métricas de Software¶
1. ¿Qué son las Métricas de Software?¶
Las métricas de software son herramientas cuantitativas para:
- Medir la calidad del software
- Mejorar el código y procesos
- Evaluar objetivamente (no opiniones)
- Tomar decisiones basadas en datos
Sin métricas, la calidad es subjetiva y difícil de controlar. Con métricas, puedes saber exactamente dónde está el problema.
2. Acoplamiento¶
2.1 ¿Qué es el Acoplamiento?¶
El acoplamiento se refiere a cuán dependientes son los módulos/componentes entre sí.
- Bajo acoplamiento: Los módulos son independientes
- Alto acoplamiento: Los módulos dependen mucho los unos de los otros
2.2 ¿Por qué es importante?¶
Con bajo acoplamiento:
-
- Puedes cambiar un módulo sin afectar otros
-
- El código es más fácil de probar
-
- Reutilización de código más fácil
-
- Mantenimiento simplificado
Con alto acoplamiento:
- -Cambiar un módulo rompe otros
- -Difícil de probar aisladamente
- -Código frágil
- Difícil de mantener
2.3 Ejemplo de Acoplamiento¶
Alto acoplamiento (Malo):
public class Carrito {
private BaseDatos bd = new BaseDatos();
private Email email = new Email();
private Impuesto impuesto = new Impuesto();
public void procesoCompra(Producto p) {
// El carrito DEPENDE de 3 clases diferentes
bd.guardar(p);
email.enviar("Compra realizada");
impuesto.calcular(p.getPrecio());
}
}
// Si cambio Email, debo cambiar también Carrito
// Si cambio BaseDatos, debo cambiar también Carrito
Bajo acoplamiento (Bueno):
public class Carrito {
private ProcesadorCompra procesador;
// Inyección de dependencias - el carrito NO necesita saber
// qué clases concretas usa el procesador
public Carrito(ProcesadorCompra p) {
this.procesador = p;
}
public void procesoCompra(Producto p) {
procesador.procesar(p);
}
}
// Si cambio Email o BaseDatos, Carrito no se ve afectado
3. Cohesión¶
3.1 ¿Qué es la Cohesión?¶
La cohesión se refiere a cuán relacionadas están las tareas dentro de un módulo.
- Alta cohesión: Todos los métodos del módulo trabajan juntos para un propósito claro
- Baja cohesión: Los métodos hacen cosas no relacionadas
3.2 ¿Por qué es importante?¶
Con alta cohesión:
- El módulo tiene una responsabilidad clara
- Código más comprensible
- Más fácil de mantener
- Menos propenso a errores
Con baja cohesión:
- El módulo hace demasiadas cosas
- Difícil de entender
- Difícil de reutilizar
- Propenso a errores
3.3 Ejemplo de Cohesión¶
Baja cohesión (Malo):
public class GestorTodo {
// Cosas no relacionadas
public void procesarPago() { /* ... */ }
public void enviarEmail() { /* ... */ }
public void generarReporte() { /* ... */ }
public void calcularImpuestos() { /* ... */ }
public void conectarBaseDatos() { /* ... */ }
}
// ¿Qué es realmente la responsabilidad de esta clase?
// Hace 5 cosas completamente diferentes
Alta cohesión (Bueno):
public class ProcesadorPago {
// Una responsabilidad clara: procesar pagos
public void validarTarjeta(Tarjeta t) { /* ... */ }
public void procesarTransaccion(Pago p) { /* ... */ }
public void verificarFondos(double cantidad) { /* ... */ }
}
public class NotificadorEmail {
// Una responsabilidad clara: enviar emails
public void enviarConfirmacion(Usuario u) { /* ... */ }
public void enviarRecuperacionPassword(Usuario u) { /* ... */ }
}
4. Complejidad Ciclomática¶
4.1 ¿Qué es la Complejidad Ciclomática?¶
La complejidad ciclomática es una métrica que mide cuántos caminos diferentes puede tomar la ejecución de un método.
Desarrollada por Thomas J. McCabe en 1976.
Fórmula: M = E - N + 2P
Donde: - E = número de aristas (conexiones) en el grafo de flujo - N = número de nodos (bloques de código) en el grafo de flujo - P = número de componentes conectados (normalmente 1)
Para propósitos prácticos: Cada estructura de control (if, while, for, switch) suma 1.
4.2 Interpretación¶
Complejidad Ciclomática Significado
═════════════════════════════════════════════
1-3 Muy simple - Fácil de mantener
4-7 Moderado - Todavía manejable
8-10 Complejo - Difícil de mantener
>10 Muy complejo - Refactorizar necesario
4.3 Ejemplo Práctico¶
Ejemplo 1: Complejidad 1 (Simple)¶
public int maximo(int a, int b) {
return a > b ? a : b;
}
// Complejidad = 1 (no hay ramificaciones)
// Un único camino de ejecución
Ejemplo 2: Complejidad 2 (Bajo)¶
public int maximo(int a, int b) {
if (a > b) { // +1 por el if
return a;
} else {
return b;
}
}
// Complejidad = 2
// Dos caminos: if verdadero, if falso
Ejemplo 3: Complejidad 4 (Moderado)¶
public void procesarPedido(Pedido p) {
if (p != null) { // +1
if (p.tieneProductos()) { // +1
if (p.montototal() > 0) { // +1
if (cliente.tieneCredito()) { // +1
guardar(p);
}
}
}
}
}
// Complejidad = 5 (4 ifs + 1 base)
// 5 = 2 - 1 + 2 * 1 - 1 = ... mejor contar los if's
Ejemplo 4: Complejidad Alta (MALO - Necesita Refactorización)¶
public String validarUsuario(String email, String password, boolean premium) {
String resultado = "";
if (email == null) { // +1
resultado = "Email inválido";
} else if (!email.contains("@")) { // +1
resultado = "Email sin @";
} else if (password.length() < 8) { // +1
resultado = "Password muy corta";
} else if (!estaRegistrado(email)) { // +1
resultado = "Usuario no existe";
} else if (!verificarPassword(email, password)) { // +1
resultado = "Password incorreta";
} else if (premium && !verificarSuscripcion()) { // +1
resultado = "Suscripción vencida";
} else if (estaBaneado(email)) { // +1
resultado = "Usuario baneado";
} else {
resultado = "OK";
}
return resultado;
}
// Complejidad = 7 (alto - deberían refactorizar)
// 7 caminos posibles de ejecución
// Muy difícil de probar todas las combinaciones
4.4 ¿Cómo Reducir la Complejidad Ciclomática?¶
Opción 1: Extraer métodos
// Refactorizado - más legible y testeable
public String validarUsuario(String email, String password, boolean premium) {
if (!esEmailValido(email)) {
return "Email inválido";
}
if (!esPasswordValida(password)) {
return "Password inválida";
}
if (!esUsuarioValido(email)) {
return "Usuario inválido";
}
if (premium && !esSubscripcionValida()) {
return "Suscripción vencida";
}
return "OK";
}
// Complejidad de validarUsuario = 5 (mejorado)
// Complejidad de cada método individual = 1-2 (fácil)
Opción 2: Usar patrones como Strategy
public String validarUsuario(Usuario u, List<Validador> validadores) {
for (Validador v : validadores) {
String error = v.validar(u);
if (error != null) return error;
}
return "OK";
}
// Complejidad baja, fácil agregar nuevos validadores
4.5 ¿Por Qué Importa?¶
Código con complejidad ciclomática baja: - Más fácil de entender - Más fácil de probar (menos casos de test) - Menos bugs - Más fácil de mantener
5. Relación entre las Tres Métricas¶
BAJO ACOPLAMIENTO + ALTA COHESIÓN + BAJA COMPLEJIDAD CICLOMÁTICA = CÓDIGO DE CALIDAD
┌──────────────────────────────────────────────────────────┐
│ CÓDIGO DE CALIDAD │
├──────────────────────────────────────────────────────────┤
│ ✓ Bajo acoplamiento │
│ → Módulos independientes │
│ → Cambios localizados │
│ │
│ ✓ Alta cohesión │
│ → Una responsabilidad clara por módulo │
│ → Código comprensible │
│ │
│ ✓ Baja complejidad ciclomática │
│ → Métodos simples y claros │
│ → Fáciles de probar y mantener │
└──────────────────────────────────────────────────────────┘
Actividades¶
-
AC905. Se te proporciona el siguiente código:
public class GestorPedidos { public void procesarPedido(Pedido pedido) { if (pedido == null) { System.out.println("Pedido nulo"); return; } if (!pedido.tieneProductos()) { System.out.println("Pedido sin productos"); return; } double total = pedido.calcularTotal(); if (total > 1000) { System.out.println("Descuento 10%"); total = total * 0.9; } else if (total > 500) { System.out.println("Descuento 5%"); total = total * 0.95; } else if (total > 100) { System.out.println("Descuento 2%"); total = total * 0.98; } if (esClienteVIP(pedido.getCliente())) { total = total * 0.95; System.out.println("Descuento VIP aplicado"); } if (pedido.requiereEnvio()) { double envio = calcularEnvio(total); total = total + envio; if (total > 2000) { total = total - 100; System.out.println("Envío gratis por monto alto"); } } System.out.println("Total final: " + total); } }Tu tarea:
- Calcula la complejidad ciclomática (cuenta todos los puntos de ramificación)
- Identifica si es demasiado complejo
- Refactoriza el código para reducir la complejidad extrayendo métodos
- Calcula la nueva complejidad
- Explica qué mejoras de mantenibilidad logras
Entrega: Código refactorizado comentado + documento con tu análisis.
-
AC906. Revisa un proyecto de código que hayas hecho (puede ser de otra asignatura).
Elige una clase y analiza:
-
Acoplamiento: - ¿Cuántas clases depende? - ¿Son dependencias reales o podrían reducirse? - ¿Usa inyección de dependencias o instancia directamente?
-
Cohesión: - ¿Cuál es la responsabilidad principal de la clase? - ¿Todos los métodos contribuyen a esa responsabilidad? - ¿Hay métodos que hacen cosas no relacionadas?
-
Mejoras: - Propone cómo reducir el acoplamiento - Propone cómo mejorar la cohesión - Refactoriza la clase aplicando tus mejoras
Entrega: Código original + código refactorizado + documento explicando cambios.
-