Gof – Singleton

Vamos a arrancar este 2012 retomando el tema de los patrones de diseño y en este primer post del año veremos el patrón de creación Singleton. La finalidad de Singleton es que solo se pueda crear una única instancia de una clase determinada. De esta forma nos aseguramos que nadie pueda crear instancias adicionales de la clase y que todos accedan a la misma instancia.

Veamos la definición de 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.

¿En qué casos nos puede resultar útil implementar Singleton? En clases que se encargan de controlar el acceso a un recurso único (imaginemos un archivo abierto en modo exclusivo) o cuando necesitamos tener disponible información que es única para el resto de los objetos.

Hecha la introducción, es hora de escribir código e implementar este patrón con C#. Para esto vamos a crear una nueva aplicación de Consola y agregar una clase llamada Singleton (sobre la cual aplicaremos el patrón).

El primer punto es hacer que la propia clase sea responsable de crear la única instancia de si misma. Para esto vamos a definir el constructor con el modificador private – para impedir que otros puedan crear instancias “desde afuera” – y marcar la clase como sealed – para evitar que se herede de la misma -.

Hagamos una pequeña pausa y veamos que nos dice MSDN de este último modificador:

El modificador sealed se puede aplicar a clases, métodos de instancia y propiedades. Una clase sealed no se puede heredar. Un método sellado reemplaza un método en una clase base, pero no se puede reemplazar también en una clase derivada. Cuando se aplica a un método o propiedad, el modificador sealed siempre se debe utilizar conoverride (Referencia de C#).

Bien, en este momento deberíamos tener la clase Singleton definida de la siguiente manera:

Singleton class

Singleton class

El proximo paso es permitir el acceso a la instancia mediante un método o propiedad de clase. Para esto vamos a agregar una propiedad de sólo lectura (la que llamaremos Instance) que sera responsable de crear y devolvernos la única instancia de la clase:

Instance property

Instance property

Como podemos ver, la primera vez que solicitamos una instancia de la clase, la propiedad Instance se encarga de crearla y asignarla a la propiedad privada instance. Finalmente se devuelve la instancia generada.

Para validar que solo existe un única instancia, vamos a escribir un simple test unitario en el cual crearemos dos objetos Singleton y los compararemos para confirmar si realmente son iguales:

Unit test

Unit test

Si ejecutamos el test, veremos que el mismo es correcto ya que ambos objetos apuntan a la misma y única instancia que se creó en memoria de la clase Singleton.

Test passed

Test passed

Pero esto tiene un pequeño problema, no soporta multi-thread. Al momento de validar si existe una instancia creada, lo hacemos preguntando si el valor de la propiedad instance es distinto de null. Si trabajamos en entornos multi-thread podría darse la situación en que múltiples hilos entren al mismo tiempo en dicho if, por lo que se podrían crear varias instancias de Singleton.

Esto podemos solucionarlo de una manera sencilla, generando un bloqueo (lock) dentro de Instance que nos permita sincronizar todos los accesos a la creación del objeto.

Ampliemos sobre lock consultando MSDN:

La instrucción lock permite garantizar que un subproceso no va a entrar en una sección crucial de código mientras otro subproceso ya se encuentre en ella. Si otro subproceso intenta entrar en un código bloqueado, esperará, o se bloqueará, hasta que el objeto se libere.

Lo que necesitamos para hacer esta validación, es crear un objeto del tipo Object que llamaremos sync y que utilizaremos en el bloqueo:

Sync property

Sync property

Si volvemos a correr el test, el mismo vuelve a pasar correctamente pero ahora con soporte multi-thread. 😉

Sin bien ya tenemos implementado el patrón Singleton en nuestra clase, veamos que hay una forma más simple de aplicarlo. Solo debemos definir la propiedad instance como readOnly. Este modificador nos va a permitir crear la instancia en la misma declaración de la propiedad evitando que se creen nuevas instancias luego.

Veamos de que se trata este modificador consultando una vez más MSDN:

La palabra clave readonly corresponde a un modificador que se puede utilizar en campos. Cuando una declaración de campo incluye un modificador readonly, las asignaciones a los campos que aparecen en la declaración sólo pueden tener lugar en la propia declaración o en un constructor de la misma clase.

Refactorizamos y obtendremos una nueva y más prolija clase Singleton 🙂

Refactorizando Singleton

Refactorizando Singleton

Cabe aclarar que el comportamiento es exactamente el mismo que el de la implementación del punto anterior.

Espero que les sea útil y tengan presente el patrón para, llegado el caso, decidan implementarlo.

Anuncios