Vous êtes sur la page 1sur 16

ingleton

De Wikipedia, la enciclopedia libre


Saltar a navegación, búsqueda
Para el término matemático, véase Conjunto unitario.

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.

El patrón singleton provee una única instancia global gracias a que:

• La propia clase es responsable de crear la única instancia.


• Permite el acceso global a dicha instancia mediante un método de clase.
• Declara el constructor de clase como privado para que no sea instanciable
directamente.

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

[editar] Ejemplo de implementación


[editar] Delphi

Ésta implementación ha sido sacada de [1] y está basada en la sobreescritura de los


métodos NewInstance y FreeInstance que se hereda de la clase TObject, la madre de
todos los objetos en Delphi.

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;

Y su implementación sería así:

procedure TSingleton.FreeInstance;
begin
Dec( Ref_Count );
if ( Ref_Count = 0 ) then
begin
Instance := nil;
// Destroy private variables here
inherited FreeInstance;
end;
end;

class function TSingleton.NewInstance: TObject;


begin
if ( not Assigned( Instance ) ) then
begin
Instance := inherited NewInstance;
// Initialize private variables here, like this:
// TSingleton(Result).Variable := Value;
end;
Result := Instance
Inc( Ref_Count );
end;

class function TSingleton.RefCount: Integer;


begin
Result := Ref_Count;
end;

[editar] Java

Una implementación correcta en el lenguaje de programación Java para programas


multi-hilo es la solución conocida como "inicialización bajo demanda" sugerida por Bill
Pugh:

public class Singleton {


private static Singleton INSTANCE = new Singleton();

// El constructor privado no permite que se genere un


constructor por defecto
// (con mismo modificador de acceso que la definicion
de la clase)
private Singleton() {}

public static Singleton getInstance() {


return INSTANCE;
}
}

Un ejemplo correcto de inicialización diferida. Se deja para comentar un error común


en Java al no tener en cuenta la sincronización de métodos.

public class Singleton {


private static Singleton INSTANCE = null;

// Private constructor suppresses


private Singleton() {}

// creador sincronizado para protegerse de posibles


problemas multi-hilo
// otra prueba para evitar instanciación múltiple
private synchronized static void createInstance() {
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}

public static Singleton getInstance() {


if (INSTANCE == null) createInstance();
return INSTANCE;
}
}

Para asegurar que se cumpla el requerimiento de "única instancia" del singleton; la clase
deberia producir un objeto no clonable:

//Así se podria clonar el objeto y no tendria unicidad.


SingletonObjectDemo clonedObject = (SingletonObjectDemo)
obj.clone();

Entonces, se deberia impedir la clonacion sobreescribiendo el metodo "clone" de la


siguiente manera:

//El metodo "clone" es sobreescrito por el siguiente que


arroja una excepción:
public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}

Ref: http://forums.sun.com/thread.jspa?threadID=785736, 28/07/2010.

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#

Un ejemplo correcto de inicialización diferida y segura en entornos multi-hilo en C#


sería:

public class Singleton


{
// Variable estática para la instancia, se necesita
utilizar una función lambda ya que el constructor es
privado
private static readonly Lazy<Singleton> instance = new
Lazy<Singleton>(() => new Singleton());

// Constructor privado para evitar la instanciación


directa
private Singleton()
{
}

// Propiedad para acceder a la instancia


public static Singleton Instance
{
get
{
return instance.Value;
}
}
}

// 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++

Una solución posible en C++ (conocida como el singleton de Meyers) en la cual el


singleton es un objeto local estático (notar que esta solución no es segura en programas
multi-hilo):

template<typename T> class Singleton


{
public:
static T& Instance()
{
static T laInstanciaSingleton; //asumir T posee
un constructor por defecto
return laInstanciaSingleton;
}
};

class SoloUno : public Singleton<SoloUno>


{
friend class Singleton<SoloUno>; //para dar acceso al
constructor privado de SoloUno
//..definir aquí el resto de la interfaz
};

[editar] Python

El siguiente es un ejemplo de implementación de Singleton en Python (tampoco es


segura en la programación multi-hilo)

class Singleton (object):


instance = None
def __new__(cls, *args, **kargs):
if cls.instance is None:
cls.instance = object.__new__(cls, *args,
**kargs)
return cls.instance

