Académique Documents
Professionnel Documents
Culture Documents
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.
El patrón singleton se implementa creando en nuestra clase un método que crea una
instancia del objeto sólo si todavía 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).
La instrumentación del patrón puede ser delicada en programas con múltiples hilos de
ejecución. Si dos hilos de ejecución intentan crear la instancia al mismo tiempo y esta
no existe todavía, sólo uno de ellos debe lograr crear el objeto. La solución clásica para
este problema es utilizar exclusión mutua en el método de creación de la clase que
implementa el patrón.
Las situaciones más habituales de aplicación de este patrón son aquellas en las que
dicha clase controla el acceso a un recurso físico único (como puede ser el ratón o un
archivo abierto en modo exclusivo) o cuando cierto tipo de datos debe estar disponible
para todos los demás objetos de la aplicación.
Contenido
[ocultar]
• 1 Ejemplo de implementación
o 1.1 Delphi
o 1.2 Java
o 1.3 C#
o 1.4 C++
o 1.5 Python
o 1.6 Visual Basic. NET
o 1.7 PHP5
o 1.8 Action Script 3
o 1.9 Javascript
• 2 Patrones relacionados
• 3 Enlaces externos
type
TSingleton = class
public
class function NewInstance: TObject; override;
procedure FreeInstance; override;
class function RefCount: Integer;
end;
var
Instance : TSingleton = nil;
Ref_Count : Integer = 0;
procedure TSingleton.FreeInstance;
begin
Dec( Ref_Count );
if ( Ref_Count = 0 ) then
begin
Instance := nil;
// Destroy private variables here
inherited FreeInstance;
end;
end;
[editar] Java
Para asegurar que se cumpla el requerimiento de "única instancia" del singleton; la clase
deberia producir un objeto no clonable:
Otra cuestión a tener en cuenta es que los métodos (o la clase) deberían ser declarados
como: final para que no puedan ser sobreescritos.
[editar] C#
// Clase de prueba
public class Prueba
{
private static void Main(string[] args)
{
//Singleton s0 = new Singleton(); //Error
Singleton s1 = Singleton.Instance;
Singleton s2 = Singleton.Instance;
if(s1==s2)
{
// Misma instancia
}
}
}
[editar] C++
[editar] Python
#Usage
mySingleton1 = Singleton()
mySingleton2 = Singleton()
class Singleton(type):
class A:
__metaclass__ = Singleton
# Definir aquí el resto de la interfaz
a1 = A()
a2 = A()
assert a1 is a2
Return _instancia
End Get
End Property
End Class
[editar] PHP5
<?php
class Ejemplo
{
// Contenedor Instancia de la Clase
private static $instance;
// EL metodo singleton
public static function singleton()
{
if (!isset(self::$instance)) {
$c = __CLASS__;
self::$instance = new $c;
}
return self::$instance;
}
// Clone no permitido
public function __clone()
{
trigger_error('Clone no se permite.',
E_USER_ERROR);
}
?>
[editar] Javascript
Lo primero que podemos pensar es que necesitamos una variable global, pero como
dice un profesor que conozco: “las variables globales apestan”, además, no aseguran
que no podamos instanciar más de un objeto de esa clase. La solución es hacer que sea
la propia clase la responsable de controlar la existencia de una única instancia. Existe un
patrón de diseño clásico que dicta este comportamiento: el singleton o instancia única.
No importa que libro o página web se mire, bajo toda definición de patrón que se
encuentre subyace la misma idea: un patrón es una solución a un problema de diseño no
trivial que es efectiva y reusable; efectiva porque ya ha resuelto un problema similar
anteriormente de manera satisfactoria y reusable porque se puede aplicar a muchos
problemas de diseño en diferentes circunstancias.
Trabajar con patrones es como jugar con un Mecano o un Lego. Cogemos piezas que
funcionan y sabemos lo que hacen, las adaptamos a nuestras necesidades y las juntamos.
Aunque elegir un patrón de diseño no siempre es tarea del programador - que muchas
veces se encuentran con la labor de traducir a código lo que otra persona ha diseñado -,
este debe saber como plasmar dicho patrón en su código. Normalmente la experiencia es
suficiente. Sin embargo nadie hace ascos a una ayudita. Y en eso estamos.
Parece sencillo. Más adelante veremos posibles extensiones que podemos aplicar al
patrón inicial.
Ahora que sabemos como es, vamos a intentar llevarlo al código de varias maneras
distintas.
Lo que vamos a intentar en primer lugar es coger el diseño UML propuesto y llevarlo a
Java directamente. Tenemos el código siguiente:
private Singleton() { }
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
/*
* Metodos del singleton.
*/
Para utilizar nuestro Singleton solo tenemos que solicitar la instancia única del mismo.
Habitualmente se hace de dos formas diferentes, la primera se usa en llamadas
puntuales.
Singleton.getsingleton().metodo();
La segunda se usa cuando vamos a invocar nuestro Singleton varias veces, evitando
llamadas innecesarias a métodos. Este tipo de singleton es un objeto como otro
cualquiera, así que puede ser referenciado sin problemas.
Singleton s = Singleton.getSingleton();
s.metodo();
Instanciación automática
Como ya dije, la condición del método singleton() puede ser eliminada. Dicha
condición se va a evaluar a cierto siempre salvo la primera vez. Además, el cargador de
clases de Java, cuando referenciamos una clase no cargada anteriormente, se ocupa de
cogerla de disco y cargarla en memoria inicializando sus miembros static.
Por tanto el siguiente código actúa exactamente igual que el caso 1, creando la instancia
en la primera invocación pero eliminando la condición.
private Singleton() { }
/*
* Metodos del singleton.
*/
Eliminar una condición puede ser una mejora pequeña o grande. Como siempre la
optimización debe hacerse por las partes del código que se ejecutan extensivamente.
3. No hay instancia
Java nos permite trabajar con clases sin necesidad de instanciarlas – posibilidad que
usamos en los casos anteriores para acceder al Singleton-. Esta nueva implementación
conserva la idea pero no su estructura, ya que aquí realmente no hay una instancia. El
Singleton es una clase con métodos y atributos estáticos y nunca llega a existir un
objeto.
static {
/*
* Inicializacion del singleton
*/
}
private Singleton() { }
/*
* Metodos del singleton.
*/
Con esa implementación, no podemos guardar referencias al objeto como en los casos
anteriores – porque el objeto no existe – así que las invocaciones a métodos serán todas
del tipo:
Singleton.metodo();
Alguna extensión
Como todo patrón que se precie, el singleton, puede extenderse. Puede resultar
interesante entre otras:
Controlando la concurrencia
static {
valor = 0;
}
private Singleton() { }
Ahora generaremos dos hilos uno cambiará el valor y el otro intentará leer.
t = new Thread() {
public void run() {
System.out.println(this.getName() + " intentando leer
valor");
System.out.println(this.getName() + " leido valor " +
Singleton.getValor());
}
};
t.start();
}
}
Esta idea, quizás, no parece tener mucho sentido con una variable entera - a no ser
que sea el balance de una cuenta corriente o algo parecido- . Pero si el obtener y el fijar
implicasen cambios estructurales en una lista enlazada, por ejemplo, sin proteger el
acceso concurrente podemos llegar a tener inconsistencias estructurales en la misma.
Se debe tener cuidado al serializar, pues se degrada el rendimiento. Debe buscarse el
máximo paralelismo con la mínima serialización. Si llegamos al punto en que no hay
trabajo en paralelo, es mejor hacer que la aplicación sea monothread.
Es ahora cuando después de explicar todo esto exponemos la verdad: los singletons
en Java no existen... Tras el shock inicial, pasemos a la explicación.
Antes mencioné a grandes rasgos como funciona un cargador de clases. Quedó claro
que nuestra clase Singleton se encargaba de mantener una única instancia. Sin
embargo, quién dice que solo existe un cargador de clases.
Este enfoque es habitual en aplicaciones web; entornos donde suele haber múltiples
cargadores de clases. Puede haber casos donde un servidor web nos de alguna
alternativa. Por ejemplo, Jakarta Tomcat proporciona una manera de compartir clases
entre cargadores, sin embargo, estas soluciones son desaconsejables, pues nada nos
asegura que en el futuro tengamos que cambiar nuestro entorno de ejecución,
invalidando estas “peculiares” soluciones.
El libro imprescindible
Si queréis saber más de este patrón de diseño clásico – y de muchos otros – debéis
remitiros a la Biblia, el libro del "Gang of four" – la banda de los cuatro - ...
Y hasta aquí hemos llegado. Espero que os resulte de utilidad. ¡Hasta el próximo
artículo!