Con el tiempo, los desarrolladores de software adquieren experiencia como, por ejemplo, la capacidad de evaluar la calidad del código y detectar problemas de diseño y errores. Es una cosa extraña, un “sexto sentido” que has desarrollado, sientes que algo no está bien, pero para poder explicarlo, debes hacer memoria. Esto es algo parecido a la capacidad de los mecánicos de los coches que simplemente por escuchar el sonido del motor saben cuál puede ser el problema del coche.

En este artículo vemos qué es lo bueno, lo feo y lo malo en el diseño de software, o dicho de otra manera: qué debemos respetar y qué debemos evitar mientras estamos diseñando el software.

Lo bueno: los patrones de diseño

Ya hemos hablado sobre los principios SOLID y las 5 reglas del diseño de software simple que son principios que se deben tomar en cuenta cuando diseñamos el software, pero no debemos olvidarnos sobre los patrones de diseño.

Los patrones de diseño son soluciones probadas para problemas comunes que, relacionados entre sí en orden de posición y utilidad, forman un lenguaje de patrones para describir tanto el problema como las soluciones para diferentes contextos. El término lenguaje de patrones fue acuñado por el arquitecto Christopher Alexander y popularizado por su libro de 1977 A Pattern Language. Después, la idea de utilizar patrones para resolver problemas comunes se entendió en otras disciplinas.

En 1994, Erich Gamma, Richard Helm, Ralph Johnson y John Vlissides, conocidos como "Gang of Four" (GoF), publicaron el libro “Design Patterns: Elements of Reusable Object-Oriented Software”, el cual contiene 23 patrones de diseño de software y se convirtió en referente en la programación orientada a objetos.

Los patrones de diseño se dividen en tres categorías:

1 Patrones creacionales

Los patrones creacionales están enfocados a la creación de objetos de manera eficiente y flexible en diferentes situaciones, reduciendo la complejidad del código y mejorando la reutilización de código.

Los patrones creacionales son:

2 Patrones estructurales

Los patrones estructurales proporcionan soluciones para simplificar la estructura de los objetos y las relaciones entre ellos, lo que facilita la comprensión y la modificación del sistema de software.

Los patrones estructurales son:

3 Patrones de comportamiento

Los patrones de comportamiento se utilizan para resolver problemas relacionados con la interacción y comunicación entre objetos.

Los patrones de comportamiento son:

Lo feo: code smells

A veces, mientras caminamos podemos percibir un olor desagradable sin ver su origen, lo que nos indica que algo no está bien, pero no sabemos qué. Lo mismo ocurre con los code smells (también conocidos como un código que huele o apesta), síntomas en el código que vienen a indicar que tal vez no se están haciendo las cosas de una forma del todo correcta, lo que puede derivar en problemas futuros.

Martin Fowler y Kent Beck, reconocidas autoridades en el tema de refactoring, en su libro titulado “Refactoring: Improving the Design of Existing Code”, que fue publicado en 1999, presentaron 22 patrones de diseño deficientes en aplicaciones orientadas a objetos, los cuales denominaron "bad smells in code”, y presentan las posibles guías para removerlos. Lo que no queda muy claro en su libro es cómo detectar estos bad smells. Los autores consideran que lo mejor es la inspección humana para tal fin y afirman:

“En nuestra experiencia ningún conjunto de métricas rivalizan con la información de la intuición humana”.

Los code smells son parecidos a los antipatrones de programación, pero funcionan a un nivel todavía más sutil. No describen situaciones complejas, sino indicios, que son bastante subjetivos y dependientes del lenguaje y las tecnologías concretas.

Los code smells no quieren decir qué hay errores o bugs de programación, ya que pueden no ser técnicamente incorrectos y la aplicación funcione correctamente. Los code smells indican deficiencias en el diseño y pueden hacer que se realice un desarrollo más lento, aumentando el riesgo de errores o fallos en el futuro.

Es difícil definir si el código es malo o bueno, o cuando deberíamos cambiarlo. Un buen desarrollador tiene el “olfato fino” para detectarlos.

Podemos agrupar los codes smells en 5 categorías:

1 Bloaters

Los bloaters son métodos y clases que han aumentado a proporciones tan gigantescas que es difícil trabajar con ellos. Por lo general, estos code smells no surgen de inmediato, sino que se acumulan con el tiempo a medida que la aplicación evoluciona (y especialmente cuando nadie hace un esfuerzo por erradicarlos).

En este grupo también se incluye:

2 Object-Orientation Abusers

