lunes, 6 de enero de 2020

Capítulo 14: acoplamiento entre componentes - Parte 2 - Clean Architecture

Diseño descendente

La estructura de componentes no se diseña de manera descendente si no que esta evoluciona a medida que se desarrolla es sistema.

La estructura de dependencia de componentes no tiene que ver con la funcionalidad de la aplicación. Esta tiene que ver con la construibilidad y la mantenibilidad del sistema. Esta es la razón por la cual no se diseña al principio del proyecto. No hay nada que construir ni nada que mantener. 

A medida que el sistema se va desarrollando se necesita gestionar las dependencias que van surgiendo. Se deberían usar «Single Responsibility Principle» y «Common Closure Principle» para agrupar las clases que cambian juntas.

Una de las principales preocupaciones de la estructura de dependencias es aislar la volatilidad. Los componentes no deberían cambiar frecuentemente y deberían ser estables. En este punto,el gráfico de dependencia se utiliza para proteger los componentes estables frente a los componentes volátiles.

Cuando el sistema sigue creciendo surge la necesidad de la reusabilidad. En este punto el «Common Reuse Principle» comienza a influir en la estructura de los componentes.

Como se puede observar la estructura de dependencia de componentes se va desarrollando a medida que el sistema crece. 

El Principio de dependencias estables

El diseño no puede ser completamente estático dado que es necesario algo de volatilidad si hay que mantener el diseño. Por el «Common Closure Principle» se crean componentes que son sensibles a ciertos tipos de cambios pero inmunes a otros. Algunos de estos componentes están diseñados para ser volátiles. Se espera que ellos cambien.

Ningún componente que se espera que sea volátil debería depender de un componente que es difícil de cambiar. De otra manera, el componente volátil también sería complicado de cambiar.

Esta es la perversidad del software dado que un módulo que has diseñado para que sea fácil de cambiar se convierta en difícil de cambiar por alguien quién simplemente cuelga una dependencia con él. No se ha cambiado una sóla línea de código en el módulo pero debido a la dependencia este es más difícil de cambiar. Al cumplir con el «Stable Dependency Principle» nos aseguramos de que los módulos que son fáciles de modificar no dependen de módulos que sean más difíciles de cambiar.

Estabilidad

Lo primero sería definir qué es la estabilidad. Si se pone de canto una moneda tenemos la sensación de que no está en una posición estable. No obstante, si la moneda no sufre ninguna perturbación podría estar un tiempo indefinido en esa posición. Podemos concluir que la estabilidad no tiene que ver con la frecuencia de cambio.

Una definición más acertada sería que la estabilidad tiene que ver con la cantidad de trabajo que se requiere para producir un cambio. En el caso de la moneda, no se requiere mucha cantidad de trabajo para que cambie su estado y por ello se considera inestable.

La estabilidad de un componente software va a depender de muchos factores como pueden ser su tamaño, la complejidad que albergue, su claridad, etc. Una manera de hacer un componente difícil de cambiar es hacer que otros componentes dependan de este componente. En este caso, cualquier cambio en el componente requiere cambios en todos los componentes dependientes.

En el siguiente diagrama se puede observar 4 componentes. El componente X es un componente del cual dependen los otros 3. Se dice que el componente X es estable debido a que tiene 3 razones para no cambiar. No obstante, el componente X no tiene dependencias por lo que se dice que este es independiente.

Métricas de estabilidad

¿Cómo se puede medir la estabilidad de un componente? Una forma es contar el número de dependencias que entran y salen de ese componente. Estos número nos indicarán la estabilidad posicional del componente.

·        Fan-in: dependencias de entrada. Estas métricas identifican el número de clases fuera del componente que dependen de clases de dentro del componente.
·        Fan-out: dependencias de salida. 
·        I: Instability: «I = Fan-out / (Fan-in + Fan-out)». Esta métrica tiene el rango [0,1]. I con el valor 0 indica un valor estabilidad máxima mientras que I = 1 tiene una inestabilidad máxima.

