2009-08-16 28 views
29

Tengo muchas fábricas (abstractas) y generalmente se implementan como singletons.fábricas "Singleton", ¿está bien o mal?

Por lo general, para la comodidad de no tener que pasar por capas que realmente no tienen nada que ver con el uso o el conocimiento de estas fábricas.

mayoría de las veces sólo tengo que tomar una decisión en el arranque de los cuales implementación de fábrica el resto del programa de código, tal vez a través de una cierta configuración

se ve, por ejemplo, como

abstract class ColumnCalculationFactory { 
    private static ColumnCalculationFactory factory; 

public static void SetFactory(ColumnCalculationFactory f) { 
     factory = f; 
    } 

    public static void Factory() { 
     return factory; 
    } 

public IPercentCalculation CreatePercentCalculation(); 
public IAverageCalculation CreateAverageCalculation(); 
    .... 

} 

calle detrás olor hacer sobre esto, pero no estoy seguro de lo que - es tal vez más de un mundial disuised que un producto único. No es realmente realmente tener que ser una sola fábrica creando ColumnCalculations, aunque mis programas no necesitan más.

¿Esto se considera mejor practuce? ¿Debería rellenarlos en alguna clase de AppContext (semi) global? ¿Algo más (no estoy listo para cambiar a un contenedor de IoC más grande, o spring.net aún por cierto)?

Respuesta

11

Realmente depende de lo que esté haciendo y del alcance de su aplicación. Si se trata de una aplicación bastante pequeña y nunca va a crecer más allá de esto, entonces su enfoque actual bien puede estar bien. No existe una "mejor" práctica universal para estas cosas. Si bien no recomendaría el uso de singletons para nada que no sean métodos de hojas sin estado y/o llamadas unidireccionales (por ejemplo, el registro), descartarlo de inmediato 'solo porque' es un singleton no es necesariamente lo correcto.

Para cualquier cosa que no sea un código trivial o de prototipo, personalmente me gusta usar explícitamente la inversión de control con la inyección de constructor, ya que significa que todas las dependencias son contabilizadas y no obtienes ninguna 'sorpresa'. El compilador no le permitirá crear instancias A sin B y B sin C. Singletons inmediatamente enterrará estas relaciones: puede crear instancias A sin B y B sin C. Cuando se produce la llamada de A a B, obtendrá una referencia nula excepción.

Esto es especialmente molesto cuando se prueban, ya que tiene que trabajar iterativamente hacia atrás a través de fallas en el tiempo de ejecución. Cuando prueba el código, está utilizando la API como haría un codificador compañero, por lo que es indicativo de los problemas de diseño con este enfoque. La inyección de Constructor garantiza que esto nunca ocurra; todas las dependencias se mencionan por adelantado. La desventaja con la inyección de constructor es que la configuración de su gráfico de objetos es más complicada. Esto se mitiga mediante el uso de un contenedor IoC.

Supongo que lo que estoy tratando de decir es que si ha llegado al punto en el que está considerando utilizar algún tipo de objeto de contexto y un patrón de registro, también puede echar un vistazo a los contenedores de IoC. Hacer el esfuerzo de rodar su propia versión de mutt es probablemente una pérdida de tiempo cuando puede usar un producto establecido y gratuito como Autofac.

3

¿Esto se considera la mejor práctica?

no veo ningún problema: usted dijo "mis programas no necesitan más", por lo tanto, su nada más flexible/abstract implementación podría ser un caso de You Ain't Gonna Need It.

6

No, porque lo que estás haciendo aquí es crear un estado global. Hay todo tipo de problemas con el estado global; el principal de ellos es que una función depende de manera bastante invisible del comportamiento de otras funciones. Si una función llama a otra función que olvida almacenar y restaurar el factory antes de que termine, entonces tiene un problema porque ni siquiera puede recuperar el valor anterior a menos que lo haya almacenado en alguna parte. Y tiene que agregar código para hacer eso (a juzgar por su código, supongo que está en un idioma con finally, lo que deja aún más margen para errores). Lo que es más, si termina con un código que necesita cambiar entre dos fábricas rápidamente para dos subobjetos, debe escribir una llamada a método en cada punto; no puede almacenar el estado en cada subobjeto (bueno, puede, pero luego usted derrota el propósito del estado global [que ciertamente no es mucho]).

