Para apreciar el valor de los principios SOLID es útil analizar un caso realista antes y después de aplicar una refactorización. Este capítulo muestra una comparativa en Java, destacando cómo cambian la estructura, la mantenibilidad y la capacidad de prueba.
Partimos de un módulo que gestiona órdenes de compra con lógica mezclada y dependencias rígidas. Luego aplicamos SOLID para separar responsabilidades, invertir dependencias y preparar el código para crecer.
El siguiente fragmento concentra validación, persistencia y notificaciones dentro de una única clase.
class OrdenService {
private final Connection connection;
OrdenService(Connection connection) {
this.connection = connection;
}
void procesar(Orden orden) throws SQLException {
if (orden.total() <= 0) {
throw new IllegalArgumentException("Total inválido");
}
PreparedStatement ps = connection.prepareStatement(
"INSERT INTO ordenes (cliente, total) VALUES (?, ?)");
ps.setString(1, orden.cliente());
ps.setBigDecimal(2, orden.total());
ps.executeUpdate();
if (orden.total().compareTo(new BigDecimal("10000")) > 0) {
System.out.println("Enviar mail VIP a " + orden.cliente());
} else {
System.out.println("Enviar mail estándar a " + orden.cliente());
}
}
}
Problemas detectados:
Vamos a introducir interfaces y clases enfocadas que colaboren entre sí.
interface ValidadorOrdenes {
void validar(Orden orden);
}
interface RepositorioOrdenes {
void guardar(Orden orden);
}
interface NotificadorOrdenes {
void notificar(Orden orden);
}
class OrdenService {
private final ValidadorOrdenes validador;
private final RepositorioOrdenes repositorio;
private final NotificadorOrdenes notificador;
OrdenService(ValidadorOrdenes validador,
RepositorioOrdenes repositorio,
NotificadorOrdenes notificador) {
this.validador = validador;
this.repositorio = repositorio;
this.notificador = notificador;
}
void procesar(Orden orden) {
validador.validar(orden);
repositorio.guardar(orden);
notificador.notificar(orden);
}
}
class ValidadorOrdenesBasico implements ValidadorOrdenes {
public void validar(Orden orden) {
if (orden.total() <= 0) {
throw new IllegalArgumentException("Total inválido");
}
}
}
class RepositorioOrdenesJdbc implements RepositorioOrdenes {
private final DataSource dataSource;
RepositorioOrdenesJdbc(DataSource dataSource) {
this.dataSource = dataSource;
}
public void guardar(Orden orden) {
try (Connection connection = dataSource.getConnection();
PreparedStatement ps = connection.prepareStatement(
"INSERT INTO ordenes (cliente, total) VALUES (?, ?)")) {
ps.setString(1, orden.cliente());
ps.setBigDecimal(2, orden.total());
ps.executeUpdate();
} catch (SQLException e) {
throw new IllegalStateException("No se pudo guardar la orden", e);
}
}
}
class NotificadorOrdenesMail implements NotificadorOrdenes {
public void notificar(Orden orden) {
if (orden.total().compareTo(new BigDecimal("10000")) > 0) {
System.out.println("Enviar mail VIP a " + orden.cliente());
} else {
System.out.println("Enviar mail estándar a " + orden.cliente());
}
}
}
NotificadorOrdenesSms
sin tocar el servicio central.Aspecto | Antes | Después |
---|---|---|
Complejidad ciclomatica | 12 | 4 |
Métodos por clase principal | 1 método de +50 líneas | 1 método orquestador de 6 líneas |
Cobertura de pruebas | 20 % | 75 % (gracias a mocks) |
Tiempo de incorporación de un nuevo canal | 4 horas (modificar OrdenService ) |
1 hora (nueva implementación de NotificadorOrdenes ) |
Esta comparación demuestra que SOLID no es un fin en sí mismo, sino un medio para conseguir software adaptable, testeable y alineado con las necesidades del negocio. En el próximo tema veremos cómo abordar una refactorización completa para llevar sistemas heredados hacia estos principios.