CUPIDo mató a los principios SOLIDos (I)

Desmontando los principios SOLID

A principios de 2017, en el marco de una PubConf, una conferencia desarrollada en un pub, y en la que se llevan a cabo charlas donde el ponente suele tener 5 minutos de exposición haciendo uso de una presentación con 20 diapositivas que van pasando de manera automática cada 15 segundos, Dan North expuso su controvertido «Why Every Element of SOLID is Wrong» (Por qué cada elemento de SOLID es erróneo).

CUPIDo y SOLIDos

Marcos Antonio González Gómez-Caro
Marcos Antonio González Gómez-CaroSenior Software Developer en VIEWNEXT

Es decir, en 5 minutos, con tres diapositivas por principio (una de introducción, una para criticarlo y otra para proporcionar una alternativa) intentó desmontar algo que se considera por gran parte de la industria uno de los pilares del buen desarrollo de software. Ahí es nada.

Pero, primero hagamos el ejercicio de recordemos de manera somera cuáles son los principios SOLID:

  • Single Responsibility Principle [SRP] (Principio de Responsabilidad Única).
  • Open Closed principle [OCP] (Principio de Abierto Cerrado).
  • Liskov Substitution Principle [LSP] (Principio de Sustitución de Liskov).
  • Interface Segregation Principle [ISP] (Principio de Segregación de Interfaces).
  • Dependency Inversion Principle [DIP] (Principio de Inversión de Dependencia).

Con ellos en mente pasemos a ver el tratamiento que Dan North le dió a cada uno de ellos:

Single Responsibility Principle (Principio de Responsabilidad Única).

“A class should have only one reason to change.”

Se planteó inicialmente como que un modulo o clase debería tener una, y sólo una, razón para cambiar. Debe tener una responsabilidad, y por lo tanto, una única razón para cambiar.

Para Dan North es el Pointlessly Vague Principle (Principio Inútilmente Vago). Y es que, ¿qué es hacer una única cosa? Y ponía como ejemplo un ETL (Extract, Transform and Load). ¿Es una sola cosa? ¿Son tres distintas?

En su lugar, Dan North habla de escribir código sencillo, siguiendo la heurística de “que quepa en mi cabeza”, en el sentido de que sea posible razonar sobre dicho código sin dificultad. Y, además, esta heurística debería trasladarse a cualquier nivel de abstracción (método, clase, componente o sistema distribuido).

El objetivo final debe ser poder captar el todo a cualquier nivel, y no generar particiones artificiosas por el hecho de seguir el concepto de responsabilidad única. Por lo tanto, podemos tener código que haga varias cosas relacionadas, y teniendo todo lo anterior en consideración, se puede refactorizar el código hasta conseguir que el mismo “quepa en tu cabeza”, cumpliendo con la heurística.

Open Closed Principle (Principio Abierto Cerrado).

“Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification.”

La idea es que el código debería estar abierto a extensión, pero cerrado a modificación. Es decir, que puedas extenderlo sin cambiarlo, y que puedas confiar en lo que hace para no tener la necesidad de modificarlo.

Dan North no le quita un ápice del valor que proporcionaba (ojo al tiempo verbal empleado) en una época en la que los cambios eran caros en tiempo (compilación, enlazado, etc), y un claro riesgo debido a la falta de conocimientos de técnicas de refactorización, herramientas (IDEs) para automatizar dichas refactorizaciones, etc, a día de hoy todas esas restricciones han desaparecido.

Actualmente, sin las restricciones mencionadas, el código debe considerarse maleable, por lo que si hay necesidad de que el código haga otra cosa, se cambia para que lo haga. Es decir, si esa necesidad de cambio existe, se debe considerar que el código actual es erróneo, por lo que hay que reemplazarlo con código que funcione correctamente.

Para el autor, este principio se puede renombrar como el Cruft Accretion Principle (Principio de Acumulación de Basura), donde el término cruft viene de la jerga usada en el MIT para los deshechos tecnológicos que se iban acumulando en el Laboratorio Cruft de Harvard. Vendría a ser como un Síndrome de Diógenes digital relativo al código.

Ahondando en la idea, el código no debe considerarse como un activo que debe ser preservado, sino como un coste. Todo código es un coste, y como tal, su reducción debe ser un objetivo en sí misma, y escribir código sencillo ayuda a que sea fácil de cambiar, de testar, y a tener un código abierto y cerrado, lo que sea que se requiera.

