En ingeniería de software , el patrón singleton es un patrón de diseño de software que restringe la instanciación de una clase a una instancia "única". Esto es útil cuando se necesita exactamente un objeto para coordinar acciones en todo el sistema. El término proviene del concepto matemático de singleton .
Los críticos consideran que el singleton es un anti-patrón ya que se usa con frecuencia en escenarios donde no es beneficioso, introduce restricciones innecesarias en situaciones en las que no se requiere una instancia única de una clase e introduce un estado global en una aplicación. [1] [2] [3] [4]
Descripción general
El patrón de diseño singleton [5] es uno de los veintitrés patrones de diseño conocidos "Gang of Four" que describen cómo resolver problemas de diseño recurrentes para diseñar software orientado a objetos flexible y reutilizable, es decir, objetos que son más fáciles de manipular. implementar, cambiar, probar y reutilizar.
El patrón de diseño singleton resuelve problemas como: [6]
- ¿Cómo se puede garantizar que una clase tenga solo una instancia?
- ¿Cómo se puede acceder fácilmente a la única instancia de una clase?
- ¿Cómo puede una clase controlar su instanciación?
- ¿Cómo se puede restringir el número de instancias de una clase?
- ¿Cómo se puede acceder a una variable global?
El patrón de diseño singleton describe cómo resolver tales problemas:
- Ocultar el constructor de la clase.
- Defina una operación estática pública (
getInstance()
) que devuelva la única instancia de la clase.
La idea clave en este patrón es hacer que la propia clase sea responsable de controlar su instanciación (que solo se instancia una vez).
El constructor oculto (declarado privado o protegido ) asegura que la clase nunca pueda ser instanciada desde fuera de la clase.
Se puede acceder fácilmente a la operación estática pública utilizando el nombre de la clase y el nombre de la operación ( Singleton.getInstance()
).
Usos comunes
- Los patrones de fábrica abstracta , método de fábrica , constructor y prototipo pueden usar singletons en su implementación.
- Los objetos de fachada son a menudo singleton porque solo se requiere un objeto de fachada.
- Los objetos de estado suelen ser singleton.
- Los singleton a menudo se prefieren a las variables globales porque:
Consideraciones antipatrón
El patrón Singleton generalmente se considera un anti-patrón por las siguientes razones:
Las clases singleton rompen los principios del diseño orientado a objetos
- No se puede heredar . Para agregar una nueva funcionalidad, no se puede descender una nueva clase para contener esa funcionalidad, rompiendo la Separación de preocupaciones.
- Sin control sobre la creación . Es imposible saber si una referencia es de una instancia existente o una nueva instancia.
- Evita la inyección de dependencia . Como solo hay una instancia de la clase, no se puede inyectar una dependencia en ella. Si se hace a través de una propiedad, la dependencia se cambia para todas las referencias a esa instancia.
Las clases singleton no permiten el desarrollo basado en pruebas (TDD)
- Como no hay control sobre la creación, no se puede utilizar una instancia "limpia" del objeto para cada prueba.
- Sin el simulacro de inyección de dependencia, los objetos no se pueden usar en pruebas.
Implementación
Una implementación del patrón singleton debe:
- asegurarse de que solo exista una instancia de la clase singleton; y
- proporcionar acceso global a esa instancia.
Normalmente, esto se hace mediante:
- declarando que todos los constructores de la clase son privados ; y
- proporcionando un método estático que devuelve una referencia a la instancia.
La instancia generalmente se almacena como una variable estática privada ; la instancia se crea cuando se inicializa la variable, en algún momento antes de que se llame por primera vez al método estático. La siguiente es una implementación de muestra escrita en Java .
clase final pública Singleton { Singleton final estático privado INSTANCIA = nuevo Singleton (); Singleton privado () {} getInstance () { return INSTANCE ; public static Singleton ; } }
Implementación de Python
class Singleton : __instance = None def __new__ ( cls , * args ): if cls . __instance es None : cls . __instance = objeto . __new__ ( cls , * args ) devuelve cls . __ejemplo
Implementación de C ++
A partir de C ++ 11 , la inicialización de variables locales estáticas es segura para subprocesos y se produce después de la primera invocación. [7] Esta es una alternativa más segura que un espacio de nombres o una variable estática de ámbito de clase.
class Singleton { public : static Singleton & GetInstance () { // Asignar con `new` en caso de que Singleton no sea trivialmente destructible. Singleton estático * singleton = nuevo Singleton (); return * singleton ; } privado : Singleton () = predeterminado ; // Eliminar copiar / mover para que no se puedan crear / mover instancias adicionales. Singleton ( const Singleton & ) = eliminar ; Singleton & operator = ( const Singleton & ) = eliminar ; Singleton ( Singleton && ) = eliminar ; Singleton & operator = ( Singleton && ) = eliminar ; };
Implementación de C #
Singleton clase pública sellada { instancia pública Singleton estática { get ; } = nuevo Singleton (); Singleton privado () { } }
En C # también puede usar clases estáticas para crear singleton, donde la clase en sí es el singleton.
pública estática clase Singleton { pública estática MyOtherClass Instancia { get ; } = new MyOtherClass (); }
Implementación de Unity
Los singletons pueden ser una herramienta útil cuando se desarrolla con Unity , debido a la forma única en que se crean instancias de clases. Se prefiere este método a la ocultación del constructor, ya que es posible crear una instancia de un objeto con un constructor oculto en Unity.
Para evitar que la instancia se sobrescriba, se debe realizar una verificación para garantizar que la instancia sea nula. Si Instance no es nulo, el GameObject que contiene el script infractor debe destruirse.
Si otros componentes dependen del Singleton, se debe modificar el orden de ejecución del script. Esto asegura que el componente que define el Singleton se ejecute primero.
clase Singleton : MonoBehaviour { pública estática Singleton Instancia { get ; conjunto privado ; } private void Awake () { if ( Instance ! = null && Instance ! = this ) { Destroy ( this . gameObject ); } else { Instancia = esto ; } } }
Nota: También es posible implementarlo eliminando solo el script ofensivo, no el GameObject, llamando a Destroy (this);
Inicialización perezosa
Una implementación singleton puede usar la inicialización diferida , donde la instancia se crea cuando se invoca por primera vez el método estático. Si se puede llamar al método estático desde varios subprocesos simultáneamente, es posible que deban tomarse medidas para evitar condiciones de carrera que podrían dar lugar a la creación de varias instancias de la clase. La siguiente es una implementación de muestra segura para subprocesos , que utiliza la inicialización diferida con bloqueo de doble verificación , escrito en Java. [a]
clase final pública Singleton { instancia privada estática volátil Singleton = nulo ; Singleton privado () {} public static Singleton getInstance () { if ( instancia == nulo ) { sincronizado ( Singleton . clase ) { if ( instancia == nulo ) { instancia = nuevo Singleton (); } } } instancia de retorno ; } }
Implementación de dardos
class Singleton { Singleton . _ (); estático Singleton obtener instancia => Singleton . _ ();}
Implementación PHP
clase Singleton{ $ instancia estática privada = nula ; función privada __construct () {} función estática pública getInstance () : self { if ( nulo === self :: $ instancia ) { self :: $ instancia = nuevo self (); } return self :: $ instancia ; }}
Implementación de Java [8]
Moneda de clase pública { privado estático final int ADD_MORE_COIN = 10 ; moneda privada int ; instancia de moneda estática privada = nueva moneda (); // Carga con entusiasmo de la instancia singleton Moneda privada () { // privado para evitar que cualquier otra persona cree una instancia } getInstance () {de la moneda estática pública instancia de retorno ; } public int getCoin () { devolución de moneda ; } public void addMoreCoin () { moneda + = ADD_MORE_COIN ; } public void deductCoin () { moneda - ; }}
Implementación de Kotlin [8]
La palabra clave del objeto Kotlin declara una clase singleton [9]
objeto Coin { moneda var privada : Int = 0 fun getCoin (): Int { devolución de moneda } fun addCoin () { moneda + = 10 } fun deductCoin () { moneda - }}
Implementación de Delphi y Free Pascal
GetInstance es una implementación segura para subprocesos de Singleton.
unidad SingletonPattern ;interfaztipo TTest = clase sellada estrictamente privada FCreationTime : TDateTime ; constructor público Create ; propiedad CreationTime : TDateTime read FCreationTime ; terminar ; función GetInstance : TTest ;implementaciónusa SysUtils , SyncObjs ;var FCriticalSection : TCriticalSection ; FInstance : TTest ;función GetInstance : TTest ; comience FCriticalSection . Adquirir ; intente si no está asignado ( FInstance ) entonces FInstance : = TTest . Crear ; Resultado : = FInstance ; finalmente FCriticalSection . Lanzamiento ; terminar ; terminar ;constructor TTest . Crear ; iniciar la creación heredada ; FCreationTime : = Ahora ; terminar ; inicialización FCriticalSection : = TCriticalSection . Crear ;finalización FreeAndNil ( FCriticalSection ) ;final .
Uso:
procedimiento TForm3 . btnCreateInstanceClick ( Remitente : TObject ) ; var i : número entero ; comenzar para i : = 0 a 5 hacer ShowMessage ( DateTimeToStr ( GetInstance . CreationTime )) ; terminar ;
Notas
- ^ En Java, para evitar la sobrecarga de sincronización mientras se mantiene la inicialización diferida con seguridad de subprocesos, el enfoque preferido es utilizar el lenguaje de titular de inicialización bajo demanda . [ cita requerida ]
Referencias
- ^ Scott Densmore. Por qué los solteros son malvados , mayo de 2004
- ^ Steve Yegge. Singletons considerados estúpidos , septiembre de 2004
- ^ Charlas de Código Limpio - Estado Global y Singletons
- ^ Maximiliano Contieri. Patrón Singleton: La raíz de todo mal , junio de 2020
- ↑ a b Erich Gamma, Richard Helm, Ralph Johnson, John Vlissides (1994). Patrones de diseño: elementos de software orientado a objetos reutilizable . Addison Wesley. págs. 127 y siguientes . ISBN 0-201-63361-2.CS1 maint: varios nombres: lista de autores ( enlace )
- ^ "El patrón de diseño Singleton - Problema, solución y aplicabilidad" . w3sDesign.com . Consultado el 16 de agosto de 2017 .
- ^ https://en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables
- ^ a b "¿Es usted un desarrollador de Android y aún no usa Singleton Class?" .
- ^ "Declaraciones de objeto" . Consultado el 19 de mayo de 2020 .
enlaces externos
- Artículo completo " Explicación del patrón Java Singleton "
- Cuatro formas diferentes de implementar singleton en Java " Formas de implementar singleton en Java "
- Extracto del libro: Implementación del patrón Singleton en C # por Jon Skeet
- Singleton en el Centro de desarrolladores de patrones y prácticas de Microsoft
- Artículo de IBM " Bloqueo con doble verificación y patrón Singleton " de Peter Haggar
- Artículo de IBM " Use sus singletons sabiamente " por JB Rainsberger
- Geary, David (25 de abril de 2003). "Cómo navegar por el patrón Singleton engañosamente simple" . Patrones de diseño de Java. JavaWorld . Consultado el 21 de julio de 2020 .
- Artículo de Google " Por qué los singleton son controvertidos "
- Google Singleton Detector (analiza el código de bytes de Java para detectar singleton)