#Usage
mySingleton1 = Singleton()
mySingleton2 = Singleton()

#mySingleton1 y mySingleton2 son la misma instancia


assert mySingleton1 is mySingleton2

Y otra posibilidad interesante es implementarlo como una metaclase:

class Singleton(type):

def __init__(cls, name, bases, dct):


cls.__instance = None
type.__init__(cls, name, bases, dct)

def __call__(cls, *args, **kw):


if cls.__instance is None:
cls.__instance = type.__call__(cls, *args,**kw)
return cls.__instance

class A:
__metaclass__ = Singleton
# Definir aquí el resto de la interfaz

a1 = A()
a2 = A()

assert a1 is a2

[editar] Visual Basic. NET

Una implementación del patrón singleton en Visual Basic. NET es la siguiente:

Public Class Singleton

Private Sub New()


End Sub

Private Shared _instancia As Singleton

Public Shared ReadOnly Property Instancia() As


Singleton
Get
If _instancia Is Nothing Then
_instancia = New Singleton
End If

Return _instancia
End Get
End Property

End Class

[editar] PHP5

Una implementación del patrón singleton en PHP5 es la siguiente:

<?php
class Ejemplo
{
// Contenedor Instancia de la Clase
private static $instance;

// A private constructor; previene creacion de objetos


via new
private function __construct()
{
echo 'Soy el constructor';
}

// 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] Action Script 3

Una implementación del patrón singleton en Action Script 3 es la siguiente:


public class Singleton{
private static var instance:Singleton;
private static var allowInstance:Boolean;
public function Singleton(){
if(!allowInstance){
throw new Error("Debes usar getInstance()");
}else{
trace("Se inicializó una instancia de Singleton");
}
}

public static function getInstance():Singleton{


if(instance==null){
allowInstance=true;
instance= new Singleton();
allowInstance=false;
}else{
trace("Se regresa la instancia existente");
}
return instance;
}
}

[editar] Javascript

Una implementación del patrón singleton en Javascript es la siguiente:

Singleton = function Singleton$constructor() {


return {
getInstance : function Singleton$getInstance() {
return this;
}
};
}();

[editar] Patrones relacionados


EXTRA---------------------------------------
Muchas veces, en nuestras aplicaciones necesitamos acceder fácilmente a un objeto
único, la instancia única de cierta clase: un gestor de impresoras, un pool de conexiones
a una base de datos, un gestor de parámetros de configuración, etc.

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.

¿Qué es un patrón de diseño?

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.

¿Qué vamos a ver?

A lo largo de este artículo intentaremos explicar en que se basa el patrón singleton e


implementarlo de varias maneras. Comentaremos además extensiones, y ciertas
peculiaridades que podemos encontrarnos si lo usamos.

Estructura, qué es un singleton

Con un diagrama UML, un singleton es algo tan simple como lo siguiente:


Podemos observar dos características básicas:

Restricción de acceso al constructor...

Con esto conseguimos que sea imposible crear nuevas


instancias.Solo la propia clase puede crear la instancia.

Mecanismo de acceso a la instancia...

El acceso a la instancia única se hace a través de un único punto


bien definido, que es gestionado por la propia clase y que puede ser
accedido desde cualquier parte del código – en principio -

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.

1. Instanciación bajo demanda

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:

public class Singleton {

static private Singleton singleton = null;

private Singleton() { }

static public Singleton getSingleton() {

if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}

/*
* Metodos del singleton.
*/

public String metodo() {


return "Singleton instanciado bajo demanda";
}

Como reza el título de esta subsección, esta implementación se caracteriza porque


nuestra instancia única se crea cuando se usa por primera vez – bajo demanda – gracias
a una sentencia condicional. En el siguiente punto eliminaremos esto, no por ser
incorrecto, sino porque en general no es necesario.

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.

public class Singleton {

static private Singleton singleton = new Singleton();

private Singleton() { }

static public Singleton getSingleton() {


return singleton;
}

/*
* Metodos del singleton.
*/

public String metodo() {


return "Singleton ya instanciado";
}

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.

La manera de acceder al Singleton es la misma que en el caso 1, por supuesto.

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.

public class Singleton {

static {
/*
* Inicializacion del singleton
*/
}

private Singleton() { }

/*
* Metodos del singleton.
*/

public static String metodo() {


return "Singleton estatico";
}

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();

Una desventaja evidente es la necesidad de definir todos los métodos y atributos


como static. Unas líneas más abajo comentaremos una posible extension del patrón
básico para la cual esta implementación no es útil - a la hora de crear jerarquías de
singletons -.

¿Qué usar?... En el fondo es una cuestión de estilo, gusto o de necesidades. Las


soluciones propuestas son validas en muchos ámbitos.

Alguna extensión

Como todo patrón que se precie, el singleton, puede extenderse. Puede resultar
interesante entre otras:

• Que haya un número variable de instancias, seguimos controlando la cantidad,


pero esta es mayor que uno.
• Extender el singleton, siendo la superclase la que decida que instancia de una
subclase debe ser devuelta, como puede verse en el diagrama.

Controlando la concurrencia

Hemos encontrado un mecanismo para tener una única instancia de un objeto y


acceder a ella. Ahora bien, qué pasa en una aplicación multihilo, donde varios threads
acceden simultáneamente al mismo espacio de memoria.

Presumiblemente podremos cometer errores debido a inconsistencias de datos – dirty


reads, escrituras fantasma, etc - . Por ello es necesario controlar el acceso concurrente,
sincronizando los threads en ejecución.

La sincronización de threads en Java es capítulo aparte y por eso no nos extenderemos


más. Únicamente sincronizaremos un Singleton y veremos como actúa. Este
Singleton serializa las lecturas y escrituras de una variable. Para mostrar el bloqueo,
asumiremos que la escritura tarda unos segundos – durmiendo al thread que escriba – y
que no queremos que durante este proceso pueda leerse el valor.

public class Singleton {

static int valor;

static {
valor = 0;
}
private Singleton() { }

public static synchronized String getValor() {


return Integer.toString(valor);
}

public static synchronized void setValor(int nuevoValor) {

System.out.println(Thread.currentThread().getName() + " toma


el bloqueo");
valor = nuevoValor;
try {
Thread.sleep(2000);
} catch (InterruptedException ie) { }

System.out.println(Thread.currentThread().getName() + " suelta


el bloqueo");
}
}

Ahora generaremos dos hilos uno cambiará el valor y el otro intentará leer.

public class Main {

public static void main(String[] args) {

Thread t = new Thread() {


public void run() {
Singleton.setValor(25);
}
};
t.start();

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();
}
}

Si ejecutamos obtenemos la siguiente salida:

Thread-0 toma el bloqueo


Thread-1 intentando leer valor
Thread-0 suelta el bloqueo
Thread-1 leido valor 25

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.

El cargador de clases y la mentira a medias

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.

De existir varios cargadores de clases, podría existir un singleton diferente en cada


uno pero único en el contexto de ese cargador. Cada cargador es independiente del
resto.

El lector no debería preocuparse realmente. Todo es cuestión de ser cuidadosos. Si


estamos seguros de que solo hay un cargador de clases – como en una aplicación
standalone común – trabajamos como hasta ahora.

Si existen varios cargadores de clases pueden suceder dos cosas. Si nuestro


Singleton se usa solo para lectura de datos, no hay problema. Si lo usamos para
escritura de datos debemos tener en cuenta que se pueden producir inconsistencias.
Montar un “protocolo de coherencia entre singletons” es inviable – a la par que absurdo
-. Lo normal en estos casos es centralizar la escritura, por ejemplo en una base de datos.

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 - ...

Design Patterns. Elements of Reusable Object-


Oriented Software.
E.Gamma, R.Helm, R.Johnson, J. Vlissides
Addison Wesley

Aunque el código de este libro es C++ y Smalltalk realmente es muy


sencillo seguirlo y resulta una lectura imprescindible para todo programador
OO.
También os pueden resultar interesantes los siguientes libros, ya aplicados
a Java...

Patterns in Java, a catalog of reusable design patterns


illustrated with UML. Volúmenes 1 y 2
M. Grand
John Wiley & sons

Y hasta aquí hemos llegado. Espero que os resulte de utilidad. ¡Hasta el próximo
artículo!

Vous aimerez peut-être aussi