Liskov Substitution Principle (Principio de Substitución de Liskov).

“Objects should be replaceable with their subtypes without affecting the correctness of the program.”

Inicialmente formulado por Barbara Liskov en 1987 en una keynote titulada Data abstraction and hierarchy, según el mismo, toda clase derivada (subtipo) debe poder ser sustituida por sus clases base.

Dan North lo compara con el Principle of Least Surprise, o Principio de la Menor Sorpresa, aplicado a la sustitución de código, de modo que a un subtipo de algo se le debería poder asumir un comportamiento igual a ese algo, llegando a reformularlo como el Drucker’s Warning Principle (Principio de Advertencia de Drucker) que postula que no hay nada más inútil que hacer con gran eficiencia algo que no debería ni siquiera hacerse.

Sin embargo, aquí entra en juego la distinción entre subtipo y subclase, siendo esta última la que prevaleció entre los desarrolladores, por lo que la tendencia fue a establecer, y estancarse, en las relaciones “is-a” y “has-a” como las únicas que se podían aplicar. Lo que debemos buscar es componer las estructuras complejas que necesitemos haciendo uso de otras pequeñas y simples.

De nuevo, como en los anteriores principios, el autor aporta como solución el escribir código sencillo sobre el que se pueda razonar.

Interface Segregation Principle (Principio de Segregación de Interfaz).

“Keep interfaces small so that users don’t end up depending on things they don’t need.”

El enunciado de este principio indica que ningún cliente debería verse forzado a depender de métodos que no va a usar.

Esto es algo que es totalmente deseable, pero el autor argumenta que simplemente no es un principio, sino un patrón, una estrategia que trabaja bien en un contexto concreto. De hecho, el escenario de un Objeto Dios (God Object) fue lo que motivó a Robert C. Martin a formular el principio.

Si realmente fuera un principio, debería ser el “Stable Door Principle” (Principio de la Puerta de Establo) en el que tendremos clases pequeñas, basadas en el rol que van a jugar. Es decir, escribir código sencillo.

Dependency Inversion Principle (Principio de Inversión de Dependencia).

“Depend in the direction of abstraction. High level modules should not depend upon low level details.”

El enunciado del principio indica que los módulos de más alto nivel no deberían depender de los de más bajo nivel. Ambos deberían depender de abstracciones.

Según Dan North, el principio no tiene nada malo per se si lo que se considerara fuera la inversión de opciones (siendo la opción la relativa a la dependencia a aplicar). Lo malo son las consecuencias que ha acarreado. Y es que una dependencia, siempre según el autor, sólo es interesante cuando puede haber múltiples formas de proporcionarla. Y sólo se necesita invertir la relación cuando crees que la misma es lo suficientemente importante como para tratarla como una preocupación con entidad propia.

Lo que ha ocurrido es que hemos terminado en la idea de que debemos invertir todas las dependencias, generando bases de código donde cada clase es respaldada por una interfaz la cual sólo existe para satisfacer al framework de turno, por lo que pasamos a tener otro tipo de dependencia, la del framework de inversión que usemos, por lo que para el autor este sería el Wrong Goal Principle (Principio del Objetivo Equivocado).

Y es que no se necesita invertir la mayoría de las dependencias ya que no son opciones sino la forma en la que se van a hacer las cosas. Por lo que escribir código sencillo enfocado en el uso más que en el reusado sería la formula a aplicar.

En resumen, para Dan North la bala de plata para el desarrollo de software es el escribir código sencillo. Pero, ¿qué es escribir código sencillo? ¿Cómo se consigue escribir código sencillo?

Pues, el propio Robert C. Martin se encargó algunos años después (2020) en argumentar cómo se podía llegar a escribir código sencillo. Y para sorpresa de nadie, lo hizo en base a los propios principios SOLID que él mismo formuló allá por la década de los 90s del siglo (y milenio) pasado.

Veamos cómo lo hizo:

Single Responsibility Principle (Principio de Responsabilidad Única).

Uncle Bob comentaba que, independientemente de la aproximación arquitectural que se siguiera (monolito, microservicios, etc), la vigencia del principio era una realidad.

No queremos mezclar el código de las reglas de negocio con el de la interfaz gráfica de usuario. No queremos mezclar sentencias SQL con código relativo al protocolo de comunicación. Realizamos la separación del código que cambia por diferentes motivos para evitar que el cambio en uno pueda romper el otro.

Y es que hemos de recordar que el propio Robert C. Martin reformuló el principio varios años antes de la disertación de Dan North, de forma que:

