Aunque parece fácil escribir una clase genérica que envuelva la funcionalidad del Patrón singleton, no es tan fácil porque se presentan una serie de problemas que si bien no son importantes (si es uno mismo el que usa la clase y tiene cuidado), la realidad es que no quedan ‘redondas’ por lo que en este articulo se estudia y desarrolla una técnica que permite resolver esos problemas y poder usar la clase genérica con el patrón Singleton de una forma natural
En este documento no se trata nada de los problemas de concurrencia que existen al usar el patrón. Ese es un problema (que como dice Michael Ende en su Historia Interminable) será contado en otra ocasión. En este documento solamente se estudia el problema de la construcción de una clase base genérica que implemente el patrón Singleton
A poco que se haya trabajado en programación sabemos que el patrón Singleton es un patrón de comportamiento básico, que se utiliza frecuentemente, lo que implica repetir en varios objetos el código necesario para usar el patrón.
Una de las reglas básicas en programación es no repetir código. Por esta razón, he creado una clase base que usando genéricos permita usar el patrón Singleton simplemente heredando la clase base.
La primera restricción de la clase son los calificadores de la clase base
Public MustInherit Class SingletonBaseGenerica(Of T As {Class})
El Modelo básico de la clase base genérica Singleton será el siguiente:
Public MustInherit Class SingletonBaseGenerica(Of T As {Class}) Private Shared _SingletonObjetoInterno As T Public Function GetInstance() As T If (_SingletonObjetoInterno Is Nothing) Then _SingletonObjetoInterno = New T End If Return _SingletonObjetoInterno End Get End Function End Class
Aunque no lo parece, al empezar a usarla aparece un problema en la propiedad [GetInstance], y consiste en la función necesita acceder al constructor de la clase hija. (observa: _SingletonObjetoInterno = New T ) Para ello, ese constructor (necesariamente) tiene que ser público.
Esto significa que el código anterior tiene un problema importante, por dos razones, la primera es que un objeto Singleton no debe tener un constructor de acceso público, y la segunda es que la clase hija, puede instanciarse usando ese constructor y entonces este ejercicio no sirve para nada
Podríamos pensar que la solución es fácil. Hacemos el constructor de la clase Hija [Private] y asunto resuelto ¿no?... pues no, lo que ocurre entonces es que en la línea
_SingletonObjetoInterno = New T
Se genera un error porque no se puede localizar el constructor de la clase (T)
Resumiendo… Tenemos que encontrar una forma de que la clase Base Genérica que estamos creando pueda usar un constructor privado de la clase Hija y al solución está en usar reflexión
El código que usaremos será el siguiente:
Public Function GetInstance() As T '------------------- If (_SingletonObjetoInterno Is Nothing) Then Try '----------------------------------------------------- ' Utilizar reflexión para localizar el ' constructor de la clase (Of T) Dim constructor As Reflection.ConstructorInfo = Nothing '----------------------------------------------------- ' Utilizar Binding flags para excluir constructores públicos. ' BindingFlags (Enumeración) Especifica los marcadores que ' controlan el enlace y la manera en que se realiza ' la búsqueda de miembros y tipos por reflexión. ' Instance Indica que se deben incluir los miembros ' de instancia en la búsqueda. ' NonPublic Indica que se deben incluir los miembros ' no públicos en la búsqueda. constructor = GetType(T).GetConstructor( _ (Reflection.BindingFlags.Instance Or _ Reflection.BindingFlags.NonPublic), _ Nothing, _ Type.EmptyTypes, _ Nothing) '----------------------------------------------------- ' * si el constructor es Nothing o bien ' * el constructor es visible como mucho para otros tipos del ' mismo ensamblado y no es visible para los tipos derivados ' fuera del ensamblado. es decir es private o protected, '----------------------------------------------------- If ((constructor Is Nothing) OrElse _ constructor.IsAssembly) Then ' componer el mensaje de error Dim mensajeError As String mensajeError = String.Format( _ "No se encuentra un constructor 'private' o " & _ " 'Protected' para el objeto '{0}'.", _ GetType(T).Name) ' Escribir en el LOG My.Application.Log.WriteEntry( _ mensajeError, TraceEventType.Error) ' disparar exception Throw New Exception(mensajeError) End If '----------------------------------------------------- ' si no existe (todavia) la instancia de esta clase ' crearla usando el constructor (private o Protected) ' obtenido por reflexion If _SingletonObjetoInterno Is Nothing Then _SingletonObjetoInterno = CType(constructor.Invoke(Nothing), T) End If Catch exception As Exception Throw End Try End If ' (_SingletonObjetoInterno Is Nothing) '----------------------------------------------------- ' Devolver el valor de la instancia Return _SingletonObjetoInterno End Function
De esta forma resolvemos el problema... por una parte tenemos nuestra clase base que implementa la lógica del patrón singleton, y por otra obligamos a que las cases que vayan a heredar de esta clase base no tengan ningún constructor publico, lo que permite cumplir con la restricción básica del patrón: las clases Singleton no deben tener un constructor de acceso público. Además… si no se cumple esa restricción, se dispara un error en tiempo de ejecución lo que impide cualquier error involuntario.
En el fichero adjunto encontraras una clase genérica para el Patrón Singleton que resuelve el problema de los entornos multihilo
Código MD5 para los paranoicos
© 1997 - - La Güeb de Joaquín | |||||
Joaquín Medina Serrano
|
|||||
|
Codificación | |
Fecha de creación | |
Última actualización |