Editar: De otra pregunta, proporcioné una respuesta que tiene enlaces a muchas preguntas / respuestas sobre singletons: Más información sobre singletons aquí:
Así que he leído el hilo Singletons: ¿buen diseño o una muleta?
Y la discusión aún continúa.
Veo Singletons como un patrón de diseño (bueno y malo).
El problema con Singleton no es el Patrón sino los usuarios (lo siento, todos). Todos y su padre piensan que pueden implementar uno correctamente (y de las muchas entrevistas que he hecho, la mayoría de la gente no puede). Además, como todos piensan que pueden implementar un Singleton correcto, abusan del Patrón y lo usan en situaciones que no son apropiadas (¡reemplazando las variables globales con Singletons!).
Entonces, las principales preguntas que deben responderse son:
- ¿Cuándo deberías usar un Singleton?
- ¿Cómo implementas un Singleton correctamente?
Mi esperanza para este artículo es que podamos reunir en un solo lugar (en lugar de tener que buscar en Google y buscar en varios sitios) una fuente autorizada de cuándo (y luego cómo) usar un Singleton correctamente. También sería apropiado una lista de Anti-Usos e implementaciones erróneas comunes que explican por qué no funcionan y para las buenas implementaciones sus debilidades.
Así que ponte en marcha:
levantaré la mano y diré que esto es lo que uso, pero que probablemente tenga problemas.
Me gusta el manejo de "Scott Myers" del tema en sus libros "Efectivo C ++"
Buenas situaciones para usar Singletons (no muchos):
- Marcos de registro
- Piscinas de reciclaje de hilos
/*
* C++ Singleton
* Limitation: Single Threaded Design
* See: http://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf
* For problems associated with locking in multi threaded applications
*
* Limitation:
* If you use this Singleton (A) within a destructor of another Singleton (B)
* This Singleton (A) must be fully constructed before the constructor of (B)
* is called.
*/
class MySingleton
{
private:
// Private Constructor
MySingleton();
// Stop the compiler generating methods of copy the object
MySingleton(MySingleton const& copy); // Not Implemented
MySingleton& operator=(MySingleton const& copy); // Not Implemented
public:
static MySingleton& getInstance()
{
// The only instance
// Guaranteed to be lazy initialized
// Guaranteed that it will be destroyed correctly
static MySingleton instance;
return instance;
}
};
OKAY. Vamos a obtener algunas críticas y otras implementaciones juntas.
:-)
fuente
Respuestas:
Todos ustedes están equivocados. Lea la pregunta. Responder:
Use un Singleton si:
No use un Singleton si:
Cómo crear el mejor singleton:
fuente
Los Singletons te dan la posibilidad de combinar dos rasgos malos en una clase. Eso está mal en casi todos los sentidos.
Un singleton te da:
El número uno es sencillo. Los globales son generalmente malos. Nunca debemos hacer que los objetos sean globalmente accesibles a menos que realmente lo necesitemos.
El número dos puede parecer que tiene sentido, pero pensemos en ello. ¿Cuándo fue la última vez que ** accidentalmente * creó un nuevo objeto en lugar de hacer referencia a uno existente? Como esto está etiquetado como C ++, usemos un ejemplo de ese lenguaje. ¿Escribes a menudo accidentalmente?
Cuando pretendías escribir
Por supuesto no. No necesitamos protección contra este error, porque ese tipo de error simplemente no ocurre. Si es así, la respuesta correcta es irse a casa y dormir durante 12-20 horas y esperar que se sienta mejor.
Si solo se necesita un objeto, simplemente cree una instancia. Si un objeto debe ser accesible globalmente, conviértalo en global. Pero eso no significa que deba ser imposible crear otras instancias del mismo.
La restricción "solo una instancia es posible" realmente no nos protege contra posibles errores. Pero hace que nuestro código sea muy difícil de refactorizar y mantener. Porque a menudo descubrimos más tarde que necesitábamos más de una instancia. Nosotros hacemos tener más de una base de datos, que sí tiene más de un objeto de configuración, sí queremos varios registradores. Nuestras pruebas unitarias pueden querer poder crear y recrear estos objetos en cada prueba, para tomar un ejemplo común.
Por lo tanto un conjunto unitario debe utilizarse si y sólo si, necesitamos tanto los rasgos que ofrece: Si necesitamos acceso global (que es raro, porque globales son generalmente desalentados) y que necesitamos para evitar que alguien alguna vez la creación de más de una instancia de una clase (que me parece un problema de diseño). La única razón por la que puedo ver esto es si crear dos instancias dañaría el estado de nuestra aplicación, probablemente porque la clase contiene varios miembros estáticos o tonterías similares. En cuyo caso la respuesta obvia es arreglar esa clase. No debería depender de ser la única instancia.
Si necesita acceso global a un objeto, conviértalo en global, como
std::cout
. Pero no restrinja el número de instancias que se pueden crear.Si realmente necesita restringir el número de instancias de una clase a solo una, y no hay forma de que crear una segunda instancia pueda manejarse de manera segura, entonces haga cumplir eso. Pero tampoco lo haga accesible a nivel mundial.
Si necesita ambos rasgos, entonces 1) conviértalo en un singleton y 2) dígame para qué lo necesita, porque me está costando mucho imaginar ese caso.
fuente
El problema con los singletons no es su implementación. Es que combinan dos conceptos diferentes, ninguno de los cuales es obviamente deseable.
1) Los Singletons proporcionan un mecanismo de acceso global a un objeto. Aunque podrían ser marginalmente más seguros o marginalmente más confiables en idiomas sin un orden de inicialización bien definido, este uso sigue siendo el equivalente moral de una variable global. Es una variable global vestida con una sintaxis incómoda (foo :: get_instance () en lugar de g_foo, por ejemplo), pero tiene exactamente el mismo propósito (un solo objeto accesible en todo el programa) y tiene exactamente los mismos inconvenientes.
2) Singletons previenen múltiples instancias de una clase. Es raro, IME, que este tipo de característica se incluya en una clase. Normalmente es una cosa mucho más contextual; Muchas de las cosas que se consideran como uno y solo uno son realmente solo para ser uno solo. En mi opinión, una solución más adecuada es crear solo una instancia, hasta que se dé cuenta de que necesita más de una instancia.
fuente
Una cosa con los patrones: no generalizar . Tienen todos los casos cuando son útiles y cuando fallan.
Singleton puede ser desagradable cuando tienes que probar el código. Por lo general, está atascado con una instancia de la clase y puede elegir entre abrir una puerta en el constructor o algún método para restablecer el estado, etc.
Otro problema es que el Singleton, de hecho, no es más que una variable global disfrazada. Cuando tiene demasiado estado compartido global sobre su programa, las cosas tienden a retroceder, todos lo sabemos.
Puede dificultar el seguimiento de dependencias . Cuando todo depende de su Singleton, es más difícil cambiarlo, dividirlo en dos, etc. Por lo general, está atrapado en él. Esto también dificulta la flexibilidad. Investigue un marco de inyección de dependencia para tratar de aliviar este problema.
fuente
Los Singletons básicamente le permiten tener un estado global complejo en idiomas que, de lo contrario, hacen que sea difícil o imposible tener variables globales complejas.
Java en particular usa singletons como reemplazo de variables globales, ya que todo debe estar contenido dentro de una clase. Lo más parecido a las variables globales son las variables públicas estáticas, que pueden usarse como si fueran globales con
import static
C ++ tiene variables globales, pero el orden en que se invocan los constructores de las variables de clase global no está definido. Como tal, un singleton le permite diferir la creación de una variable global hasta la primera vez que se necesita esa variable.
Los lenguajes como Python y Ruby usan singletons muy poco porque en su lugar puedes usar variables globales dentro de un módulo.
Entonces, ¿cuándo es bueno / malo usar un singleton? Casi exactamente cuándo sería bueno / malo usar una variable global.
fuente
El diseño moderno de C ++ de Alexandrescu tiene un singleton genérico heredable y seguro para subprocesos.
Para mi valor de 2p, creo que es importante tener vidas definidas para sus singletons (cuando es absolutamente necesario usarlos). Normalmente no dejo que la
get()
función estática cree una instancia de nada, y dejo la configuración y destrucción a alguna sección dedicada de la aplicación principal. Esto ayuda a resaltar las dependencias entre singletons, pero, como se destacó anteriormente, es mejor evitarlas si es posible.fuente
Hay un problema que nunca he visto mencionado, algo con lo que me encontré en un trabajo anterior. Teníamos singletons en C ++ que se compartían entre las DLL, y la mecánica habitual de garantizar una sola instancia de una clase simplemente no funciona. El problema es que cada DLL obtiene su propio conjunto de variables estáticas, junto con el EXE. Si su función get_instance está en línea o es parte de una biblioteca estática, cada DLL terminará con su propia copia del "singleton".
La solución es asegurarse de que el código singleton solo esté definido en una DLL o EXE, o crear un administrador singleton con esas propiedades para parcelar instancias.
fuente
El primer ejemplo no es seguro para subprocesos: si dos subprocesos llaman a getInstance al mismo tiempo, esa estática será una PITA. Alguna forma de mutex ayudaría.
fuente
Como otros han señalado, las principales desventajas de los singletons incluyen la incapacidad de extenderlos y perder el poder de crear instancias en más de una instancia, por ejemplo, con fines de prueba.
Algunos aspectos útiles de los singletons:
Sin embargo, no tiene que usar un singleton para obtener estos beneficios. Puede escribir un objeto normal que haga el trabajo, y luego hacer que las personas accedan a él a través de una fábrica (un objeto separado). La fábrica puede preocuparse por solo crear una instancia y reutilizarla, etc., si es necesario. Además, si programa en una interfaz en lugar de una clase concreta, la fábrica puede usar estrategias, es decir, puede activar y desactivar diversas implementaciones de la interfaz.
Finalmente, una fábrica se presta a tecnologías de inyección de dependencia como Spring, etc.
fuente
Los Singletons son útiles cuando se ejecuta un código de lote al inicializar y objetar. Por ejemplo, cuando usa iBatis cuando configura un objeto de persistencia, tiene que leer todas las configuraciones, analizar los mapas, asegurarse de que todo esté correcto, etc. antes de acceder a su código.
Si hicieras esto cada vez, el rendimiento se vería muy degradado. Al usarlo en un singleton, tomas ese golpe una vez y luego todas las llamadas posteriores no tienen que hacerlo.
fuente
La verdadera caída de Singletons es que rompen la herencia. No puede derivar una nueva clase para darle una funcionalidad extendida a menos que tenga acceso al código donde se hace referencia al Singleton. Por lo tanto, más allá del hecho de que el Singleton hará que su código esté estrechamente acoplado (reparable mediante un Patrón de estrategia ... también conocido como Inyección de dependencia) también evitará que cierre secciones del código de la revisión (bibliotecas compartidas).
Por lo tanto, incluso los ejemplos de registradores o grupos de subprocesos no son válidos y deben reemplazarse por estrategias.
fuente
La mayoría de las personas usan singletons cuando intentan sentirse bien acerca del uso de una variable global. Hay usos legítimos, pero la mayoría de las veces cuando las personas los usan, el hecho de que solo pueda haber una instancia es solo un hecho trivial en comparación con el hecho de que es accesible a nivel mundial.
fuente
Debido a que un singleton solo permite que se cree una instancia, controla efectivamente la replicación de la instancia. por ejemplo, no necesitaría múltiples instancias de una búsqueda; por ejemplo, un mapa de búsqueda morse, por lo tanto, envolverlo en una clase singleton es apto. Y el hecho de que tenga una sola instancia de la clase no significa que también esté limitado en el número de referencias a esa instancia. Puede poner en cola las llamadas (para evitar problemas de subprocesos) a la instancia y efectuar los cambios necesarios. Sí, la forma general de un singleton es pública a nivel mundial, ciertamente puede modificar el diseño para crear un singleton más restringido de acceso. No he cansado esto antes, pero estoy seguro de que es posible. Y para todos los que comentaron que el patrón singleton es completamente malo, deben saber esto:
fuente
Pero cuando necesito algo como un Singleton, a menudo termino usando un contador Schwarz para crear una instancia.
fuente
A continuación se muestra el mejor enfoque para implementar un patrón singleton seguro para subprocesos con desasignación de la memoria en el destructor mismo. Pero creo que el destructor debería ser opcional porque la instancia de singleton se destruirá automáticamente cuando el programa finalice:
En cuanto a las situaciones en las que necesitamos usar clases singleton, podemos ser: si queremos mantener el estado de la instancia durante la ejecución del programa, si estamos involucrados en escribir en el registro de ejecución de una aplicación donde solo una instancia del archivo necesita ser utilizado ... y así sucesivamente. Será apreciable si alguien puede sugerir optimización en mi código anterior.
fuente
Yo uso Singletons como prueba de entrevista.
Cuando le pido a un desarrollador que nombre algunos patrones de diseño, si todo lo que pueden nombrar es Singleton, no son contratados.
fuente
Anti-uso:
Un problema importante con el uso excesivo de singleton es que el patrón impide la fácil extensión e intercambio de implementaciones alternativas. El nombre de la clase está codificado donde se usa el singleton.
fuente
Creo que esta es la versión más robusta para C #:
Aquí está la versión optimizada para .NET :
Puede encontrar este patrón en dotfactory.com .
fuente
El patrón Singleton de Meyers funciona bastante bien la mayor parte del tiempo, y en ocasiones lo hace, no necesariamente vale la pena buscar algo mejor. Siempre que el constructor nunca arroje y no haya dependencias entre singletons.
Un singleton es una implementación para un objeto accesible globalmente (GAO de ahora en adelante) aunque no todos los GAO son singletons.
Los propios registradores no deberían ser únicos, pero los medios para iniciar sesión deberían ser idealmente accesibles a nivel mundial, para desacoplar dónde se genera el mensaje de registro desde dónde o cómo se registra.
La carga diferida / evaluación diferida es un concepto diferente y, por lo general, singleton también lo implementa. Viene con muchos de sus propios problemas, en particular la seguridad de los hilos y problemas si falla con excepciones tales que lo que parecía una buena idea en ese momento resulta no ser tan bueno después de todo. (Un poco como la implementación de COW en cadenas).
Con eso en mente, los GOA se pueden inicializar así:
No es necesario que se haga tan groseramente como eso, y claramente en una biblioteca cargada que contiene objetos, es probable que desee algún otro mecanismo para administrar su vida útil. (Póngalos en un objeto que obtenga cuando cargue la biblioteca).
¿En cuanto a cuando uso singletons? Los usé para 2 cosas: una tabla singleton que indica qué bibliotecas se han cargado con dlopen: un controlador de mensajes al que los registradores pueden suscribirse y al que pueden enviar mensajes. Requerido específicamente para manejadores de señal.
fuente
Todavía no entiendo por qué un singleton tiene que ser global.
Iba a producir un singleton donde escondía una base de datos dentro de la clase como una variable estática constante privada y hacía funciones de clase que utilizaran la base de datos sin exponer la base de datos al usuario.
No veo por qué esta funcionalidad sería mala.
fuente
Los encuentro útiles cuando tengo una clase que encapsula mucha memoria. Por ejemplo, en un juego reciente en el que he estado trabajando, tengo una clase de mapa de influencia que contiene una colección de matrices muy grandes de memoria contigua. Quiero que todo esté asignado al inicio, todo liberado al apagar y definitivamente solo quiero una copia. También tengo que acceder desde muchos lugares. Creo que el patrón singleton es muy útil en este caso.
Estoy seguro de que hay otras soluciones, pero creo que esta es muy útil y fácil de implementar.
fuente
Si usted fue quien creó el singleton y quien lo usa, no lo haga como singleton (no tiene sentido porque puede controlar la singularidad del objeto sin hacerlo único) pero tiene sentido cuando es desarrollador de un biblioteca y desea proporcionar un solo objeto a sus usuarios (en este caso, usted es quien creó el singleton, pero no es el usuario).
Los singletons son objetos, así que úselos como objetos, muchas personas acceden a los singletons directamente llamando al método que lo devuelve, pero esto es perjudicial porque está haciendo que su código sepa que el objeto es singleton, prefiero usar los singletons como objetos, los paso a través del constructor y los uso como objetos ordinarios, de esa manera, su código no sabe si estos objetos son únicos o no y eso hace que las dependencias sean más claras y ayuda un poco para refactorizar ...
fuente
En las aplicaciones de escritorio (lo sé, ¡solo nosotros, los dinosaurios, las escribimos más!) Son esenciales para obtener una configuración global de aplicación relativamente inmutable: el idioma del usuario, la ruta de acceso a los archivos de ayuda, las preferencias del usuario, etc., que de lo contrario tendrían que propagarse en cada clase y cada diálogo .
Editar: ¡por supuesto, estos deben ser de solo lectura!
fuente
Otra implementación
fuente
Instance()
debe devolver un puntero, no una referencia. Dentro de su.cpp
archivo, inicializar la instancia a nulo:Singleton* Singleton::instance_ = nullptr;
. YInstance()
debe ser implementado como:if (instance_ == nullptr) instance_ = new Singleton(); return instance_;
.