martes, 29 de octubre de 2013

The Interface Segregation Principle

The Interface Segregation Principle (ISP)
"Un cliente no debe estar obligado a depender de interfaces que no usa"

Este post pertenece a la serie Principios SOLID.

Primero me gustaría aclarar que aunque el nombre de este principio usa la palabra "Interface" se refiere a una interfaz en un sentido amplio de la palabra como cualquier estructura de código que define una parte pública de la clase que la implementa.
Es decir, debemos entender una interfaz tanto en el sentido estricto (IDisposable, IEnumerable, etc...) como en el sentido de una clase abstracta que al heredar de ella obliga a implementar ciertos métodos.

Es preferible varias interfaces pequeñas, cohesionadas y especializadas que una interfaz grande de propósito general

En este post hablaremos sobre los inconvenientes de interfaces grandes. Las clases con interfaces grandes están normalmente poco cohesionadas, es decir, los métodos y propiedades de la interfaz no suelen tener una relación fuerte con la misma responsabilidad "superior". Y es muy probable que estén violando el principio de responsabilidad única (SRP) del que ya hablamos en un post anterior.
Hay otra razón más sutil pero muy importante para evitar interfaces grandes. Supongamos la siguiente situación:


La interfaz IMethodForAll la implementan tanto ClientA como ClientB. IMethodsForAll tiene métodos que realmente sólo usa ClientA y otros sólo ClientB pero ambos están obligados a implementar todos los métodos. 

Supongamos ahora que nuestro jefe nos dice que después de un profundo estudio de las necesidades de los futuros clientes ha llegado a la conclusión de que se necesita agregar un nuevo método a ClientB y que en un alarde de originalidad llamaremos Method3ForClientB.
Agregamos Method3ForClientB la clase ClientB y a la interfaz. Pero ahora nos encontramos con que debemos modificar también ClientA para que implemente la interfaz correctamente.

Este ejemplo aunque de una forma simplista nos revela como al tener una interfaz grande cuando modificamos un cliente de la interfaz (ClientB) tenemos que modificar otro cliente (ClientA) que en principio no tiene porque estar relacionados. En otras palabras, produce un "acoplamiento indirecto" entre clientes de la interfaz.

Para solventar este problema debemos segregar la interfaz en varias interfaces más pequeñas y cohesionadas para cada tipo de cliente. Si un método es común a varias de estas nuevas interfaces se pone el  mismo método en todas las interfaces que lo necesiten (esto facilita mucho al cliente la comprensión de la interfaz que necesita implementar).

Bueno, la idea está clara: "dividir las interfaces grandes en varias interfaces pequeñas más cohesionadas". Pero no nos volvamos locos. No podemos crear una interfaz para cada cliente, ni cosas como versiones de la misma interfaz al estilo IMethodsForClientA_v2, IMethodsForClientA_v3, etc. Esto asusta sólo con pensarlo.
En lugar de eso, tendremos que tratar de agrupar los clientes por categorías y crear una interfaz para cada categoría de clientes. Si un cliente tiene que implementar métodos de varias categorías entonces implementará varias interfaces y solucionado.

Un ejemplo de todo esto que hemos visto es la clase abstracta MembershipProvider del framework .NET.
Esta es la clase abstracta de la que se debe heredar para implementar tu propio sistema de credenciales en una aplicación ASP.NET. Esta clase abstracta tiene ¡27 métodos abstractos! que te obliga a sobrescribir cuando heredas de ella y ¡puedes escribir tu propio "provider" sobrescribiendo sólo el método ValidateUser!. Una interfaz enorme y puedes crear clientes que sólo necesitan un método.

Un problema que nos surge al usar clases abstractas o interfaces grande de terceros (como el caso de MembershipProvider) es que no podemos cambiarlas. Para separar estar interfaces grandes en otras mas pequeñas y cohesionadas podemos usar el Patrón Adapter que nos permite ocultar la parte de esa interfaz grande que no nos interesa.
El patrón adapter lo veremos en profundidad junto con muchos otros en dentro de la próxima serie de post sobre patrones de diseño.


No hay comentarios:

Publicar un comentario