Introducción
Los seguidores más frecuentes de este blog ya conocerán mi predilección hacia los patrones de diseño como herramientas para afrontar aquellos problemas más comunes que se nos presentan a diario.
A estas alturas, hemos revisado ya el patrón del módulo (incluyendo su versión revelada), el prototipado, algún que otro facade y otras construcciones más complejas que pueden prestarse como tales. En esta ocasión, vamos a repasar una estructura muy conocida para aquellos desarrolladores que provienen de lenguajes como Java y que tiene por nombre singleton.
Patrón Singleton
Según la siempre sabia Wikipedia, el patrón de diseño singleton (instancia única) está diseñado para restringir la creación de objetos pertenecientes a una clase o el valor de un tipo a un único objeto. Su intención consiste en garantizar que una clase sólo tenga una instancia y proporcionar un punto de acceso global a ella.
Este tipo de patrón se implementa creando en nuestra clase un método que crea a su vez una instancia del objeto sólo si añun no existe alguna. Para asegurar que la clase no puede ser instanciada nuevamente se regula el alcance del constructor (con atributos como protegido o privado).
En lenguajes como el mencionado Java, no se puede crear un objeto directamente, sino que se necesita una clase para hacerlo; es cierto que existen excepciones pero para ilustrar este artículo las pasaremos por alto.
En definitiva, con el patrón singleton, se prentende que la clase corresponda con una única instancia estableciéndose una relación uno-a-uno entre ambas.
A continuación se muestra un ejemplo de este esquema en Java:
public class Singleton { public static final Singleton INSTANCE = new Singleton(); // Private constructor prevents external instantiation private Singleton() { } public void amethod() { // ... } } |
El singleton en Javascript
Javascript permite la creación directa de objetos. De hecho, es de los pocos lenguajes de programación que poseen esta característica, por lo que un esquema de este tipo no es estrictamente necesario. Obsérvese el siguiente ejemplo:
Nota: ejemplos gracias a Axel Rauschmayer (@rauschma )
var namespace = { singleton: { amethod: function() { console.log("amethod"); } } }; // Invoke: namespace.singleton.amethod() |
El contexto creado con namespace no es imprescindible, pero si una buena práctica para aislar el objeto. Por lo demás, como vemos, las características de Javascript hacen que este esquema no tenga una portabilidad demasiado útil. De hecho, tiene muchas similitudes con el esquema del módulo que mencionamos antes y sus expansiones.
Si necesitamos crear el singleton bajo demanda (lo que conocemos generalmente con el término lazily), el código se complica un poco:
var namespace = { _singleton: null, getSingleton: function() { if (!this._singleton) { this._singleton = { amethod: function() { console.log("amethod"); } } } return this._singleton; } }; // Invoke: namespace.getSingleton().amethod() |
A simple vista, el código es más complejo de lo que debería. En navegadores recientes, gracias a la nueva especificación ECMAScript 5, este ejemplo podría reducirse considerablemente a través del uso del método getter.
var namespace = { _singleton: null, get singleton() { if (!this._singleton) { this._singleton = { amethod: function() { console.log("amethod"); } } } return this._singleton; } }; // Invoke: namespace.singleton.amethod() |
Consideraciones sobre seguridad
Las soluciones presentadas arriba pueden parecer demasiado simples, pero son son relativamente fáciles de entender y con un funcionamiento obvio. Sin embargo, en determinados escenarios, es posible que sea necesario mejorar la estructura anterior para ofrecer funcionalidades semejantes a las que encontramos en otros lenguajes.
Algunos de estos requisitos exigibles podrían ser los siguientes:
- Prevenir la creación de copias: los que provienen de Java, tienen una conciencia especial sobre este aspecto y es posible conseguirlo escondiendo el constructor.
- Prevenir que el patrón pueda ser reemplazado: un atacante puede tratar de cambiar el singleton de una aplicación por otro implementado por él mismo. Mediante los nuevos atributos ECMAScript 5 de solo lectura (read-only) y de no-configuración (non-configurable), se puede conseguir fácilmente:
- Prevenir que el singleton pueda ser modificado: Los métodos methodsObject.preventExtensions(), Object.seal(), y Object.freeze() nos pueden permitir esa granularidad deseada.
- Mantener los datos privados: De nuevo, son los programadores Java los que más buscarán este comportamiento. Conseguirlo es sencillo: basta con crear una función autoejecutable que envuelva al singleton para esconder sus variables al exterior:
Object.defineProperty( namespace, "singleton", { writable: false, configurable: false, value: { ... } } ); |
var namespace = { getSingleton: (function() { // BEGIN iife var singleton; return function() { if (!singleton) { singleton = { amethod: function() { console.log("amethod"); } } } return singleton; }; }()) // END iife }; // Invoke: namespace.getSingleton().amethod() |
Conclusión
Aunque debido a las características de Javascript un patrón singleton no es estrictamente necesario, ni especialmente útil, los desarrolladores que provienen de otros lenguajes como Java se encontrarán mucho más cómodos con una estructura que les resulta muy familiar. Mediante algunos artificios y giros del lenguaje, podemos conseguir una funcionalidad similar a algo que, reitero una vez más, no tiene mayor razón de ser: debería quedar únicamente como un juego o ejercicio de estructura POO antes que como un código eficaz.
A tal respecto, en el libro Essential JAvaScript & jQuery Design Patterns, Addy Osmani concluye con que este patrón, tal y como lo hemos tratado anteriormente, puede resultar útil únicamente cuando necesitamos que un objeto concreto y exacto coordine otras implementaciones de patrones a lo largo de un sistema.
En mi opinión, podemos prescindir de esta estructura sin miedo.
Más información
Axel Rauschmayer, The Singleton Pattern in JavaScript: not needed
Kai Jäger, The Singleton Design Pattern in JavaScript
Robert Nyman, Getters And Setters With JavaScript
Despues de leer el artículo, no entiendo como se llega a la conclusión de que el patrón Singleton resulta inútil en Javascript. El patrón es un concepto de diseño que tiene utilidad independientemente del lenguaje de implementación que se utilice. No me ha quedado claro entonces porqué influye las peculiaridades de Javascript en su uso. Además, bien implementado, el patrón SIngleton en Javascript puede utilizarse para compartir objetos únicos entre diferentes contextos (iFrames) de javascript, lo cual es bastante útil.
En definitiva, que no me ha quedado clara la exposición, se agradecerían más aclaraciones.
Un saludo.
Si; quizás no han quedado claras las conclusiones.
El singleton no tiene mucha razón de ser en Javascript por el hecho de que el fin mismo de esta estructura es la de crear objetos en aquellos lenguajes en los que no pueden definirse de un modo directo sino a través de clases.
En Javascript, los objetos responden a un modelo declarativo directo: no son necesarios intermediarios. Así, tenemos que el siguiente código, es perfectamente válido:
De hecho, al existir en Javascript un constructor directo de objetos (new), aún parece menos necesario el recurrir a otras estructuras:
Estrictamente hablando, si este patrón tiene como fin garantizar que una clase sólo tenga una instancia (en Javascript, ante la ausencia de clases hablaríamos de objetos directamente) y proporcionar un punto de acceso global a ella (él), nos encontramos con que se corresponde, efectivamente, con la lógica interna del lenguaje: sería el comportamiento por defecto del constructor new.
Si además, queremos aportar valores como visibilidad o atributos relacionados con la seguridad (inmodificable, irremplazable, etc), el esquema va derivándose a lo que conocemos en Javascript como módulos:
La estructura es idéntica: al forzar la portabilidad, nos encontramos con que estamos creando en realidad otro patrón diferente que, a su vez, resulta mucho más flexible dada todas sus cualidades y capacidad para vertebrar una arquitectura compleja en JS.
Es por todo que, en su versión más ortodoxa, el singleton no tiene demasiado sentido en Javascript: este lenguaje no requiere de intermediarios para crear objetos.
Cualquier agregación posterior, da como resultado un patrón diferente y definido.
Saludos!
Ok, entonces entiendo que intentas decir que las cualidades del SIngleton están intrinsecamente representadas en el lenguaje, lo cual indica no que el singleton no sea útil, sino que es implementable directamente usando la sintaxis de creación de objetos propia de javascript.
Aun así nosotros utilizamos por ejemplo una clase síngleton para tener objetos únicos compartidos entre varios iFrames de una misma aplicación web compleja, que sin un mecanismos intermediario no creo que sea posible en javascript. Esa parte me parece interesante debatirla, pot ver si encaja efectivamente dentro del patrón singleton o se asocia más bien a otro patrón de diseño que desconozco.
Un saludo y gracias por las aclaraciones!
Exactamente Ángel;
el argumento acerca de la inutilidad del Singleton viene de que el objetivo que se busca con él, es parte de la lógica natural del lenguaje y que, por tanto, es un patrón que no aporta nada. Obviamente en aquellos otros basado en los conceptos de clases, este patrón funciona perfectamente.
Me gustaría conocer un poco más a fondo cómo lo utilizáis en el caso que comentas de los iframes y la estructura con la que lo implementáis. Parece un escenario interesante.
Saludos!
Ángel. En realidad, tienes que dejar de pensar en un patrón de diseño como solo una solución. Un patrón es la conjunción de 3 cosas:
– Un problema
– Un entorno en el que ocurre ese problema
– Una solución conocida y aceptada como «buena»
Así que no, el patrón no es un concepto independiente del lenguaje que se use. Son muy diferentes los patrones en un entorno orientado a objetos que en uno funcional, por poner un ejemplo.
El patrón Singleton es así:
En un lenguaje OO basado en clases (entorno), queremos limitar la creación de objetos de modo que de una cierta clase sólo pueda existir una única instancia (problema). Para conseguirlo, hacemos que su constructor no esté directamente accesible (solución).
En Javascript no hay clases, y el lenguaje soporta la creación directa de objetos sin usar un generador. De modo que no es que sea inútil, es que **no tiene sentido** hablar de una instancia única de cierta clase.
disculpa si me paso de ignorante con la pregunta, pero es que el resultado del singleton en javascript se me asemeja mas a una clase estatica…puedes acceder a los metodos sin instanciar el objeto…es un error de percepción mio o exist alguna diferencia?…gracias!!!
by the way..soy el mismo del comentario anterior y soy otro angel que el de los comentarios anteriores..:D…si los hubiese leido antes me habria puesto otro nombre para no confundir…..
Vamos a ver, vamos a ver … , si creo dos objetos distintos mediante ‘new’ de la misma ‘función constructora’ tendré dos objetos ocupando memoria … si los creo utilizando el patrón Singleton sólo existirá un objeto en memoria …
¿ Y si quiero utilizar un único y exlcusivo objeto en diferente sitios, iframes o scopes sin ni siquiera arrastrar las referencias (ose el nombre de las variables que los apuntan) pero manteniendo todo su estado interno en memoria ? Pues entonces utilizo el patrón Singleton …
CREO QUE TIENE RAZÓN ÁNGEL, y que este patrón puede tener sentido cuando se trata con objetos SUPER padres que albergan lógica de programación en distintos apartados de una aplicación javascript, incluso en distintos archivos …
Vamos, no lo he utilizado fuera de Java y PHP, pero presupongo que también se podrá utilizar en proyectos Javascript sobre todo de cierta envergadura, donde sea preferible no arrastrar esas referencias (variables) a objetos creados.
Si se asemeja a un patrón Módulo no lo sé, quizás sean la misma cosa con distinto nombre, o quizás le falte al Módulo implementar un método ‘getInstance()’..
Lo que en resumen quiero decir es que creo que es una forma de ahorrar recursos (memoria) con SUPER objetos frecuentemente utilizados en una gran aplicación, y a la vez aislar la posibilidad de recrearlo sucesivamente.