El Principio de Segregación de Interfaces (ISP, Interface Segregation Principle) plantea que nadie debería verse obligado a depender de métodos que no utiliza. Propone diseñar interfaces pequeñas, específicas para cada necesidad, en lugar de interfaces monolíticas que obligan a implementar comportamientos innecesarios.
Al aplicar ISP, las dependencias entre componentes se vuelven más claras y manejables. Esto reduce el acoplamiento, facilita las pruebas y evita que un cambio menor en una funcionalidad afecte a todas las implementaciones de una interfaz enorme.
ISP fue formalizado por Robert C. Martin a partir de experiencias en sistemas donde una sola interfaz representaba todas las operaciones del dominio. Esa práctica generaba implementaciones llenas de métodos sin uso, complicaba las pruebas y rompía el principio de responsabilidad única.
UnsupportedOperationException
: señal de que la interfaz exige operaciones irrelevantes.Imaginemos una interfaz para dispositivos multifunción que combinan impresión, escaneo y fax. Algunos dispositivos no soportan todas las funciones, pero la interfaz los obliga a implementarlas igual.
interface DispositivoMultifuncion {
void imprimir(String documento);
void escanear(String destino);
void enviarFax(String numero);
}
class ImpresoraBasica implements DispositivoMultifuncion {
public void imprimir(String documento) {
System.out.println("Imprimiendo " + documento);
}
public void escanear(String destino) {
throw new UnsupportedOperationException("Esta impresora no escanea");
}
public void enviarFax(String numero) {
throw new UnsupportedOperationException("Esta impresora no envía fax");
}
}
La clase ImpresoraBasica
viola ISP porque depende de métodos que no necesita. Las excepciones indican que la interfaz está mal segregada.
Dividimos la interfaz monolítica en contratos especializados para cada capacidad. Cada dispositivo implementa solo los métodos que realmente ofrece.
interface Impresora {
void imprimir(String documento);
}
interface Escaner {
void escanear(String destino);
}
interface Fax {
void enviarFax(String numero);
}
class ImpresoraBasica implements Impresora {
public void imprimir(String documento) {
System.out.println("Imprimiendo " + documento);
}
}
class CentroDeCopias implements Impresora, Escaner, Fax {
public void imprimir(String documento) { /* ... */ }
public void escanear(String destino) { /* ... */ }
public void enviarFax(String numero) { /* ... */ }
}
El código resultante cumple el principio: las clases dependen solo de lo que utilizan, y cada interfaz representa una capacidad concreta.
En sistemas existentes es común encontrar interfaces grandes. Una estrategia gradual consiste en crear adaptadores que implementen la interfaz antigua y deleguen en nuevas interfaces pequeñas. Así se evita romper el código cliente mientras se refactoriza.
Supongamos una plataforma que expone un API para gestionar pedidos, pagos y envíos. Si todas las operaciones se agrupan en una sola interfaz, los consumidores que solo procesan pagos terminan viendo métodos de envíos innecesarios.
interface ApiComercio {
Pedido crearPedido(Pedido pedido);
void confirmarPago(Pago pago);
void despacharEnvio(Envio envio);
}
class ProcesadorPagos implements ApiComercio {
public Pedido crearPedido(Pedido pedido) {
throw new UnsupportedOperationException("No crea pedidos");
}
public void confirmarPago(Pago pago) {
// procesamiento del pago
}
public void despacharEnvio(Envio envio) {
throw new UnsupportedOperationException("No gestiona envíos");
}
}
Separar el API en interfaces especializadas permite que cada consumidor implemente o dependa solo de lo necesario.
interface ServicioPedidos {
Pedido crearPedido(Pedido pedido);
}
interface ServicioPagos {
void confirmarPago(Pago pago);
}
interface ServicioEnvios {
void despacharEnvio(Envio envio);
}
class ProcesadorPagos implements ServicioPagos {
public void confirmarPago(Pago pago) {
// procesamiento del pago
}
}
De esta manera se reduce el acoplamiento y se cumple ISP tanto del lado del servidor como del cliente.
El Principio de Segregación de Interfaces evita interfaces infladas, mejora el acoplamiento y prepara el terreno para que el Principio de Inversión de Dependencias funcione correctamente. En el próximo tema abordaremos cómo elevar la abstracción para desacoplar módulos completos.