Las métricas «Fan-out» y el «Fan-in» se calculan contando el número de clases fuera del componente que tiene dependencia con las clases dentro del componente en cuestión. 

Considerando el siguiente diagrama se va a proceder a calcular a la estabilidad del componente «Cc».



Se puede apreciar que hay 3 clases fuera del componente «Cc» que dependen de clases del componente «Cc». Entonces, el «Fan-in»  es 3. Además, hay una clase «v» del componente «Cd»  que es dependiente de la clase «u» del componente «Cc». Por lo tanto el «Fan-out» es 1 y la Inestabilidad «I» tiene un valor: 

I = 1 / (4+1) = ¼

Cuando la métrica «I» es igual a 1 quiere decir que ningún otro componente depende de este componente (Fan-in = 0) y este componente depende de otros (Fan-out > 0). 

En el siguiente diagrama se puede apreciar como el Componente A tiene un «Fan-in» de 0 y un «Fan-out» de 3 dando una estabilidad de 1. Esta situación representa la máxima inestabilidad de un componente. Este componente es dependiente e irresponsable.



Cuando la métrica «I» es igual a 0 significa que otros componentes depende de él («Fan-out» > 0) pero él mismo no depende de otros componentes («Fan-In» = 0). Este es un componente tan estable como puede llegar a ser.

En el siguiente diagrama se puede apreciar como el Componente D tiene un «Fan-in» de 3 y u «Fan-out» de 0 dando lugar a una estabilidad de 0. Esta situación representa todo lo estable que un componente puede llegar a ser.

Sus dependencias hacen más complicado cambiar el componente y no tiene dependencias que lo obliguen a cambiar.

El «Stable Dependency Principle» establece que la métrica «I» de un componente debería ser mayor que la métrica «I» de los componentes que este depende. Esto significa que las métricas «I» deberían disminuir en la dirección de la dependencia.


No todos los componentes deberían ser estables


Si todos los componentes de un sistema tuvieran la máxima estabilidad el sistema no sería capaz de cambiar y esto no es una situación deseable. Una buena arquitectura debe tener una arquitectura de componentes de manera que algunos componentes son estables y otros inestables. 

Imaginemos un sistema que inicialmente está formado por tres componentes.


A partir de este punto se requiere de un nuevo componente «Cd» y este se diseña de manera que sea fácil de cambiar, es decir, que sea volátil. Una vez creado y añadido al sistema, un desarrollador crea una dependencia de una de las clases del componente «Cc» (estable) al componente «Cd» (volátil) quedando de la siguiente manera.


Esto cambia las métricas por lo que hace que el componente volátil, que inicialmente se diseñó para ser fácil de cambiar, sea difícil de cambiar ya que un cambio en este forzará a tratar con todas las dependencias que tenga el componente estable.

La única manera de volver a hacer que el componente volátil sea fácil de cambiar es que este no tenga dependencia con el componente estable. Imaginemos que el componente estable tiene una clase A de la cual hace uso una clase B que es parte del componente volátil.

Para solucionar este problema se puede recurrir al «Dependency Inversion Principle». En este caso se puede crear una interfaz «AI» e incluir esta en un nuevo componente «AServer». Esta nueva interfaz debe debe definir todos los métodos que la clase A necesita usar. Entonces, se hace que la clase B implemente esta interfaz tal y como se muestre en la siguiente imagen.
Esto rompe la dependencia del componente estable con el componente volátil. Ahora, ambos componentes dependen del componente «AServer». El componente «AServer» es un componente muy estable dado que su métrica «I» tiene un valor de 0 mientras que el componente volátil mantiene su inestabilidad máxima con un valor de «I» de 1 (I=1/1=1).

Ahora se puede decir que no se vulnera el «Stable Dependency Principle» y que todas las dependencias fluyen ahora en dirección de «I» decreciente.

Componentes abstractos


Es posible encontrarse con un componente que sólo tenga una interfaz y nada de código ejecutable como puede ser el caso de «Aserver». Esto son componentes abstractos con la máxima estabilidad que hace que otros componentes menos estables dependan de ellos.

No hay comentarios:

Publicar un comentario