13

Algo que me ha estado molestando desde que leí una respuesta en otra pregunta de stackoverflow (la que ahora se me escapa) donde un usuario dijo algo como "If you" Si llama al Localizador de servicios, lo está haciendo mal. "Inyección de Dependencia con Ninject, MVC 3 y usando el Patrón de Localizador de Servicio

Era alguien con una gran reputación (en los cientos de miles, creo) así que tiendo a pensar que esta persona podría saber de lo que están hablando . He estado usando DI para mis proyectos desde que empecé a aprender sobre él y qué tan bien se relaciona con las Pruebas de Unidad y lo que no. Es algo con lo que estoy bastante cómodo ahora y creo que Sé lo que estoy haciendo.

Sin embargo, hay muchos lugares donde he estado usando el Localizador de servicios para resolver dependencias en mi proyecto. Una vez, el mejor ejemplo proviene de las implementaciones de ModelBinder.

Ejemplo de una carpeta modelo típica.

public class FileModelBinder : IModelBinder { 
    public object BindModel(ControllerContext controllerContext, 
          ModelBindingContext bindingContext) { 
     ValueProviderResult value = bindingContext.ValueProvider.GetValue("id"); 

     IDataContext db = Services.Current.GetService<IDataContext>(); 
     return db.Files.SingleOrDefault(i => i.Id == id.AttemptedValue); 
    } 
} 

no es una verdadera aplicación - sólo un ejemplo rápido

Desde la implementación ModelBinder requiere una nueva instancia cuando una carpeta es primera solicitó, es imposible utilizar la inyección de dependencias en el constructor para esta implementación particular.

Es así en muchas de mis clases. Otro ejemplo es el proceso de caducidad de caché que ejecuta un método cada vez que un objeto de caché caduca en mi sitio web. Ejecuto un montón de llamadas a bases de datos y otras cosas. También estoy usando un Localizador de servicios para obtener la dependencia requerida.

Otro problema que tuve recientemente (que he publicado una pregunta aquí sobre) era que todos mis controladores requieren una instancia de IDataContext que utilicé para DI - pero un método de acción requiere una instancia diferente de IDataContext. Afortunadamente Ninject vino al rescate con una dependencia nombrada. Sin embargo, esto se sintió como un obstáculo y no como una solución real.

Pensé que, al menos, entendía el concepto de Separation of Concerns razonablemente bien, pero parece haber algo fundamentalmente erróneo en cómo entiendo Dependency Injection y el Service Locator Pattern, y no sé qué es eso.

La forma en que lo entiendo actualmente - y esto también podría estar mal - es que, al menos en MVC, ControllerFactory busca un Constructor para un Controlador y llama al Localizador de Servicios para obtener las dependencias requeridas y luego pasa ellos adentro. Sin embargo, puedo entender que no todas las clases y qué no tienen una fábrica para crearlas. Entonces me parece que algún patrón de Localizador de Servicio es aceptable ... pero ...

  1. ¿Cuándo no es aceptable?
  2. ¿Qué tipo de patrón debo tener en cuenta cuando debería replantearme cómo estoy usando el patrón del localizador de servicios?
  3. ¿Está mal la implementación de mi ModelBinder? Si es así, ¿qué necesito aprender para solucionarlo?
  4. En otra pregunta similar a la de este usuario Mark Seemann, recomendó una fábrica abstracta: ¿cómo se relaciona esto?

Supongo que es eso - No puedo pensar en ninguna otra pregunta para ayudar a mi comprensión, pero cualquier información adicional es muy apreciada.

Entiendo que DI puede no ser la respuesta a todo y que podría estar exagerando en la forma de implementarlo, sin embargo, parece funcionar de la manera que lo esperaba con Unit Testing y qué no.

No estoy buscando un código para corregir mi implementación de ejemplo. Estoy buscando aprender, buscando una explicación para corregir mi error de comprensión.

Ojalá stackoverflow.com tuviera la capacidad de guardar las preguntas del borrador. También espero que quien responda esta pregunta tenga la reputación adecuada para responder esta pregunta, ya que creo que estoy pidiendo mucho. Gracias por adelantado.

