Alternativas a Singletons / globals


16

He escuchado innumerables veces sobre las trampas de Singletons / globals, y entiendo por qué a menudo están mal vistos.

Lo que no entiendo es cuál es la alternativa elegante y no desordenada. Parece que la alternativa al uso de Singletons / globals siempre implica pasar objetos un millón de niveles hacia abajo a través de los objetos del motor hasta que lleguen a los objetos que los necesitan.

Por ejemplo, en mi juego, precargo algunos recursos cuando se inicia el juego. Estos recursos no se utilizan hasta mucho más tarde cuando el jugador navega por el menú principal y entra al juego. ¿Se supone que debo pasar estos datos de mi objeto Juego a mi objeto ScreenManager (a pesar de que solo una Pantalla realmente se preocupa por estos datos), luego al objeto Pantalla apropiado y en cualquier otro lugar?

Simplemente parece que estoy intercambiando datos de estado global por inyección de dependencia desordenada, pasando datos a objetos que ni siquiera se preocupan por los datos, excepto con el propósito de pasarlos a objetos secundarios.

¿Es este un caso en el que un Singleton sería algo bueno, o hay alguna solución elegante que me falta?

Respuestas:


16

No mezcle singletons y globals. Si bien generalmente es necesario algún tipo de variables globales, el singleton no es solo un reemplazo de una variable global, sino principalmente una forma de solucionar problemas de orden de inicialización estática en C ++ ( y FQA ). (En otros idiomas, es una forma de solucionar diferentes deficiencias de lenguaje, como la falta de variables globales y funciones básicas).

Si solo puede usar un puntero global en lugar de un singleton, y asegurarse de que se inicializa (manualmente) antes de que algo lo necesite, evite la llamada a la función y la sobrecarga de la rama, la sintaxis lame para llegar al objeto, y realmente puede hacer un segunda instancia de la clase cuando lo necesita para las pruebas o porque su diseño cambió.

Para las pocas variables globales que desea (ejemplos comunes son salida de audio, lista de ventanas abiertas, controlador de teclado, etc.), recomiendo el patrón de localización del servicio . Facilita la sustitución de cosas con diferentes implementaciones (p. Ej., Dispositivo de audio real frente a nulo) y reúne todas sus variables globales en una estructura para evitar contaminar su espacio de nombres.


+1. Buena respuesta y gracias por el enlace de patrón del localizador de servicios. Esa es una lectura muy interesante.
bummzack

1

Si no puede / no puede tener una parte del código mágicamente "saber" acerca de algunos datos, entonces tendrá que pasar de alguna manera. Sin embargo, eso no significa que necesariamente deba pasarse solo a través de argumentos.

En su caso de ejemplo, ¿no podría tener algún tipo de "AssetManager" que cargaría y almacenaría los activos, y luego el ScreenManager solo necesitaría una referencia a eso (probablemente en la creación)? En ese sentido, está pasando las referencias a los activos envueltos en otro objeto, y puede pasar eso una vez, en la inicialización, en lugar de pasarlo a la función de hoja cuando sea necesario.

Ahora, en mi humilde opinión, ese AssetManager, siendo el tipo de cosas de las que solo quieres una, podría ser un singleton. Siempre que comprenda las trampas y codifique específicamente para evitarlas (suponga que se accederá a singleton simultáneamente desde múltiples subprocesos y apuñale con un tenedor cada vez que haga algo que deba bloquear), luego déjese inconsciente.


1

Creo que Jason D tiene toda la razón: así es como lo manejaría:

El juego tiene una instancia de AssetManager, un objeto del que puede obtener cualquier activo por su nombre.

En el juego:

assetManager = new AssetManager();
screenManager = new ScreenManager();
screenManager.assetManager = assetManager;

En ScreenManager:

screen = new Screen();
screen.assetManager = assetManager;

En pantalla:

myAsset = assetManager.getBitmp("lava.png");

Ahora todas las pantallas tienen acceso a los activos que necesitan. Esto no es más complejo o loco que usar globals o Singletons, y tiene la opción de tener 2 instancias de Juego ejecutándose en la misma aplicación sin conflictos. Una vez tuve que hacer un juego que estaba compuesto por 8 minijuegos, todos compartiendo las mismas clases / framework base. Tuve que refactorizar todos mis globals / singletons para usar este estilo de aprobación de referencia, y nunca he mirado hacia atrás. Las únicas cosas que deberían ser globales son cosas que solo pueden existir físicamente una vez, como audio, redes, E / S, etc.


0

Puede usar el patrón Factory para reemplazar Singleton . Luego, la clase de fábrica tiene el control sobre cuántas instancias puede crear, que puede cambiar fácilmente más adelante cuando encuentre que necesita más de una AssetManager. Como se indica en este artículo :

Le brinda toda la flexibilidad del Singleton, sin tener tantos problemas.


Otra posibilidad bastante limitada es hacer que la clase sea estática (lo cual no creo que sea factible para un AssetManager y solo es posible en lenguajes que tengan clases estáticas). Pero eso solo funciona si no necesita herencia / polimorfismo. Es una solución muy inflexible:

Los métodos estáticos son tan flexibles como el granito. Cada vez que usa uno, está proyectando parte de su programa en concreto. Solo asegúrate de no tener el pie atascado allí mientras lo observas endurecerse. Algún día te sorprenderá que, por Dios, realmente necesitas otra implementación de esa clase PrintSpooler, y debería haber sido una interfaz, una fábrica y un conjunto de clases de implementación. D'oh!

Se trata de métodos estáticos, pero también se puede aplicar a clases estáticas.


¿Qué quieres decir con "hacer que la clase sea estática"? C ++ no tiene clases estáticas. ¿Quieres decir que solo tienes métodos estáticos? ¿Por qué molestarse en tener una clase en lugar de un espacio de nombres?

1
@ Joe: Bueno, la pregunta no se centra en C ++ como lo entendí. En C # o Java puedes hacer clases estáticas y me estoy refiriendo a ellas. Además, como dije, las clases estáticas no son una solución óptima la mayor parte del tiempo, pero en casos excepcionales podría funcionar como un global.
Michael Klement
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.