[…] as you think about this principle, remember that the reasons for change are people. It is people who request changes. And you don’t want to confuse those people, or yourself, by mixing together the code that many different people care about for different reasons.

— The Single Responsibility Principle. The Clean Code Blog. Robert C. Marti. May 2014.

Y, si la alternativa al principio es escribir código simple, la aplicación de SRP es a lo que nos lleva.

Open Closed Principle (Principio Abierto Cerrado).

En este caso es muy tajante respecto a las apreciaciones de Dan North. Claro que queremos un código abierto a extensión pero cerrado a cambios. Lo queremos cuando planteamos un sistema que sea independiente del dispositivo (p.e. escribir ya sea en un fichero, en una impresora o en una pantalla).

Aunque el código pasara a ser erróneo debido a cambios de requerimiento, sólo lo sería parte del mismo, por lo que nos debemos asegurar que la parte correcta no se vea afectada y siga funcionando.

Y, como bien decía el propio Dan North, escribir código simple lleva a tener un código abierto y cerrado, por lo que el principio es de aplicación.

Liskov Substitution Principle (Principio de Substitución de Liskov).

El problema es la asunción de que el principio trataba sobre herencia, cuando en realidad es sobre subtipado. El principio trata sobre mantener las abstracciones nítidas y bien definidas. Es decir, que cada subtipo de un tipo base debe estar de acuerdo con el significado de este último y no romper con dicho significado, lo que nos llevaría a la proliferación de sentencias de toma de decisiones (if, switch, etc).

Y escribir código sencillo es escribir un código que mantiene las relaciones de subtipos nítidas, sin romper la significancia del tipo base.

Interface Segregation Principle (Principio de Segregación de Interfaz).

Según Robert C. Martin, independientemente del tipo de lenguaje usado, en mayor o menor medida, cuando un módulo A depende de un módulo B en tiempo de compilación, pero no en tiempo de ejecución, los cambios en el módulo B forzarán la compilación del módulo A, y su redespliegue.

Además, en la diapositiva de Dan North al respecto, se indicaba que los clientes no dependen de métodos que no usan, cosa que es falsa desde el momento en que cambios en dichos métodos provocan la recompilación y el redespliegue de dichos clientes.

CUPIDo y SOLIDos

Uncle Bob concluye que puede escribir código sencillo en el que se puede dividir una clase con dos interfaces en dos clases separadas, y sería entonces una buena idea hacerlo (aplicaríamos Single Responsibility Principle). Pero esa separación a menudo no es factible, ni siquiera deseable.

Dependency Inversion Principle (Principio de Inversión de Dependencia).

Para Robert C. Martin es difícil imaginar una arquitectura que no haga un uso significativo de este principio. Queremos aislar las abstracciones de alto nivel de los detalles de bajo nivel. Esa separación se logra administrando cuidadosamente las dependencias dentro del sistema para que todas las dependencias del código fuente, especialmente aquellas que cruzan límites arquitectónicos, apunten hacia abstracciones de alto nivel, no a detalles de bajo nivel. No queremos que nuestras reglas comerciales de alto nivel dependan de detalles de bajo nivel, ni queremos que los cálculos que nos generan dinero se contaminen con SQL, validaciones de bajo nivel o problemas de formato.

Concluía Uncle Bob que, aunque aquello de escribir código sencillo es un buen consejo, no otorga las disciplinas guiadas por los principios SOLID. Son dichos principios los que aportan la simplicidad, y las disciplinas las que dirigen hacia la producción de código sencillo. Por lo que la mejor manera de crear un problema es no orientar de modo alguno salvo con un escueto “escribir código sencillo”.

¿Qué opinión te merece lo que hemos visto? ¿Consideras que los principios SOLID siguen teniendo vigencia, o eres de los que creen que han envejecido mal? ¿Crees que hay alternativas a los principios SOLID? Pues, el propio Dan North proporcionó la suya. Las propiedades CUPID. Pero esto lo veremos en el siguiente post.

Webliografía:

CUPID—the back story – Dan North & Associates Limited

Why Every Element of SOLID is Wrong – Speaker Deck

Solid Relevance

SOLID – Wikipedia

Why Every Single Argument of Dan North is Wrong – Entropy Wins

Otros artículos relacionados

2024-02-14T08:46:12+01:0014 febrero, 2024|

¡Compártelo en tus redes sociales!

Ir a Arriba