SOLID: Principio de Segregación de Interfaces
Victor Manuel Pinzon
Posted on May 24, 2021
Este principio fue desarrollado por Robert C. Martin mientras trabajaba como consultor en Xerox. Durante su periodo laboral, Martin estuvo a cargo del desarrollo de un nuevo sistema de gestión de impresiones, que permitía realizar diferentes acciones concurrentes en una nueva línea de impresoras.
A lo largo del desarrollo notó que pequeñas modificaciones al sistema terminaban en largos despliegues a producción, en muchos casos, en despliegues de módulos fuera del alcance de los cambios. El origen del problema era una interfaz sobrecargada con diferentes firmas de métodos que era utilizada en todas las acciones concurrentes que desempeñaba el sistema. Es decir, si Martin realizaba modificaciones en los módulos de impresión, los cambios se propagaban a los otros módulos, como el de faxeo, escaneo, etc.
La solución dada por Robert C. Martin a este problema, es lo que hoy conocemos como el Principio de Segregación de Interfaces.
La definición del principio es la siguiente:
Ningún cliente debe de ser forzado a depender de una interfaz que no utiliza.
El principio es bien sencillo, ninguna clase debe de ser forzada a implementar métodos de interfaces que no utiliza. El objetivo, al igual que el principio de responsabilidad única, es limitar el alcance de los cambios a futuro.
Violación al Principio de Segregación de Interfaces
Supongamos que eres el desarrollador a cargo del sistema de gestión de la nueva línea de impresoras multifuncionales de Xerox. Este nuevo sistema se desarrollará desde cero, por lo tanto estarás a cargo de los futuros desarrollos de las diferentes impresoras que se originen de la nueva línea de multifuncionales.
Esta nueva línea cuenta con las siguientes capacidades:
- Impresión.
- Escaneo.
- Faxeo.
public interface MultifuncionalLX {
/**
* Método encargado de imprimir el siguiente documento
* activo en la cola de impresión.
*
* @return Retorna boolean indicando el resultado.
*/
public boolean imprimirDocumento();
/**
* Método encargado de escanear el documento cargado
* en la bandeja de escaneo.
*
* @return Retorna boolean indicando el resultado.
*/
public boolean escanearDocumento();
/**
* Método encargado de faxear el documento cargado
* en la bandeja de faxeo.
*
* @return Retorna boolean indicando el resultado.
*/
public boolean faxearDocumento();
}
Para llevar a cabo el desarrollo se define la siguiente interfaz. La cual establece los métodos básicos que tendrán las multifuncionales de la nueva línea.
public class ImpresoraLX8578 implements MultifuncionalLX {
@Override
public boolean imprimirDocumento() {
//Lógica de impresión de documentos
return true;
}
@Override
public boolean escanearDocumento() {
//Lógica de escaneo de documentos
return true;
}
@Override
public boolean faxearDocumento() {
//Lógica de faxeo de documentos
return true;
}
}
Para facilitar la compresión del ejemplo, se omitió la implementación especifica de cada método.
Durante varios meses se lanzan diferentes versiones de la multifuncional, las cuales utilizan la misma interfaz para definir sus funcionalidades principales.
Gracias a la aceptación de la nueva línea de impresoras. Gerencia, te solicita la integración de l sistema de gestión de impresiones a una nueva impresora económica, que será lanzada bajo la línea de impresoras que esta bajo tu responsabilidad. La diferencia es que esta nueva impresora no contará con la capacidad de enviar faxes, únicamente impresión y escaneo.
Tomando en cuenta que esta nueva impresora pertenece a la misma línea que las impresoras anteriores y también es categorizada como multifuncional, decides utilizar la interfaz definida para dicha línea.
public class ImpresoraLX8590 implements MultifuncionalLX {
@Override
public boolean imprimirDocumento() {
//Lógica de impresión de documentos
return true;
}
@Override
public boolean escanearDocumento() {
//Lógica de escaneo de documentos
return true;
}
@Override
public boolean faxearDocumento() {
throw new UnsupportedOperationException("Impresora no cuenta con la capacidad de faxeo.");
}
}
En este punto es donde se da la violación al Principio de Segregación de Interfaces, debido a que la impresora ImpresoraLX8590 es forzada a depender de métodos que no utiliza.
Implementación del Principio de Segregación de Interfaces
La solución al ejemplo anterior, es la segregación de la interfaz que agrupa todos los métodos en diferentes interfaces especializadas. Por lo tanto, la interfaz MultifuncionalLX se convertirá en las interfaces:
- MultifuncionalLXImpresion
- MultifuncionalLXEscaneo
- MultifuncionalLXFaxeo
public interface MultifuncionalLXEscaneo {
/**
* Método encargado de escanear el documento cargado
* en la bandeja de escaneo.
*
* @return Retorna boolean indicando el resultado.
*/
public boolean escanearDocumento();
}
public interface MultifuncionalLXFaxeo {
/**
* Método encargado de faxear el documento cargado
* en la bandeja de faxeo.
*
* @return Retorna boolean indicando el resultado.
*/
public boolean faxearDocumento();
}
public interface MultifuncionalLXImpresion {
/**
* Método encargado de imprimir el siguiente documento
* activo en la cola de impresión.
*
* @return Retorna boolean indicando el resultado.
*/
public boolean imprimirDocumento();
}
Se modifican las clases de las impresoras anteriores para que utilice las interfaces especializadas.
public class ImpresoraLX8578 implements MultifuncionalLXEscaneo,
MultifuncionalLXFaxeo,
MultifuncionalLXImpresion{
@Override
public boolean imprimirDocumento() {
//Lógica de impresión de documentos
return true;
}
@Override
public boolean escanearDocumento() {
//Lógica de escaneo de documentos
return true;
}
@Override
public boolean faxearDocumento() {
//Lógica de faxeol de documentos
return true;
}
}
Se define la clase ImpresoraLX8590 únicamente con las interfaces de escaneo e impresión.
public class ImpresoraLX8590 implements MultifuncionalLXEscaneo,
MultifuncionalLXImpresion{
@Override
public boolean imprimirDocumento() {
//Lógica de impresión de documentos
return true;
}
@Override
public boolean escanearDocumento() {
//Lógica de escaneo de documentos
return true;
}
}
Las modificaciones realizadas permiten que el código se encuentre en cumplimiento con el Principio de Segregación de Interfaces.
Diferencia entre el Principio de Segregación de Interfaces y el Principio de Sustitución de Liskov
Algunos desarrolladores pueden confundir estos dos principios SOLID. Sin embargo, ambos se enfocan en diferentes aspectos puntuales en el diseño de la programación orientada a objetos.
El Principio de Sustitución de Liskov se enfoca en subtipos y herencia, se enfoca en que los subtipos de una clase cumplan con el contrato especificado en la clase abstracta. Por otro lado, el Principio de Segregación de Interfaces se enfoca en segregar interfaces grandes en interfaces especializadas, así las clases que implementen dichas interfaces no dependan de interfaces y métodos que no utilizan.
Cuando utilizar el Principio de Segregación de Interfaces
Este principio, al igual que el principio de responsabilidad única, es clave para limitar el alcance de los cambios futuros en el diseño de las aplicaciones. Sin embargo, es muy difícil detectar estas violaciones durante las primeras iteraciones de la solución. Lo mejor es permitir que la solución evolucione para poder identificar los puntos claves en donde se puede aplicar este principio.
Si deseas ampliar tu conocimiento acerca del Principio de Segragación de Interfaces, puedes consultar el blog de Robert C. Martin.
En la próxima publicación discutiremos acerca del último principio SOLID, el Principio de Inversión de Dependencias.
Posted on May 24, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.