Pregunta

Seamos realistas. El patrón Singleton es un tema altamente controversial con 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. No quiero que el Singleton Controversy esté en el corazón de mi pregunta, sin embargo. Todos pueden tener un tira y afloja, luchar y ver quién gana por todo lo que me importa . Lo que estoy tratando de decir es que no creo que haya una sola respuesta correcta y que no estoy tratando intencionalmente de inflamar las disputas partidistas. Simplemente estoy interesado en singleton-alternative cuando hago la pregunta:

¿Hay alguna alternativa específica al GOF Singleton Pattern?

Por ejemplo, muchas veces cuando he usado el patrón de singleton en el pasado, simplemente me interesa preservar el estado / los valores de una o varias variables. Sin embargo, el estado / los valores de las variables se pueden conservar entre cada creación de instancias de la clase utilizando variables estáticas en lugar de usar el patrón de singleton.

¿Qué otra idea tienes?

EDITAR: Realmente no quiero que esta sea otra publicación sobre " cómo usar el singleton correctamente. " Una vez más, estoy buscando maneras de evitarlo. Por diversión, ¿vale? Supongo que estoy haciendo una pregunta puramente académica con la mejor voz de tu tráiler de película, "En un universo paralelo donde no hay singleton, ¿qué podríamos hacer?",

¿Fue útil?

Solución

Alex Miller en " Patterns I Hate " ; cita lo siguiente:

" Cuando un singleton parece ser la respuesta, a menudo es más sabio:

  1. Cree una interfaz y una implementación predeterminada de su singleton
  2. Construya una instancia única 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 definido de varias maneras dependiendo de su sistema.
  3. Pase la instancia única a cada componente que la necesite (inyección de dependencia)

Otros consejos

Para comprender la forma correcta de solucionar los Singletons, debe comprender qué está mal con Singletons (y el estado global en general):

Singletons ocultar dependencias.

¿Por qué es eso importante?

Porque Si oculta las dependencias, tiende a perder de vista la cantidad de acoplamiento.

Podrías argumentar que

void purchaseLaptop(String creditCardNumber, int price){
  CreditCardProcessor.getInstance().debit(creditCardNumber, amount);
  Cart.getInstance().addLaptop();
}

es más sencillo 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.

Por lo tanto, la manera de solucionar Singletons no es utilizar variables estáticas o localizadores de servicio, sino cambiar las clases Singleton en instancias, que se crean instancias en el ámbito en el que tienen sentido y se inyectan en los componentes y métodos que los necesitan. . Puede usar un IoC-framework para manejar esto, o puede hacerlo manualmente, pero lo importante es deshacerse de su estado global y hacer explícitas las dependencias y colaboraciones.

La mejor solución que he encontrado es usar el patrón de fábrica para construir instancias de tus clases. Al usar 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 manejar, pero después de leer esta publicación del blog " Where Have All the Singletons Gone? " , parece muy natural. Además, ayuda mucho a aislar las pruebas de unidad.

En resumen, ¿qué necesitas hacer? Cuando un objeto depende de otro, recibirá una instancia de él solo a través de su constructor (no hay palabras clave nuevas 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 va a crear una instancia de su fábrica solo una vez, habrá una única instanciación de exSingleton. Cada vez que llames a buildNeedy, la nueva instancia de NeedyClass se incluirá con exSingleton.

Espero que esto ayude. Por favor, señale cualquier error.

Spring o cualquier otro IoC-Container hace un trabajo razonablemente bueno en eso. Dado que las clases se crean y administran fuera de la aplicación, el contenedor puede crear clases simples simples e inyectarlas cuando sea necesario.

No deberías tener que salir 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 encaja en su lugar). Cuando diseña un sistema, tiene la opción de usar un patrón o no usarlo. Sin embargo, no debe hacer todo lo posible para evitar cualquier cosa que sea, en última instancia, una opción de diseño.

No evito el patrón de Singleton. O es apropiado y lo uso o no es apropiado y no lo uso. Creo que es tan simple como eso.

La adecuación (o falta de ella) del Singleton depende de la situación. Es una decisión de diseño que debe tomarse y las consecuencias de esa decisión deben entenderse (y documentarse).

El patrón singleton existe porque hay situaciones en las que se necesita un único objeto para proporcionar un conjunto de servicios .

Incluso si este es el caso, sigo considerando el enfoque de crear singletons mediante el uso de un campo / propiedad estática global que representa la instancia, inapropiada. Es inapropiado porque crea una dependencia en el código entre el campo estático y el objeto no, los servicios que proporciona el objeto.

Por lo tanto, en lugar del patrón de singleton clásico, recomiendo usar el patrón 'me gusta' del servicio con contenedores con servicio , donde en lugar de usar su singleton a través de un campo estático, obtiene una referencia a este 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 un solo global

*pseudocode* singletonType.Instance

De esta manera, cuando desee cambiar el tipo de un objeto de singleton a otra cosa, tendrá un tiempo fácil para hacerlo. Además, como un beneficio adicional, no tiene que pasar todas las instancias de objetos a cada método.

También vea Inversión de control , la idea es que al exponer singletons directamente al consumidor, creará una dependencia entre el consumidor y la instancia del objeto, no los servicios de objeto 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 deseable.

Monostate (descrito en el desarrollo de software ágil de Robert C. Martin) es una alternativa al singleton. En este patrón, los datos de la clase son todos estáticos, pero los captadores / definidores 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.

Si está utilizando un Singleton para representar un único objeto de datos, podría pasar un objeto de datos como un parámetro de método.

(aunque, diría que esta es la forma incorrecta de usar un Singleton en primer lugar)

Si su problema es que desea mantener el estado, desea 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 retiene a través de eso. Es probable que su MumbleManager contenga una bolsa de propiedades que contiene su estado.

Este tipo de estilo se siente muy parecido a C y no muy parecido a un objeto: encontrará que todos los objetos que definen su sistema tendrán una referencia al mismo MumbleManager.

Utilice un objeto plano y un objeto de fábrica. La fábrica es responsable de vigilar la instancia y los detalles del objeto simple solo con la información de configuración (que contiene, por ejemplo) y el comportamiento.

En realidad, si diseña desde cero para evitar 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 diferentes instancias de objetos, sin embargo, internamente todas se comportan como si estuvieran usando un Singleton.

¿Puede dar un ejemplo detallado en el que usa un Singleton o en el que actualmente se usa un Singleton y está tratando de evitar usarlo? Esto podría ayudar a las personas a encontrar una solución más sofisticada sobre cómo podría manejarse la situación sin un 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 en ellos. Es decir, si no estás abusando de ellos. Se puede abusar de todas las técnicas útiles y, si se abusa de ellas, se obtendrán resultados negativos. Otra técnica que es comúnmente mal utilizada es la herencia. Aún así, nadie diría que la herencia es algo malo solo porque algunas personas la maltratan horriblemente.

Personalmente para mí & # 1072; Una forma mucho más sensata de implementar algo que se comporte como singleton es usar una clase completamente 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)

Creo que el mejor lugar para vigilar al singleton es en el nivel de diseño de la clase. En esta etapa, debería poder trazar un mapa de las interacciones entre clases y ver si algo en absoluto requiere, definitivamente, que solo exista una 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 como una conveniencia durante la codificación, entonces realmente debería revisar su diseño y también detener la codificación de dichos singletons :)

Y sí, "policía" es la palabra que quiero decir aquí en lugar de "evitar". El singleton no es algo que deba evitarse (de la misma manera que goto y las variables globales no son algo que deba evitarse). En su lugar, debe supervisar su uso y asegurarse de que sea el mejor método para lograr que lo que quiere hacer sea efectivo.

Utilizo 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 instanciación 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 " por lo tanto contenido.

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 de "caja negra" que se inicializaba automáticamente y no tenía que pasar las llamadas de función hacia arriba y hacia abajo en un marco incompleto y algo roto.

Después de leer algunos enlaces de patrones singleton, no estoy completamente seguro de que lo implementaría de la misma manera otra vez. Lo que realmente se necesitaba era varios objetos con almacenamiento compartido (por ejemplo, el identificador de la base de datos real) y esto es prácticamente lo que convirtió mi llamada.

Como la mayoría de los patrones y algoritmos, usar un singleton 'solo porque es genial' es The Wrong Thing To Do. Necesitaba una llamada de "caja negra" que pareciera un singleton. Y en mi opinión, esa es la manera de abordar la cuestión: tenga en cuenta el patrón, pero también observe su alcance más amplio y en qué nivel debe ser única la instancia.

¿Qué quieres decir con cuáles son mis técnicas para evitarlo?

Para " evitar " Eso implica que hay muchas situaciones con las que me encuentro en las que el patrón de singleton es un buen ajuste natural, 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.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top