2011-12-21 6 views
9

No soy un enemigo de los singleton, pero sé que se abusan y por esa razón quiero aprender a evitar usarlos cuando no los necesite.Necesito ayuda para evitar el uso de un Singleton

Estoy desarrollando una aplicación multiplataforma (Windows XP/Vista/7, Windows Mobile 6.x, Windows CE5, Windows CE6). Como parte del proceso, estoy rediseñando el código en proyectos separados, para reducir la duplicación de código y, por lo tanto, la posibilidad de corregir los errores del sistema inicial.

Una parte de la aplicación que se separa es bastante simple, es un administrador de perfiles. Este proyecto es responsable de almacenar perfiles. Tiene una clase Profile que contiene algunos datos de configuración que utilizan todas las partes de la aplicación. Tiene una clase ProfileManager que contiene Profiles. El ProfileManager leerá/guardará Profiles como archivos XML separados en el disco duro, y permitirá que la aplicación recupere y configure el "activo" Profile. Sencillo.

En la primera compilación interna, la GUI era la SmartGUI antipatrón. Era una implementación de WinForms sin MVC/MVP realizada porque queríamos que funcionara antes en lugar de estar bien diseñada. Esto condujo a ProfileManager siendo un singleton. Esto era así desde cualquier lugar de la aplicación, la GUI podía acceder al Profile activo.

Esto significa que podría ir a ProfileManager.Instance.ActiveProfile para recuperar la configuración de diferentes partes del sistema según sea necesario. Cada GUI también podía hacer cambios en el perfil, por lo que cada GUI tenía un botón para guardar, por lo que todos tenían acceso al método ProfileManager.Instance.SaveActiveProfile() también.

No veo nada de malo en el uso del singleton aquí, y porque no veo nada de malo en él, sin embargo, sé que los singletons no son ideales. ¿Hay alguna manera mejor de manejar esto? ¿Se debería pasar una instancia de ProfileManager a cada controlador/presentador? Cuando se crea el ProfileManager, deben crearse otros componentes principales y registrarse en los eventos cuando se cambian los perfiles. El ejemplo es bastante simple, y probablemente sea una característica común en muchos sistemas, así que piense que este es un excelente lugar para aprender a evitar los singletons.

P.s. Tengo que compilar la aplicación contra Compact Framework 3.5, que limita muchas de las clases normales de .Net Framework que se pueden usar.

+6

Tal vez me quede atrapado en mis viejos hábitos, pero veo esto como un buen ejemplo de dónde se debe utilizar un Singleton. –

+0

Esto realmente suena como el caso ideal para una instancia singleton. – msarchet

+0

Gracias por todas las respuestas, parece que este es un lugar apropiado para usar un Singleton. Se tendrán en cuenta los comentarios sobre la inyección de Factory/Dependency, sin embargo, como esta aplicación se ejecutará en segundo plano, es probable que la deje como singleton tanto por la simplicidad como por el rendimiento. – JonWillis

Respuesta

8

Una de las razones por las cuales los singletons son difamados es que a menudo actúan como un contenedor para estado global, compartido y algunas veces mutable. Los Singleton son una gran abstracción cuando su aplicación realmente necesita acceso a estado global compartido: su aplicación móvil que necesita acceder al micrófono o la reproducción de audio necesita coordinar esto, ya que solo hay un conjunto de altavoces, por ejemplo.

En el caso de su aplicación, tiene un único perfil "activo", que las diferentes partes de la aplicación deben poder modificar. Creo que debes decidir si el perfil del usuario realmente se ajusta o no a esta abstracción. Dado que la manifestación de un perfil es un único archivo XML en el disco, creo que está bien tenerlo como singleton.

Creo que deberías usar la inyección de dependencia o un patrón de fábrica para conseguir un administrador de perfil. Solo necesita escribir una prueba unitaria para una clase que requiere el uso de un perfil para comprender la necesidad de esto; desea poder pasar un perfil creado por programación en tiempo de ejecución, de lo contrario su código tendrá una estrecha dependencia de algún archivo XML en algún lugar del disco.

+0

La reescritura parcial se probará en unidades, no es un punto fuerte mío, pero lo intentaré, aún estoy aprendiendo sobre TDD. Todavía no se han probado los singleton, pero supongo que el problema es que la instancia (método o propiedad) es estática, por lo que no se puede burlar/troquelar fácilmente, ya que no se puede anular la estática, que es cómo funcionan los simulacros (añadiendo una prueba subclase generada de la clase que se burla). – JonWillis

2

"Singletons" realmente solo son malos si se usan esencialmente para reemplazar variables "globales". En este caso, y si para eso se usa, no es necesariamente Singleton de todos modos.

En el caso que describa, está bien, y de hecho es ideal para que su aplicación pueda estar segura de que Profile Manager está disponible para todos los que lo necesitan y que ninguna otra parte puede crear una instancia adicional entrará en conflicto con el existente. Esto reduce los parámetros/campos adicionales desagradables en todas partes también, donde está tratando de pasar alrededor de una instancia y luego mantener referencias adicionales innecesarias a la misma. Siempre que se forme en una y solo una instanciación, no veo nada de malo en ello.

Singleton fue diseñado para evitar múltiples instancias y un solo punto de "entrada". Si eso es lo que quieres, ese es el camino a seguir. Solo asegúrate de que esté bien documentado.

+0

Gracias por su contribución, mis preocupaciones con respecto a no tener un "Singleton" y solo una "Referencia Única", es como usted indica, teniendo parámetros adicionales en muchos métodos. Que eventualmente se convierte en un negocio desordenado. – JonWillis

5

Una cosa a considerar es tener una interfaz para su ProfileManager, y pasarle una instancia al constructor de cada vista (o cualquier cosa) que la use. De esta forma, puede tener fácilmente un singleton, o una instancia por subproceso/usuario/etc., o tener una implementación que vaya a una base de datos/servicio web/etc.

Otra opción sería hacer que todas las cosas que usan ProfileManager llamen a fábrica en lugar de tener acceso directo a ellas. Entonces esa fábrica podría devolver una instancia, nuevamente podría ser un singleton o no (ir a la base de datos o archivo o servicio web, etc., y la mayoría de su código no necesita saber.

No responde a su pregunta directa, pero hace que el impacto de un cambio en el futuro sea casi nulo.

+0

Consideraba una interfaz, pero sabía que solo habría un tipo de ProfileManager, así que decidí no hacerlo. Usar Factory methods fue otro tema considerado, pero dudo en sugerirlo porque siento que estoy agregando código que simplemente devuelve un singleton de todos modos, pero entiendo si las implementaciones cambian, entonces solo actualizo la referencia de fábrica, en lugar de cada pieza de código usando ese singleton. Cheers – JonWillis

Cuestiones relacionadas