design-patterns - pattern - singleton python
¿Hay alguna alternativa viable al patrón GOF Singleton? (16)
Seamos sinceros. El patrón de Singleton es un tema muy controvertido con los programadores de hordas en ambos lados de la valla. Hay quienes sienten que el Singleton no es más que una variable global glorificada, y otros que juran por el patrón y lo usan incesantemente. Sin embargo, no quiero que la Controversia de Singleton mienta en el centro de mi pregunta. Todo el mundo puede tener un tira y afloja y luchar y ver quién gana todo lo que me importa . Lo que estoy tratando de decir es que no creo que haya una sola respuesta correcta y no estoy intentando intencionalmente inflamar las disputas partidistas. Simplemente me interesan las alternativas singleton cuando hago la pregunta:
¿Hay alguna alternativa específica al Patrón de Singleton de GOF?
Por ejemplo, muchas veces cuando he usado el patrón singleton en el pasado, simplemente me interesa preservar el estado / valores de una o varias variables. Sin embargo, el estado / valores de las variables se puede preservar entre cada instanciación de la clase usando variables estáticas en lugar de usar el patrón singleton.
¿Qué otras ideas tienes?
EDITAR: Realmente no quiero que esta sea otra publicación sobre "cómo usar el singleton correctamente". Una vez más, estoy buscando formas de evitarlo. Por diversión, ¿de acuerdo? Supongo que estoy haciendo una pregunta puramente académica en tu mejor voz de tráiler de la película: "En un universo paralelo donde no hay singleton, ¿qué podríamos hacer?"
¿Qué quieres decir con cuáles son mis técnicas para evitarlo?
Para "evitarlo", eso implica que hay muchas situaciones que encuentro en las que el patrón singleton es naturalmente bueno, y por lo tanto tengo que tomar algunas medidas para desactivar estas situaciones.
Pero no hay. No tengo que evitar el patrón singleton. Simplemente no surge.
Al no haber programado en un entorno intensamente orientado a objetos (por ejemplo, Java), no estoy completamente al tanto de las complejidades de la discusión. Pero he implementado un singleton en PHP 4. Lo hice como una forma de crear un controlador de base de datos ''black-box'' que se inicializó automáticamente y no tuvo que pasarse llamadas de funciones hacia arriba y hacia abajo en un marco incompleto y algo roto.
Después de leer algunos enlaces de patrones de singleton, no estoy completamente seguro de implementarlo de la misma manera otra vez. Lo que realmente se necesitaba eran objetos múltiples con almacenamiento compartido (por ejemplo, el manejador de la base de datos real) y esto es más o menos a lo que mi llamada se convirtió.
Al igual que la mayoría de los patrones y algoritmos, usar un singleton ''solo porque es genial'' es lo incorrecto que hay que hacer. Necesitaba una llamada realmente "caja negra" que se parecía mucho a un singleton. E IMO es la forma de abordar la cuestión: tenga en cuenta el patrón, pero también mire su alcance más amplio y en qué nivel su instancia necesita ser única.
Alex Miller en " Patterns I Hate " cita lo siguiente:
"Cuando un singleton parece ser la respuesta, me parece que a menudo es más sabio:
- Crea una interfaz y una implementación predeterminada de tu singleton
- Construya una sola instancia de su implementación predeterminada en la "parte superior" de su sistema. Esto podría estar en una configuración de Spring, o en un código, o puede definirse de varias maneras dependiendo de su sistema.
- Pase la instancia individual a cada componente que lo necesite (inyección de dependencia)
Creo que el mejor lugar para patrullar el singleton es en el nivel de diseño de clase. En esta etapa, debería poder mapear las interacciones entre las clases y ver si algo definitivamente requiere que solo exista 1 instancia de esta clase en cualquier momento de la vida de las aplicaciones.
Si ese es el caso, entonces tienes un singleton. Si está lanzando singletons por conveniencia durante la codificación, entonces realmente debería volver a visitar su diseño y también dejar de codificar dichos singletons :)
Y sí, "policía" es la palabra que quise decir aquí en lugar de "evitar". El singleton no es algo que deba evitarse (del mismo modo que las variables globales y globales no son algo que deba evitarse). En su lugar, debe controlar su uso y asegurarse de que es el mejor método para lograr que lo que quiere se haga de manera efectiva.
El patrón singleton existe porque hay situaciones en las que se necesita un solo objeto para proporcionar un conjunto de servicios .
Incluso si este es el caso, todavía considero inapropiado el enfoque de crear singleton utilizando un campo / propiedad estática global que represente la instancia. No es apropiado porque crea una dependencia en el código entre el campo estático y el objeto no, los servicios que proporciona el objeto.
Entonces, en lugar del patrón clásico de singleton, recomiendo usar el patrón de servicio "me gusta" con contenedores revisados , donde en lugar de usar su singleton a través de un campo estático, obtiene una referencia a través de un método que solicita el tipo de servicio requerido.
*pseudocode* currentContainer.GetServiceByObjectType(singletonType)
//Under the covers the object might be a singleton, but this is hidden to the consumer.
en lugar de solo global
*pseudocode* singletonType.Instance
De esta forma, cuando desee cambiar el tipo de un objeto desde singleton a otra cosa, tendrá tiempo para hacerlo. Además, como beneficio agregado, no es necesario pasar un montón de instancias de objetos a cada método.
También vea Inversion of Control , la idea es que al exponer singletons directamente al consumidor, crea una dependencia entre el consumidor y la instancia del objeto, no los servicios de objetos proporcionados por el objeto.
Mi opinión es ocultar el uso del patrón singleton siempre que sea posible, porque no siempre es posible evitarlo o desearlo.
En realidad, si diseña desde el principio evitando Singeltons, es posible que no tenga que evitar el uso de Singletons mediante el uso de variables estáticas. Al usar variables estáticas, también está creando un Singleton más o menos, la única diferencia es que está creando instancias de objetos diferentes, sin embargo internamente todos se comportan como si estuvieran usando un Singleton.
¿Puede dar un ejemplo detallado de dónde usa un Singleton o dónde se usa actualmente un Singleton y está intentando evitar usarlo? Esto podría ayudar a las personas a encontrar una solución más elegante de cómo la situación podría manejarse sin Singleton en absoluto.
Por cierto, personalmente no tengo problemas con Singletons y no puedo entender los problemas que otras personas tienen con respecto a Singletons. No veo nada malo acerca de ellos. Es decir, si no los estás abusando. Se puede abusar de cada técnica útil y si se abusa de ella, se obtendrán resultados negativos. Otra técnica que comúnmente se usa indebidamente es la herencia. Todavía nadie diría que la herencia es algo malo solo porque algunas personas la abusan horriblemente.
La mejor solución que he encontrado es utilizar el patrón de fábrica para construir instancias de tus clases. Usando el patrón, puede asegurar que solo hay una instancia de una clase que se comparte entre los objetos que la usan.
Pensé que sería complicado de administrar, pero después de leer esta entrada del blog "¿Adónde han ido todos los Singletons?" , parece tan natural. Y como un lado, ayuda mucho con el aislamiento de sus pruebas unitarias.
En resumen, ¿qué necesitas hacer? Cada vez que un objeto depende de otro, recibirá una instancia de él solo a través de su constructor (no hay palabra clave nueva en su clase).
class NeedyClass {
private ExSingletonClass exSingleton;
public NeedyClass(ExSingletonClass exSingleton){
this.exSingleton = exSingleton;
}
// Here goes some code that uses the exSingleton object
}
Y luego, la fábrica.
class FactoryOfNeedy {
private ExSingletonClass exSingleton;
public FactoryOfNeedy() {
this.exSingleton = new ExSingletonClass();
}
public NeedyClass buildNeedy() {
return new NeedyClass(this.exSingleton);
}
}
Como instanciará su fábrica solo una vez, habrá una instanciación única de exSingleton. Cada vez que llamas a buildNeedy, la nueva instancia de NeedyClass se incluirá con exSingleton.
Espero que esto ayude. Por favor señale cualquier error.
Monostate (descrito en Desarrollo de software ágil de Robert C. Martin) es una alternativa a singleton. En este patrón, los datos de la clase son estáticos pero los getters / setters no son estáticos.
Por ejemplo:
public class MonoStateExample
{
private static int x;
public int getX()
{
return x;
}
public void setX(int xVal)
{
x = xVal;
}
}
public class MonoDriver
{
public static void main(String args[])
{
MonoStateExample m1 = new MonoStateExample();
m1.setX(10);
MonoStateExample m2 = new MonoStateExample();
if(m1.getX() == m2.getX())
{
//singleton behavior
}
}
}
Monostate tiene un comportamiento similar al singleton, pero lo hace de una manera en la que el programador no es necesariamente consciente del hecho de que se está utilizando un singleton.
No deberías tener que salirte de tu camino para evitar cualquier patrón. El uso de un patrón es una decisión de diseño o un ajuste natural (simplemente cae en su lugar). Cuando está diseñando un sistema, tiene la opción de usar un patrón o no usar el patrón. Sin embargo, no deberías salirte de tu camino para evitar cualquier cosa que sea, en última instancia, una opción de diseño.
No evito el patrón Singleton. O es apropiado y lo uso o no es apropiado y no lo uso. Creo que es así de simple.
La idoneidad (o la falta de ella) de Singleton depende de la situación. Es una decisión de diseño que debe tomarse y las consecuencias de esa decisión deben ser entendidas (y documentadas).
Para comprender la forma correcta de solucionar Singletons, debe comprender qué está mal con Singletons (y el estado global en general):
Singletons esconde dependencias.
¿Por qué es eso importante?
Porque si oculta las dependencias, tiende a perder la pista de la cantidad de acoplamiento.
Usted podría argumentar que
void purchaseLaptop(String creditCardNumber, int price){
CreditCardProcessor.getInstance().debit(creditCardNumber, amount);
Cart.getInstance().addLaptop();
}
es más simple que
void purchaseLaptop(CreditCardProcessor creditCardProcessor, Cart cart,
String creditCardNumber, int price){
creditCardProcessor.debit(creditCardNumber, amount);
cart.addLaptop();
}
pero al menos la segunda API deja en claro exactamente cuáles son los colaboradores del método.
Entonces, la forma de solucionar Singletons no es usar variables estáticas o localizadores de servicios, sino cambiar las clases de Singleton en instancias, que se instancian en el alcance donde tienen sentido y se inyectan en los componentes y métodos que los necesitan. Puede usar un marco de trabajo de IoC para manejar esto, o puede hacerlo manualmente, pero lo importante es deshacerse de su estado global y hacer que las dependencias y colaboraciones sean explícitas.
Personalmente, para mí, una forma mucho más sensata de implementar algo que se comporta como singleton es utilizar una clase totalmente estática (miembros estáticos, métodos estáticos, propiedades estáticas). La mayoría de las veces lo implemento de esta manera (no puedo pensar en ninguna diferencia de comportamiento desde el punto de vista del usuario)
Si está utilizando un Singleton para representar un solo objeto de datos, en su lugar podría pasar un objeto de datos como un parámetro de método.
(aunque, yo diría que esta es la forma incorrecta de usar un Singleton en primer lugar)
Si su problema es que desea mantener el estado, quiere una clase MumbleManager. Antes de comenzar a trabajar con un sistema, su cliente crea un MumbleManager, donde Mumble es el nombre del sistema. El estado se conserva a través de eso. Lo más probable es que tu MumbleManager contenga una bolsa de propiedades que contenga tu estado.
Este tipo de estilo se siente muy parecido a C y no es muy parecido a un objeto: encontrará que los objetos que definen su sistema tendrán una referencia al mismo MumbleManager.
Spring o cualquier otro contenedor IoC hace un trabajo razonablemente bueno en eso. Dado que las clases se crean y gestionan fuera de la aplicación, el contenedor puede crear clases simples de singleton e insertarlas donde sea necesario.
Use un objeto simple y un objeto de fábrica. La fábrica es responsable de controlar la instancia y los detalles del objeto simple solo con la información de configuración (que contiene, por ejemplo) y el comportamiento.
Uso singleton principalmente como "contenedor de métodos", sin ningún estado. Si necesito compartir estos métodos con muchas clases y quiero evitar la carga de creación de instancias e inicialización, creo un contexto / sesión e inicializo todas las clases allí; todo lo que se refiere a la sesión también tiene acceso al "singleton" contenido.