Los Singletons son el mal reencarnado. El pecado original fueron las clases estáticas

No sé si alguno de vosotros ha tenido alguna vez fuertes discusiones con algun colega programador sobre este tema, yo sí. Demasiadas. ;P

Y es que siempre resulta difícil explicarle a alguien por qué algo que es cómodo es malo. En fin, qué le vamos a hacer pero la experiencia nos ha demostrado a todos, creo, que normalmente las mejores soluciones no son las más fáciles… y punto. Pero como el sentido común no es el más común de los sentidos me voy a explicar.

El patrón Singleton facilita dos cosas:

  1. Control sobre la instaciación de una clase. Sólo permite crear una instancia (Esto suena bien)
  2. Un punto de acceso global a la única instancia de esa clase y por tanto a toda su funcionalidad (Suena todavía mejor)

Pero que algo suene bien o todavía mejor no es un argumento para utilizar una técnica a diestro y siniestro. Sobre todo cuando no se piensa en todas las implicaciones de esa melodía que parece sonar tan bien.

Respecto a la primera “ventaja del patrón síngleton” no tengo casi nada que decir, precísamente es el propósito (único) del patrón de diseño. Y como patrón de diseño es bastante pobre, porque no resuelve muchos problemas habituales.

Paradójicamente, mucha gente argumenta a favor del singleton en diferentes foros porque cree que hay situaciones en las que es necesario que sólo haya una instancia de una clase. Ejemplos:

  • La clase que controla el acceso a la configuración de la aplicación
  • La clase que controla la conexión con un servicio php
  • La que controla el log de la aplicación

Y se puede añadir un largo etc. a la lista. Si te parece que estos ejemplos justifican la utilización de un singleton y trabajas en aplicaciones más complejas que una página web estática, te recomiendo que sigas leyendo porque creo que no le has prestado demasiada atención al tema.

¿Qué ocurrira si cuando has acabado el proyecto el cliente te pide que la configuración sea editable? Que necesitarás dos instancias de la clase de configuración, la activa y la que estás editando en esa marvillosa interfaz de configuración.

¿Qué ocurre si ese atractivo singleton que te has encargado de encapsular y hacer lo más abstracto posible para acceder de forma uniforme a un servicio, tiene que ser modificado para acceder a otro servicio con características diferentes? ¿Y si necesitamos que dos o más partes de la aplicación accedan a él de forma asíncrona e independiente?

¿Y si después necesitamos dos logs diferentes?

Las aplicaciones tienden a cambiar y cambian, no sólo durante el periodo de desarrollo. El cliente suele pedir funcionailidades en las que no había pensado al principio cuando ve el proyecto acabado. Podemos echarle la culpa por no haber pensado antes y ensartarle con un montón de argumentos para justificar por qué vamos a cobrarle casi lo mismo por hacer los cambios que por la aplicación original, pero

  1. Tendremos un cliente insatisfecho y con la sensación de que le hemos engañado
  2. Sufriremos amargamente refactorizando gran parte de nuestro código.

Probablemente me puedo saltar el primer punto porque todos sabemos que el cliente “nunca tiene razón” 😉

En cualquier caso, es el segundo punto el que me interesa. Además, que sorpresa, está conectado con, precisamente, la segunda supuesta ventaja del patrón singleton.

Si una variable, función, o instancia es accesible globalmente estamos haciendo algo muy mal, pero que muy mal. Y aquí estoy suponiendo que hablamos de aplicaciones medianas/grandes. Si lo hacemos en un banner o una aplicación con 4 o 5 clases sólo tiene el defecto de que estamos afianzando un mal hábito.

¿Por qué los objectos con acceso global son el mal? ¿De verdad necesitas que conteste a esta pregunta?

Pues ahí va: porque estamos hablando de programación orientada objetos, y uno de sus principios básicos es favorecer siempre que sea posible el acoplamiento débil entre clases, y en caso de que esto no sea posible, hacer que las dependencias entre clases sean lo más explícitas que nos sea posible, declarándolas en el constructor de la clase o en la firma de un método concreto en su interfaz.

Esto tiene su razón de ser en el hecho de que la programación orientada a objetos y, por extensión los patrones de diseño, son una solución para simplificar los cambios en una aplicación o la reutilización de parte de su código en un proyecto diferente.

Los objetos globales, y ejemplo más claro son las clases estáticas aunque seguidas muy de cerca por los síngletons, favorecen lo contrario.

Si se puede acceder a la instancia del logger o de la configuración desde cualquier función, desde cualquier clase, lo más probable es que uno acabe haciéndolo por comodidad, prisa, o por accidente. Todavía no he visto a nadie pasar la instancia de un singleton como argumento a un método para evitar que nuestra aplicación sufra en silencio las consecuencias de un diseño defectuoso. La consecuencia inmediata del abuso de esa melodía que sonaba bien es una cacofonía ensordecedora de clases que usarán en su implementación el singleton o la clase estática sin que se haya declarado explícitamente la dependencia en sus interfaces.

Resultado: Acoplamiento fuerte difícil de refactorizar. Si decidimos hacer cualquier cambio en nuestro singleton que afecte a la firma de sus métodos tendremos que buscar en todo el código, línea por línea y cambiar la llamada a esos métodos. Yo en la práctica me he encontrado más de una vez con la necesidad de rehacer buena parte de la aplicación por culpa de un singleton.

¿Qué hay de la posibilidad de reutilizar parte del código de una aplicación en otra que no haga prácticamente lo mismo? Si has utilizado singletons o clases estáticas es casi nula.

La pregunta que espero que el lector se haga llegados a este punto, visto que la segunda ventaja de un singleton, es más un problema que una solución, es: ¿Merece la pena restringir la instanciación de una clase?

Mi opinión es que no. Si necesitas sólo una instancia, crea sólo una instancia, pero no bloquees la posibilidad de crear dos, podrías necesitarlo en el futuro. Si quieres ahorrar recursos, habrás de tener cuidado para no hacerlo una segunda vez.

Concluyendo, el proposito de permitir una sóla instancia no supone una ventaja real en casi ningún caso, pero seguro que no compensa los efectos colaterales de tener algo muy parecido a una variable global.

El cliente va a pedir cambios, el cliente es impredecible, el cliente es el que paga, y le puedes pedir más por los cambios, pero no te pases si quieres que vuelva.

Y hablando de cambios, la primera regla que todos debiéramos seguir es: respeta tu código si quieres que sea flexible y respete tus necesidades de cambio.
Y la segunda es: Respeta al compañero que posiblemente tenga que hacer cambios, o añadir nuevas funciones, en la aplicación que has desarrollado.

Y por encima de todo lo demás, pásalo bien 🙂

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *