El Principio de Segregación de Interfaces busca que una clase no dependa de métodos que no usa.
Llegamos a la letra I de SOLID: el Principio de Segregación de Interfaces (Interface Segregation Principle o ISP).
Si el principio anterior (LSP) trataba sobre cómo se comportan las jerarquías, este trata sobre cómo definimos los contratos entre nuestras clases.
La definición formal dice:
“Clients should not be forced to depend upon interfaces that they do not use.” (Los clientes no deberían verse obligados a depender de interfaces que no utilizan).
Dicho de forma más de “estar por casa”, es mejor tener interfaces pequeñas y específicas que una interfaz gigante que haga de todo.
A las interfaces gigantes se las conoce como “Fat Interfaces” (Interfaces obesas) o “God Interfaces”. El problema de estas interfaces es que obligan a las clases que las implementan a cargar con métodos que no necesitan, generando código basura y acoplamiento innecesario.
El ejemplo clásico: la máquina multifunción
El ejemplo más visual para entender esto es el de una oficina. Supongamos que definimos una interfaz para dispositivos de oficina. Como queremos cubrirlo todo, creamos esto:
El mal diseño
public interface IMaquinaOficina
{
void Imprimir(Documento doc);
void Escanear(Documento doc);
void EnviarFax(Documento doc);
}
Ahora llega el equipo de IT e instala una Impresora Básica (de esas viejas que solo imprimen). Al implementar la interfaz, nos encontramos con un problema:
public class ImpresoraBasica : IMaquinaOficina
{
public void Imprimir(Documento doc)
{
// Lógica de impresión real...
Console.WriteLine("Imprimiendo...");
}
public void Escanear(Documento doc)
{
// ¿Qué hacemos aquí? ¡No puedo escanear!
throw new NotImplementedException();
}
public void EnviarFax(Documento doc)
{
// ¿Fax? ¿En qué siglo estamos?
throw new NotImplementedException();
}
}
¿Ves el problema?
- Código sucio: La clase
ImpresoraBasicaestá llena de métodos que lanzan excepciones o no hacen nada. - Violación de LSP: Como vimos en el artículo anterior, si lanzamos
NotImplementedException, estamos violando el principio de Liskov. - Acoplamiento: Si cambiamos la firma del método
EnviarFaxen la interfaz, tenemos que recompilar la claseImpresoraBasica, ¡aunque ni siquiera use el fax!
Si ves una clase que implementa una interfaz y deja métodos vacíos ({ }) o lanza excepciones de “no implementado”, es una señal de alarma de que esa interfaz es demasiado grande.
Segregar la interfaz
Para cumplir con el ISP, debemos segregar (dividir) la interfaz grande en otras más pequeñas y cohesivas. Se trata de agrupar los métodos por roles.
public interface IImpresora
{
void Imprimir(Documento doc);
}
public interface IEscaner
{
void Escanear(Documento doc);
}
public interface IFax
{
void EnviarFax(Documento doc);
}
Ahora, nuestra Impresora Básica solo firma el contrato que puede cumplir.
// ✅ BUEN DISEÑO: Solo implementa lo que usa
public class ImpresoraBasica : IImpresora
{
public void Imprimir(Documento doc)
{
Console.WriteLine("Imprimiendo...");
}
}
¿Y si tenemos una fotocopiadora multifunción de última generación? Pues implementamos varias interfaces.
public class Fotocopiadora : IImpresora, IEscaner, IFax
{
public void Imprimir(Documento doc) { /* ... */ }
public void Escanear(Documento doc) { /* ... */ }
public void EnviarFax(Documento doc) { /* ... */ }
}
Esto es mucho más flexible. La fotocopiadora es una impresora, y es un escáner. Pero la impresora básica no se ve obligada a ser un escáner.
Beneficios del ISP
Al aplicar este principio, ganamos varias cosas:
- Desacoplamiento: Los cambios en una interfaz (por ejemplo,
IFax) no afectan a las clases que no la usan (ImpresoraBasica). - Legibilidad: Cuando ves que una clase implementa
IImpresora, sabes exactamente qué esperar. No tienes que adivinar qué métodos funcionan y cuáles no. - Reutilización: Es mucho más fácil reutilizar interfaces pequeñas (“Role Interfaces”) en diferentes partes de la aplicación.
En lenguajes como C# o Java, una clase puede heredar de una sola clase padre, pero puede implementar tantas interfaces como necesite.
¿Cuándo aplicar ISP?
Al igual que con el SRP (Responsabilidad Única), no hay que volverse loco y crear una interfaz para cada método (una interfaz con un solo método suele ser excesivo, a menos que sea un patrón funcional).
Aplica ISP cuando:
- Tengas una interfaz con muchos métodos y notes que las clases solo usan un subconjunto de ellos.
- Tengas clientes (otras clases) que usan esa interfaz pero solo llaman a uno o dos métodos.