2012-01-31 8 views
7

Parece haber un estigma en SO con respecto al uso de Singletons. Nunca lo he aceptado personalmente, pero en aras de la mentalidad abierta intento dar una oportunidad a los conceptos de IoC porque estoy francamente aburrido de mi trabajo diario y me gustaría probar algo diferente. Perdóname si mi interpretación de los conceptos de la IoC es incorrecta o está mal dirigida.Ventaja de IoC sobre mi Factory Singleton

Aquí está la situación: estoy construyendo un simple servidor web basado en HttpListener en un servicio de Windows que utiliza un modelo de complemento para determinar cómo debe manejarse una solicitud basada en la URL solicitada (como todos los que preguntan HttpListener). Mi enfoque para descubrir los complementos es consultar un directorio configurado para conjuntos decorados con un HttpModuleAssemblyAttribute. Estos ensambles pueden contener 0 o más IHttpModule niños que además están decorados con un HttpModuleAttribute utilizado para especificar el nombre del módulo, la versión, la descripción legible por el ser humano y otra información. Algo así como:

[HttpModule(/*Some property values that matter */)] 
public class SimpleHttpModule : IHttpModule 
{ 
    public void Execute(HttpListenerContext context) 
    { 
     /* Do Something Special */ 
    } 
} 

Cuando se descubre un HttpModule que normalmente se sumaría a un objeto Dictionary<string, Type> que es único propósito es mantener un registro de los módulos que conocemos. Este diccionario típicamente viviría en mi variedad de Singleton que toma la personalidad de ACE style Singleton (un legado de mis días en C++ donde aprendí sobre Singletons).

Ahora lo que estoy tratando de implementar es algo similar usando (mi entendimiento de) conceptos generales de IoC. Básicamente lo que tengo es una colección AppService donde IAppService se define como:

public interface IAppService : IDisposable 
{ 
    void Initialize(); 
} 

Y mi plug-in AppService sería algo como:

[AppService("Plugins")] 
internal class PluginAppService : IAppService, IDictionary<string, Type> 
{ 
    /* Common IDictionary Implementation consisting of something like: */ 
    internal Type Item(string modName) 
    { 
     Type modType; 
     if (!this.TryGetValue(modName, out modType) 
      return null; 

     return modType; 
    } 

    internal void Initialize() 
    { 
     // Find internal and external plug-ins and add them to myself 
    } 

    // IDisposable clean up method that attempts to dispose all known plug-ins 
} 

A continuación, durante el servicio OnStart que se crea una instancia de AppServices cuales es conocido localmente pero se transfiere al constructor de todos los complementos instanciados:

public class AppServices : IDisposable, IDictionary<string, IAppService> 
{ 
    /* Simple implementation of IDictionary */ 

    public void Initialization() 
    { 
     // Find internal IAppService implementations, instantiate them (passing this as a constructor parameter), initialize them and add them to this. 

     // Somewhere in there would be something like 
     Add(appSvcName, appSvc); 
    } 
} 

Nuestra implementación del método, una vez sola se convierte en una implementación abstracta + un constructor en el niño:

[HttpModule(/*Some property values that matter */)] 
public abstract class HttpModule : IHttpModule 
{ 
    protected AppServices appServices = null; 
    public HttpModule(AppServices services) 
    { 
     appServices = services; 
    }    

    public abstract void Execute(HttpListenerContext context); 
} 

[HttpModule(/*Some property values that matter */)] 
public class SimpleHttpModule : HttpModule 
{ 
    public SimpleHttpModule(AppServices services) : base(services) { } 
    public override void Execute(HttpListenerContext context) 
    { 
     /* Do Something Special */ 
    } 
} 

Y cualquier acceso a los servicios de aplicaciones de uso común se convierte en:

var plugType = appServices["Plugins"][plugName]; 

en lugar de:

var plugType = PluginManager.Instance[plugName]; 

¿Me falta aquí algún concepto básico de IoC que simplifique todo esto o hay realmente un beneficio para todo este código adicional? En mi mundo, Singletons son criaturas simples que permiten que el código de un programa acceda a la información necesaria (relativamente estática) (en este caso, tipos).

plantear la pregunta de manera más explícita:

