DEV Community

Victor Manuel Pinzon
Victor Manuel Pinzon

Posted on

SOLID: Principio de Segregación de Interfaces

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();
}
Enter fullscreen mode Exit fullscreen mode

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;
    }

}
Enter fullscreen mode Exit fullscreen mode

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."); 
    }

}
Enter fullscreen mode Exit fullscreen mode

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();
}
Enter fullscreen mode Exit fullscreen mode

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;
    }

}
Enter fullscreen mode Exit fullscreen mode

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;
    }

}
Enter fullscreen mode Exit fullscreen mode

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.

Top comments (1)

Collapse
 
metalback profile image
Mauricio Beltrán

Don Victor, hace tiempo que sigo sus post y agradezco profundamente la forma en que explica los alcances de cada principio. En lo personal, siempre he considerado este principio el "menos" importante de los 5 ya que debería ser una consecuencia de aplicar otros principios.
En este punto tengo la siguiente consulta: ¿Cómo podemos evitar que nuestras interfaces crezcan exponencialmente a n-metodos que pueda "llegar a tener" la clase concreta que implementa dichas interfaces o estima que es efectivamente la mejor manera de abordar la concreción de diseños?.
En todos los ejemplos que he visto de ISP, siempre la solución es llevar a declaración de interfaces los métodos definidos en el diseño original pero por lo mismo siempre me ha causado ruido la manera en que se aborda.