Los cinco principios de diseño de software SOLID fueron reunidos por Robert Martin (@unclebobmartin) a partir de varias ideas y papers pre-existentes en la comunidad. En estos artículos los revisaremos uno a uno con la interpretación desde mi experiencia con ellos y cómo han afectado mi forma de programar.
Todos los principios los puedes ver en el índice: http://www.davidlaym.com/2015/05/solid-resumen-de-los-principios
Principio de Segregación de Interfaces
La I de SOLID es por “Interface Segregation Principle” que se puede traducir como Principio de “Segregación de Interfaces”. Se define como:
Los clientes no deben ser forzados a depender de métodos que no utilizan
Cuando hablemos de interfaz, no estaremos hablando exclusivamente de el keyword en un lenguaje en particular, sino que del conjunto de métodos y propiedades públicas que expone una clase hacia sus clientes.
Este principio trata el caso de clases que, si bien cumplen con el SRP, los clientes que utilizan estas clases ocupan solo un subconjunto de los métodos presentados por su interfaz. En estos casos, este principio nos recomienda crear interfaces independientes para cada uno de los clientes, ya sea mediante herencia o implementación de «interface».
Un ejemplo sencillo
Un bus pub/sub es un ejemplo perfecto. Si no te ha tocado trabajar con ellos, son clases que se encargan de recibir mensajes y publicarlos a todos los subscriptores, como eventos, pero sin el acoplamiento que conllevan. Un diseño ultra simplificado de un bus sería:
Podemos deducir que un bus tiene dos tipos de clientes: subscriptores y publicadores. Aveces habrán algunos clientes que realicen ambas actividades, pero no es un caso típico. Esta clase respeta el SRP ya que tiene un solo motivo para cambiar: que cambie la forma de tratar los mensajes.
Un diseño que respete el ISP sería:
Sin embargo este diseño es muy poco flexible y obliga a tener instancias separadas para cada uno de los roles. Además en la mayoría de los lenguajes, este diseño es muy complicado de implementar debido a que el publicador debe conocer los subscriptores, debiendo compartir de alguna forma las estructuras de datos internas.
Un diseño más práctico es el siguiente:
De esta forma se puede tener una sola instancia como singleton, que maneja todos las estructuras de datos internas, pero los clientes podrán depender de la interfaz que utilicen. Si usan ambas interfaces (esos raros casos) pueden depender de ambas o depender de la clase directamente.
Es difícil apreciar a primera instancia los problemas que se pueden generar al violar este principio, o los beneficios que se obtienen al cumplirlo, ya que parece algo muy inocuo y normal tener clases para los que distintos clientes utilicen distintos métodos, por lo que veremos algunos de los que considero más importantes:
Intencionalidad
Al cumplir con ISP, mejoramos la intencionalidad de nuestro código, compara lo siguiente:
public class AlgunaClaseEnTuCodigo { // constructor public AlgunaClaseEnTuCodigo(Bus bus) { // implementación omitida } }
public class AlgunaClaseEnTuCodigo { // constructor public AlgunaClaseEnTuCodigo(BusSubscriber subscriber) { // implementación omitida } }
Los dos trozos de código son casi idénticos, excepto en los componentes de los que dependen. En el segundo caso podemos determinar que esta clase se subscribe en algún momento al bus solamente leyendo la firma del constructor. En el primer caso no podemos saber nada sobre el uso que se le dará al bus del cual depende.
Esto es intencionalidad, quiere decir, nuestra codificación refleja la intención con la cual se realizan las decisiones de diseño. Es muy importante para mantener nuestro código fácil de mantener en el tiempo.
Pruebas unitarias
Al realizar pruebas unitarias, se nos simplifica mucho más la tarea si tenemos que realizar fakes o mocks a interfaces más sencillas. Es súper simple, pero las pequeñas mejoras suman y suman.
Simplificación de jerarquías
Al cumplir con ISP, pavimentamos el camino para simplificar las jerarquías de clases.
Para mi este es uno de los grandes beneficios del ISP. En el ejemplo de este artículo no se puede apreciar, pero no es difícil recordar alguna clase que tenía tres o cuatro niveles de herencia en alguno de los proyectos que hemos trabajado. Es algo que normalmente sucede. Es posible que esa cadena de herencias cumpla con el SRP, incluso posible que respete el LSP, pero es muy probable que no respete el ISP simplemente porque al heredar tantos métodos la interfaz pública es simplemente un asco. Respetar el ISP generalmente lleva a tomar la decisión no solo de separar interfaces, sino que mediante aquello también deshacer esas jerarquías y remplazar la herencia por colaboración de componentes, que es un modelo mucho más flexible en el largo aliento.
Share this post
Twitter
Google+
Facebook
Reddit
LinkedIn
StumbleUpon
Pinterest
Email