+0

Creo que te estás refiriendo a Mark Seeman y a esta publicación en el blog: http://blog.ploeh.dk/2010/02/03/ServiceLocatorIsAnAntiPattern.aspx –

Respuesta

14

considerar lo siguiente:

public class MyClass 
{ 
    IMyInterface _myInterface; 
    IMyOtherInterface _myOtherInterface; 

    public MyClass(IMyInterface myInterface, IMyOtherInterface myOtherInterface) 
    { 
    // Foo 

    _myInterface = myInterface; 
    _myOtherInterface = myOtherInterface; 
    } 
} 

Con este diseño Soy capaz de expresar los requisitos de dependencia para mi tipo. El tipo en sí no es responsable de saber cómo crear una instancia de cualquiera de las dependencias, sino que se le da (inyecta) por cualquier mecanismo de resolución que se use [típicamente un contenedor IoC]. Considerando que:

public class MyClass 
{ 
    IMyInterface _myInterface; 
    IMyOtherInterface _myOtherInterface; 

    public MyClass() 
    { 
    // Bar 

    _myInterface = ServiceLocator.Resolve<IMyInterface>(); 
    _myOtherInterface = ServiceLocator.Resolve<IMyOtherInterface>(); 
    } 
} 

Nuestra clase ahora depende de la creación de las instancias específicas, pero a través de la delegación a un localizador de servicios. En este sentido, Service Location se puede considerar anti-pattern porque no está exponiendo dependencias, pero está permitiendo que los problemas que puedan detectarse mediante la compilación se conviertan en tiempo de ejecución. (Una buena lectura es here). Usted esconde complejidades.

La elección entre uno u otro depende realmente de lo que construya y de los servicios que proporciona. Normalmente, si está creando una aplicación desde cero, elegiría DI todo el tiempo. Mejora el mantenimiento, promueve la modularidad y hace que los tipos de prueba sean mucho más fáciles. Pero, tomando ASP.NET MVC3 como ejemplo, podría implementar fácilmente SL ya que está integrado en el diseño.

Siempre puede optar por un diseño compuesto en el que pueda usar IoC/DI con SL, al igual que con el Common Services Locator. Sus componentes se pueden cablear a través de DI, pero expuestos a través de SL. Incluso podría incluir composiciones en la mezcla y usar algo como el Marco de Extensibilidad Administrada (que a su vez admite DI, pero también puede conectarse a otros contenedores IoC o localizadores de servicios). Es una gran elección de diseño, generalmente mi recomendación sería para IoC/DI cuando sea posible.

Su diseño específico no diría que está mal. En este caso, su código no es responsable de crear una instancia del archivador modelo, eso depende del marco, por lo que no tiene control sobre ese , pero su uso del localizador de servicios probablemente podría cambiarse fácilmente para acceder a un contenedor IoC . Pero la acción de llamar a resolver en el contenedor IoC ... ¿no consideraría la ubicación del servicio?

Con un patrón de fábrica abstracto, la fábrica está especializada en la creación de tipos específicos. No registra los tipos para la resolución, esencialmente registra una fábrica abstracta y crea los tipos que pueda necesitar. Con un Localizador de servicios está diseñado para localizar servicios y devolver esas instancias. Similar desde el punto de vista de una convención, pero muy diferente en comportamiento.

+0

Así es como lo uso actualmente. (El localizador de servicios comunes) Ninguna de mis clases ejemplifica sus propias dependencias cuando me es posible pasarlas. Tu última oración es en la que me estoy confundiendo porque esa es la misma pregunta que me he hecho varias veces. . Odio la idea de ver el 'Resolve' por todos lados. Simplemente * parece * Estoy haciendo algo mal ... – Buildstarted

+0

En esa situación es probablemente inevitable, desea mantener la solidez de una implementación desacoplada y, por lo tanto, no tiene control sobre la creación del tipo, la ubicación del servicio es una opción... –

Cuestiones relacionadas