Esta web utiliza cookies para que podamos ofrecerte la mejor experiencia de usuario posible. La información de las cookies se almacena en tu navegador y realiza funciones tales como reconocerte cuando vuelves a nuestra web o ayudar a nuestro equipo a comprender qué secciones de la web encuentras más interesantes y útiles.
Single responsability principle – Principio de responsabilidad única
Open-closed principle – Principio de abierto-cerrado
Liskov substitution principle – Principio de sustitución de Liskov
Interface segregation principle – Principio de segregación de interfaces
Dependency inversion principle – Principio de inversión de dependencias
Continuando con nuestra serie de entradas sobre los principios SOLID, hoy nos centraremos en el cuarto, el principio de segregación de la interfaz (ISP).
Este principio fue formulado por primera vez por Robert C. Martin, lo define como:
“Los clientes no deben ser obligados a depender de interfaces que no utilizan”
También se puede definir como la priorización de la creación de múltiples interfaces específicas, en lugar de una única interfaz de uso general. Como resultado de su aplicación, se facilita mantener una arquitectura desacoplada, resultando más sencillo realizar refactorizaciones y modificaciones.
Para entender este principio lo primero es tener claro la funcionalidad de una interfaz dentro del diseño orientado a objetos. Las interfaces proporcionan una capa de abstracción que nos ayudan a desacoplar módulos. De esta manera, la interfaz define el comportamiento que nuestro código espera para comunicarse con otros módulos, a través de un conjunto de métodos y propiedades.
A continuación veremos un ejemplo de un sistema acoplado y como solucionarlo mediante el principio de segregación de la interfaz.
En la situación que vemos en la imagen de arriba, hay varios clientes que emplean las operaciones de la clase OPS, por ejemplo suponemos que «Client1» solo utiliza «op1», «Client2» solo utiliza «op2» y «Client3» solo utiliza «op3».
En este caso «Client1» dependerá involuntariamente de op2 y op3, aunque no los utilice. Cada vez que se realice un cambio en OPS será necesario volver a compilar los tres clientes aunque no haya cambiado ninguno de los elementos a los que hace referencia.
En código para la interfaz y para el Client1 se vería de la siguiente forma:
public interface IOperationsService { boolean op1(); string op2(); void op3(); } public class Client1 implements IOperationsService { @Override public boolean op1(){ //code } @Override public string op2(){ throw new UnsupportedOperationException(); } @Override public void op3(){ throw new UnsupportedOperationException(); } }
El código para «Client2» y «Client3» sería similar al código de «Client1», pero implementando solo el método que utilizan en cada caso.
Para resolver este problema se separa las operaciones en varias interfaces como se ve en el siguiente dibujo, de este modo «Client1» solo dependerá de «C1Ops» y «op1», por lo que un cambio en «OPS» que no implique al «Client1» no afectará a su implementación y compilación.
El código resultante para las interfaces sería:
public interface IOperation1Service { boolean op1(); } public interface IOperation2Service { string op2(); } public interface IOperation3Service { void op3(); }
Y cada cliente implementa solo la interfaz necesaria:
public class Client1 implements IOperation1Service { @Override public boolean op1(){ //code } }
Al crear interfaces más pequeños, tenemos la ventaja adicional de que, si es necesario crear nuevas implementaciones de la interfaz para un caso de uso concreto, no necesitaremos implementar también el resto de métodos que no están relacionados con ese caso de uso.
Un concepto interesante relacionado con este principio es el “role interface”, este tipo de interfaz implementa varios interfaces específicos para cada caso.
La conclusión obtenida de este principio es que ninguna clase debería depender de métodos que no usa. Por tanto, cuando creemos interfaces que definen comportamientos, es importante estar seguros de que todas las clases que implementen esas interfaces vayan a necesitar y ser capaces de agregar comportamientos a todos los métodos. En caso contrario, es mejor tener varias interfaces más pequeñas.
Como reflexión final, cuando un código depende de elementos o implementa métodos que no necesita, puede traer problemas no esperados.
Esperamos que estos ejemplos os hayan ayudado a entender el cuarto principio de SOLID. Si queréis saber más sobre el resto de principios, ¡seguid atentos a nuestro blog!