Todos estos olores son una aplicación incompleta o incorrecta de los principios de programación orientada a objetos.

3 Change Preventers

Estos code smells indican que si se requiere realizar un cambio en una parte del código, es probable que también sea necesario realizar cambios en múltiples lugares del mismo. Como resultado, el desarrollo de la aplicación se vuelve mucho más complicado y costoso.

4 Dispensables

Un prescindible es algo sin sentido e innecesario cuya ausencia haría que el código fuera más limpio, más eficiente y más fácil de entender. en esta categoría son por ejemplos los:

5 Couplers

Los code smells en este grupo aumentan el acoplamiento entre clases o ilustran los problemas causados por una delegación excesiva:

Lo malo: STUPID code

STUPID code es un acrónimo de los antipatrones de diseño:

Veámoslos en detalle.

1 Singleton

¿Cómo? ¿El singleton no era un patrón de diseño? Sí, el singleton es probablemente el patrón de diseño más conocido, pero también el más incomprendido.

¿Conocéis el síndrome de singleton? Es que se usa en todas partes. Eso definitivamente no es genial.

Los singleton son controvertidos y, a menudo, se los considera antipatrones. Deberías evitarlos. En realidad, el uso de un singleton no es el problema, sino es síntoma de un problema. Aquí hay dos razones por las que:

Pero, ¿debería realmente evitarlos todo el tiempo? Yo diría que sí porque a menudo se puede reemplazar el uso de un singleton por algo mejor.

La única exclusión es la inyección de las dependencias, allí los singleton están gestionados por el contenedor de IoC (Inversión de Control) que se utiliza para implementar la inyección de dependencia.

2 Tight Coupling

El acoplamiento estrecho (también conocido como acoplamiento fuerte) es una generalización del problema Singleton. Básicamente, hay que reducir el acoplamiento entre las clases. El acoplamiento es el grado en el que cada clase de la aplicación se basa en cada una de las otras clases.

Si realizar un cambio en una clase de su aplicación requiere que cambie a otra clase, entonces existe acoplamiento. Por ejemplo, crear instancias de objetos en la clase de su constructor en lugar de pasar instancias como argumentos. Eso es malo porque no permite más cambios, cómo reemplazar la instancia por una instancia de una subclase, una mock o lo que sea.

Las clases estrechamente acopladas son difíciles de reutilizar y también difíciles de probar.

3 Untestability

Testar una clase no debe ser una tarea difícil. No realmente. La mayoría de las veces, el problema de testar las clases se debe a un acoplamiento estrecho.

No sé quién ha dicho esta frase, pero me encanta:

“Siempre que no escribas unit tests porque no tienes tiempo, el problema real es que tu código es malo.”

La mejor manera de solucionar este problema es practicando TDD.

4 Premature Optimization

Optimizar un código que no está terminado puede ser una pérdida de tiempo.

Me encanta la frase de Donald Knuth:

“La optimización prematura es la raíz de todos los males. Solo hay costo y no beneficio”.

En realidad, las aplicaciones optimizadas son mucho más complejas que simplemente reemplazar un bucle for por stream, o usar preincremento en lugar de postincremento. Terminará con un código ilegible. Es por eso que la optimización prematura a menudo se considera un anti-patrón.

Se suele decir que hay dos reglas para optimizar una aplicación:

5 Indescriptive Naming

Esto debería ser obvio, pero aún debe decirse: nombre sus clases, métodos, atributos y variables correctamente. ¡Ah, y no abrevie! Escribe código para personas, no para ordenadores. De todos modos, no entienden lo que escribes. Los ordenadores simplemente entienden 0 y 1. Los lenguajes de programación son para humanos.

6 Duplication

El código duplicado es malo, así que respeta DRY y KISS. Sea perezoso de la manera correcta: ¡escriba el código solo una vez!

Conclusiones

El código con el que trabajamos es como nuestra casa, si no la limpiamos en unos cortos periodos de tiempo, se va a llenar de polvo. La mejor técnica de evitar esto es la refactorización, la cual nos permite mejorar el diseño de software existente sin cambiar su comportamiento externo. Es una práctica importante para garantizar que el código sea mantenible y escalable a largo plazo.

En los siguientes artículos veremos cómo detectar lo feo (los code smells) y aplicar lo bueno: los principios SOLID, las 5 reglas del diseño de software simple y los patrones de diseño con kata de programación.

Bibliografía

Cuéntanos qué te parece.

Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.

Suscríbete

Estamos comprometidos.

Tecnología, personas e impacto positivo.