Singleton
En ingeniería de software, singleton o instancia única es un patrón de diseño que restringe la creación a un único objeto la creación de objetos pertenecientes a una clase y asegura de que sólo haya esta instancia única.
Además de garantizar que una clase solo tenga una instancia, 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 solo si todavía no existe alguna. Para asegurar que la clase no puede ser instanciada nuevamente se regula el alcance del constructor (con modificadores de acceso 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, solo 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.
Los críticos consideran al singleton como un anti-patrón utilizando en escenarios donde no es beneficioso, introduce restricciones innecesarias donde una única instancia de una clase no es realmente requerida y agrega un estado global en la aplicación.[1][2]
El patrón singleton provee una única instancia global gracias a que:
- La propia clase es responsable de crear la única instancia. Por medio de su método constructor.
- 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.
- Al estar internamente autoreferenciada, en lenguajes como Java, el recolector de basura no actúa.
Ejemplo de implementación
editarActionScript 3
editarUna implementación del patrón singleton en ActionScript 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;
}
}
Autoit
editarUna implementación del patrón singleton en Autoit es la siguiente:
#include <Misc.au3>
if _Singleton("test",1) = 0 Then
Msgbox(0,"Warning","An occurence of test is already running")
Exit
EndIf
Msgbox(0,"OK","the first occurence of test is running")
C#
editarUn 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
}
}
}
C++
editarUna 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 <class T>
class Singleton
{
public:
static T& Instance()
{
static T laInstanciaSingleton; //asumir T posee un constructor por defecto
return laInstanciaSingleton;
}
};
class Singleton : public Singleton<Singleton>
{
friend class Singleton<Singleton>; //para dar acceso al constructor privado de SoloUno
//..definir aquí el resto de la interfaz
};
D
editarUna posible implementación en D sería:
final class Singleton {
private static Singleton instance;
static this() {
instance = new Singleton();
}
static Singleton opCall() {
return instance;
}
}
auto s1 = Singleton();
auto s2 = Singleton();
assert(s1 == s2);
Delphi
editarEsta 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 Embarcadero 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;
Java
editarLa implementación más usual en el lenguaje de programación Java es la siguiente:
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
// El constructor privado no permite que se genere un constructor por defecto.
// (con mismo modificador de acceso que la definición 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;
}
}
Si queremos reducir el coste de la sincronización, se realiza la comprobación de la instancia antes de invocar el método "createInstance"; también es posible hacerlo en un único método de la siguiente manera [2]:
private static void createInstance() {
if (INSTANCE == null) {
// Solo se accede a la zona sincronizada
// cuando la instancia no está creada
synchronized(Singleton.class) {
// En la zona sincronizada sería necesario volver
// a comprobar que no se ha creado la instancia
if (INSTANCE == null) {
INSTANCE = new Singleton();
}
}
}
}
Para asegurar que se cumpla el requerimiento de "única instancia" del singleton; la clase debería producir un objeto no clonable:
//Así se podría clonar el objeto y no tendría unicidad.
SingletonObjectDemo clonedObject = (SingletonObjectDemo) obj.clone();
Entonces, se debería impedir la clonación sobreescribiendo el método "clone" de la siguiente manera:
//El método "clone" es sobreescrito por el siguiente que arroja una excepción:
public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
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.
Existe otra implementación menos conocida, pero con mayores ventajas dentro de Java que es la siguiente:
public enum SingletonEnum {
INSTANCE;
int value;
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
Este enfoque es thread-safe y serializable, garantizado por la implementación de enum.
Ejemplo de uso:
public class EnumDemo {
public static void main(String[] args) {
SingletonEnum singleton = SingletonEnum.INSTANCE;
System.out.println(singleton.getValue());
singleton.setValue(2);
System.out.println(singleton.getValue());
}
}
Javascript
editarUna implementación del patrón singleton en Javascript es la siguiente:
Singleton = function Singleton$constructor() {
return {
getInstance : function Singleton$getInstance() {
return this;
}
};
}();
Objective C
editarUna sencilla implementación del patrón Singleton para el lenguaje Objective C es:
#import "Singleton.h"
@implementation Singleton
+(Singleton *) getInstance{
static Singleton* singleton = nil;
@synchronized(self){
if (!singleton) {
singleton = [self new];
}
}
return singleton;
}
@end
Otra forma mejor utilizando GCD es:
+ (id)sharedInstance
{
static dispatch_once_t pred = 0;
__strong static id _sharedObject = nil;
dispatch_once(&pred, ^{
_sharedObject = [[self alloc] init];
});
return _sharedObject;
}
Perl
editarComo es habitual en Perl, hay más de un forma de hacerlo. De entre varias posibilidades,[3] podemos señalar esta:
[Singleton.pm]
package Singleton;
my $singleton;
sub new {
my $class = shift;
$singleton ||= bless {}, $class;
}
...
[foo.pl]
require Singleton;
my $object1 = new Singleton;
my $object2 = new Singleton; # El mismo objeto
PHP
editarUn ejemplo de implementación del patrón singleton en PHP sería la siguiente:
<?php
class Singleton
{
// Contenedor Instancia de la clase
private static $instance = null;
// Constructor privado, previene la creación de instancias vía new
private function __construct() { }
// Clonación no permitida
private function __clone() { }
// Método singleton
public static function getInstance()
{
if (null === self::$instance) {
self::$instance = new Singleton();
}
return self::$instance;
}
}
Python
editarEl 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
Visual Basic. NET
editarUna implementación del patrón singleton en Visual Basic .NET es la siguiente:
Public Class Singleton
Private Sub New() 'CONSTRUCTOR
End Sub
Private Shared instancia As Singleton = Nothing
Public Shared Function getInstancia As Singleton
If instancia Is Nothing Then
instancia = New Singleton()
End If
Return instancia
End Function
End Class
Patrones relacionados
editar- Abstract Factory: muchas veces son implementados mediante singleton, ya que normalmente deben ser accesibles públicamente y debe haber una única instancia que controle la creación de objetos.
- Monostate: es similar al singleton, pero en lugar de controlar el instanciado de una clase, asegura que todas las instancias tengan un estado común, haciendo que todos sus miembros sean de clase.
Enlaces externos
editar- Wikilibros alberga un libro o manual sobre Patrones de Comportamiento.
- Patrón Singleton: explicación y modelo UML
- A Q&A page (java.sun.com)
- PEC(TM) (enlace roto disponible en Internet Archive; véase el historial, la primera versión y la última)., compilador que garantiza la correcta instrumentación del patrón singleton y de otros patrones.
- Data&Object Factory, explicación e implementación en C# del patrón singleton.
- Patrón Singleton explicado en video, artículo de Lucas Ontivero donde explica mediante tres videos de 8 minutos 3 implementaciones del patrón Singleton en CSharp (nivel básico)
Referencias
editar- ↑ «Singleton design pattern». HowToDoInJava (en inglés estadounidense). 22 de octubre de 2012. Consultado el 10 de julio de 2020.
- ↑ Contieri, Maximiliano (11 de julio de 2020). «Singleton: El Patrón del mal». Medium (en inglés). Consultado el 11 de julio de 2020.
- ↑ Cunningham & Cunningham, Inc. (ed.). «Perl Singleton» (en inglés). Consultado el 4 de septiembre de 2012.