Probablemente tenga más sentido almacenar el tipo de fábrica como miembro y pasarlo al constructor de nuevos objetos que lo necesiten (o crear uno nuevo según sea necesario, etc.). También le da un mejor control: puede garantizar que todos los objetos construidos por el objeto A pasen por la misma fábrica, o puede ofrecer métodos para intercambiar fábricas.

+0

sin embargo, dado el público expuesto SetFactory no hay garantía de que alguien no pueda cambiar más lío con la fábrica, por lo general parezco configurar la fábrica solo en el arranque, basado en una configuración o inicial inicial. – leeeroy

+0

luego caes presa del inconveniente de los singletons ... son singletons. – coppro

4

Recomendaría no hacerlo, porque hace que sea muy difícil escribir pruebas de unidades decentes para el código que llama a estas fábricas.

Misko Hevery tiene un nice article acerca de esto en su blog.

+0

Con el código anterior, podría burlarse fácilmente de una fábrica. – nos

+0

Claro, pero ¿sabría al instante de cuáles se debe burlar, en una aplicación no trivial en la que no conoce las dependencias de clase de memoria? De nuevo, me refiero al artículo de Misko. – jqno

+0

Misko también tiene algunos excelentes videos en youtube sobre este tema, http://www.youtube.com/watch?v=acjvKJiOvXw – Despertar

11

Tener unos pocos singletons es bastante típico y no suele ser problemático: una gran cantidad de singleton conduce a un código irritante.

Acabamos de pasar por una situación en la que tuvimos que poner a prueba nuestras clases con mucha carga de singleton. El problema es que cuando pruebas la clase b y obtiene la clase c (un singleton), no tienes forma de burlar la clase c (Al menos EasyMock no nos permite reemplazar el método de fábrica estático de una clase singleton).

Una solución simple es tener "Setters" para todos sus singletons con fines de prueba. No realmente recomendado.

Otra cosa que probamos fue tener una sola clase que tuviera todos los singletons: un registro. Hacer esto se está acercando bastante a la inyección de dependencia, que es lo que seguramente debería estar usando.

Además de las pruebas, aprendí hace mucho tiempo que nunca va a haber más de una instancia de un objeto dado; en la próxima versión, a menudo quieren dos, lo que hace que los singletons sean MUCHO mejores que las clases estáticas; al menos, puede agregar un parámetro al getter de un singleton y devolver un segundo sin demasiada refactorización (que nuevamente solo hace lo que hace DI))

En cualquier caso, fíjate en DI, podrías estar muy feliz.

+0

De acuerdo. Otra consecuencia de los singleton es la "fuga" de los valores establecidos durante las pruebas unitarias. Tuvimos que dar como resultado la bifurcación de JVM para cada prueba, ya que una prueba parcial única o simplemente utilizada por una prueba estaba en el estado A, y en sentido descendente debía estar en el estado B. – Trenton

+0

El problema con la TDD extensa es que usted a menudo complican el código y lo hacen más propenso a errores por el simple hecho de usar herramientas de prueba. Creo firmemente que si debe cambiar la arquitectura por el mofetismo, está usando en exceso Mocking, y en su lugar debería usar una configuración asumida de Factory/Singleton. La cobertura del código del 100% es una locura y causará más problemas y complejidad de la que resolverá. – user3225313

+0

Estoy de acuerdo con TDD, pero esa no es la única razón para usar DI. Además, el código TESTABLE generalmente será mejor que el código no comprobable, independientemente de la existencia real de las pruebas. Tener DI en lugar de un singleton no siempre es malo y puede ser realmente bueno en el caso en que realmente desee probarlo, incluso si no tiene una cobertura de prueba del 100%. –

1

La mala forma de Singleton es el que implementa el equivalente de su método de fábrica con:

return new CalculationFactory(); 

Eso es mucho más difícil de rutas internas, maqueta o una envoltura.

La versión que devuelve un objeto creado en otro lugar es mucho mejor, aunque, como casi todo, puede utilizarse mal o usarse en exceso.