  1. ¿Es esta una aplicación válida de un Singleton fábrica traducido a conceptos IOC/DI?
  2. En caso afirmativo, ¿dónde está la recuperación/beneficio del código adicional requerido y la imposición de una API aparentemente más torpe?
+0

Probablemente no recibas la respuesta que estás buscando porque tu pregunta es demasiado vaga (a pesar de ser tan larga). Realmente debería figurar EXACTAMENTE qué pregunta quiere que se responda y preguntar. –

+0

Creo que la reacción general contra los singletons se debe a que es un método para introducir el estado global en una aplicación, lo que va en contra del diseño orientado a objetos. También suelen ser difíciles de probar por unidad. –

+0

@DBM: ¿El estado global es contrario a OOP? ¿Cómo es eso? En general, es un enfoque aceptado en C++ (que también es OO). –

Respuesta

1

Si bien aún no estoy muy convencido de que IoC/DI sea mejor en esta situación, definitivamente he visto beneficios a medida que el alcance del proyecto se deslizaba. Para cosas como el registro y la configurabilidad, sin duda es el enfoque correcto.

Espero experimentarlo más en futuros proyectos.

1

IoC se vuelve más interesante cuando se llega a las pruebas de unidades de escritura. Perdón por responder una pregunta con más preguntas, pero ... ¿Cómo serían las pruebas unitarias para ambas implementaciones? ¿Sería capaz de probar las clases que usaron el PluginManager sin buscar ensambles desde el disco?

EDITAR

El hecho de que se puede conseguir la misma funcionalidad con embarazos únicos no quiere decir que sea tan fácil de mantener. Al usar IoC (al menos este estilo con constructores), usted declara explícitamente las dependencias que tiene un objeto. Al usar singletons esa información está oculta dentro de la clase. También hace que sea más difícil reemplazar esas dependencias con implementaciones alternativas.

Por lo tanto, con un singleton PluginManager sería difícil probar su servidor HTTP con complementos falsos, en lugar de buscarlos desde alguna ubicación en el disco. Con la versión de IoC, podría pasar una versión alternativa del IAppService que solo mira los complementos desde un Dictionary previamente rellenado.

+0

Basado en la naturaleza de un marco de plugin (incluso más específicamente el que he definido aquí), los simulacros son fáciles de implementar porque solo tienen que estar decorados con un 'HttpModuleAttribute' con el mismo nombre e implementar la interfaz.El marco es ignorante más allá de eso. De forma realista, ambas implementaciones indican las dependencias porque ambas hacen cumplir el requisito de que se implemente 'IHttpModule' o' HttpModule'. Posiblemente ejemplos más concretos de deficiencias ayudarían. –

+0

¿Pero para qué sirve el 'PluginManager'? Ahí es donde verías la diferencia en mi humilde opinión. – SimonC

+0

El marco en sí utiliza 'PluginManager' en función de la URL recibida. 'http: // server.com/moduleName/someFile.txt' sería algo consumido por el framework y' moduleName' sería el nombre del módulo registrado con 'PluginManager' o' PluginAppService'. De cualquier manera, su interacción con él es la misma. –

2

IoC es un término genérico. Inyección de dependencia es el término más preferido en estos días.

Dependency Injection realmente brilla en varias circunstancias. En primer lugar, define una arquitectura más comprobable que las soluciones que tienen instancias codificadas de dependencias. Los Singletons son difíciles de probar por unidad porque son estáticos, y los datos estáticos no se pueden "descargar".

En segundo lugar, Inyección de dependencias no solo crea la instancia del tipo que desea, sino de todos los tipos dependientes. Por lo tanto, si la clase A necesita clase B y la clase B necesita clase C y D, un buen marco de DI creará automáticamente todas las dependencias y controlará sus tiempos de vida (por ejemplo, hacerlas vivir durante la vida de una única solicitud web).

DI Los contenedores se pueden considerar como fábricas genéricas que pueden crear instancias de cualquier tipo de objeto (siempre que esté configurado correctamente y cumpla con los requisitos del marco DI). Entonces no tienes que escribir una fábrica personalizada.

Al igual que con cualquier solución genérica, está diseñado para dar el 90% de los casos de uso de lo que necesitan. Claro, podría crear una estructura de datos de listas vinculadas personalizadas a mano cada vez que necesite una colección, pero 90 =% de las veces una genérica funcionará bien. Lo mismo es cierto para DI y Custom Factories.

+0

Agradezco la información, aunque estoy confundido sobre cómo se aplica a las circunstancias especificadas en la pregunta? He leído artículos sobre Dependency Injection e Inversion of Control, pero he utilizado este modelo en todos los idiomas con experiencias positivas de Unit Testing. ¿Por qué cambiar ahora? –

+0

Dicho de otra manera, ¿qué pasa con mi escenario descrito hace que DI sea una mejor opción? ¿Me estoy perdiendo el concepto? –

+1

También debe tenerse en cuenta que existe una diferencia entre Inversión de control (IoC) e Inyección de dependencia (DI), http://stackoverflow.com/questions/4596300/where-excaly-is-the-difference-between-ioc -y-di –

Cuestiones relacionadas