jueves, 5 de abril de 2018

Clases - Clean code

Esta publicación se centra en el capítulo de Clases de Clean Code (Robert C. Martin).

Organización de las clases

Las clases deberían seguir una guía de estilos por lo que sería recomendable que el equipo se reuniese para establecer en la guía de codificación cómo se debería codificar una clase.

Encapsulación

La clase debería encapsular las variables y no se debería poder acceder a ellas directamente sino a través de un método. Las funciones que son utilidades de la clase deberían ser privadas o, en su defecto, estar protegidas para que se puedan heredar. En cualquier caso, perder la encapsulación debería ser el último recurso.

Tamaño de las clases

Las clases utilizan una medida diferente a la contabilización de las líneas de código para evaluar su envergadura. En este caso se cuentan las responsabilidades que tiene una clase.

El nombre de la clase es un buen indicador de la cantidad de responsabilidades de la misma. En caso de tener tener un nombre un poco ambiguo o si se incluyen partículas como  «and», «or» u otra palabra que encadene más de una funcionalidad, como «if», es muy posible que la clase tenga demasiadas responsabilidades.

El principio de responsabilidad única SRP

El principio de responsabilidad única o Single Responsibility Principle (SRP) establece que una clase debería tener una única razón para cambiar.

En los casos en los que una clase tiene demasiadas responsabilidades y se decida hacer una refactorización para cumplir el SPR el resultado será un sistema con más clases de inferior tamaño.

Estas clases, con una única responsabilidad, deberían colaborar entre sí para cumplir con las reglas de negocio.

Cohesión

Una clase debería tener un pequeño conjunto de variables de instancia y estas variables deberían ser manipuladas por los métodos de la clase. Cuantas más variables de instancia manipule un método más cohesivo es ese método con respecto de la clase. Si un método manipula todas las variables de instancia se dice que es totalmente cohesivo.

Es complicado crear clases que sean totalmente cohesivas y tampoco se recomienda. Aunque sí aconsejan que la cohesión de la clase sea alta de manera que los métodos y las variables sean codependientes y se usen como un todo.

La estrategia de reducir al máximo tanto las funciones como su lista de parámetros deriva en la creación de variables de instancia que son usadas por un subconjunto de métodos. En estos casos es posible que se puedan refactorizar esas variables y métodos en una nueva clase fuera de la clase más grande.

Mantener la cohesión da como resultado muchas pequeñas clases
En el caso de querer refactorizar una función demasiado larga con varias variables locales dentro de ella, podemos considerar extraer un método. Este método debería tener como parámetros alguna de estas variables locales.

Se puede utilizar la estrategia de refactorizar estas variables del método como variables de instancia de manera que el nuevo método use estas variables de instancia dejando la firma del método limpia de variables.

Esta estrategia conlleva la pérdida de cohesión, ya que estas variables sólo son utilizadas por el método inicial y el nuevo método extraído. De forma que, si hay varias variables de instancia que solo son utilizadas por algunos métodos, se puede extraer una nueva clase, por lo que cuando una clase pierde cohesión se podrá extraer una nueva clase.

Organización desde el cambio

Cuando se quiere añadir una nueva funcionalidad a una clase es posible que tengamos que «abrir» esa clase. Abrir una clase significa modificar la clase. Cualquier modificación que se haga en una clase tiene el riesgo potencial de romper el código que ya estaba funcionando por lo que se debe volver a comprobar todo el comportamiento de la clase para asegurar que la clase funciona correctamente.

Es posible que inicialmente no se haya detectado que la clase tiene más de una razón para cambiar y que está violando el SRP. En estos casos hay que replantear la estructura de la clase.

Si la estrategia es abrir la clase entonces hay que replantear el diseño de manera que se cumpla el Open-Closed Principle (OCP) el cual establece que las clases deben estar abiertas para la extensión pero cerradas para su modificación.

En este punto hay que replantear la distintas funcionalidades de la clase para que cada una se extraiga a una nueva clase y esta sea una subclase de la clase inicial. Con esta nueva estructura se podría implementar la nueva funcionalidad a través de la creación de una nueva subclase. Esta nueva subclase no modifica el código existente ya que las funcionalidades anteriores están aisladas entre sí en otras subclases.

Es aconsejable estructurar el sistema para trabajar lo menos posible a la hora de añadir o modificar una nueva característica. En un sistema ideal se incorporan nuevas funcionalidades por extensión del sistema, en lugar de hacerlo modificando el código existente.

Aislamiento desde el cambio

Cuando una clase depende de los detalles de implementación corre el riesgo de cambiar cuando estos detalles cambian.

Además, los detalles de implementación concretos pueden llegar a ser difíciles de testear si estos dependen de fuentes externas como una base de datos o una API que pueden no estar disponibles o no devolver siempre los mismos datos.

Las interfaces y las clases abstractas representan conceptos por lo que pueden aislar los detalles de implementación en clases que utilicen estas interfaces o hereden de las clases abstractas.

Utilizando las interfaces y las clases abstractas se consigue que el código esté menos acoplado y se puedan cambiar los detalles de implementación o añadir nuevas funcionalidades sin modificar el código existente.

A la hora de testear la clase se puede usar un Stub para simular el comportamiento de ese detalle de implementación y obtener unos datos concretos para el fin del test.

Al minimizar el acoplamiento de esta manera, las clases están cumpliendo con el Dependency Inversion Principle (DIP) el cual establece que las clases deberían depender de abstracciones y no de detalles concretos.

No hay comentarios:

Publicar un comentario