¿Cuál es la diferencia entre los patrones de Inyección de dependencias y Localizador de servicios?

Ambos patrones parecen una implementación del principio de inversión de control. Es decir, que un objeto no debería saber construir sus dependencias.

La Inyección de Dependencias (DI) parece usar un constructor o establecedor para "inyectar" sus dependencias.

Ejemplo de uso de Constructor Injection:

//Foo Needs an IBar
public class Foo
{
  private IBar bar;

  public Foo(IBar bar)
  {
    this.bar = bar;
  }

  //...
}

Service Locator parece usar un "contenedor", que conecta sus dependencias y le da a foo su barra.

Ejemplo de uso de un localizador de servicios:

//Foo Needs an IBar
public class Foo
{
  private IBar bar;

  public Foo()
  {
    this.bar = Container.Get<IBar>();
  }

  //...
}

Debido a que nuestras dependencias son solo objetos en sí mismas, estas dependencias tienen dependencias, que tienen aún más dependencias, y así sucesivamente. Así nació la Inversión de Contenedor de Control (o Contenedor DI). Ejemplos: Castle Windsor, Ninject, Structure Map, Spring, etc.)

Pero un contenedor IOC / DI parece exactamente como un localizador de servicios. ¿Llamarlo contenedor DI es un mal nombre? Es un contenedor IOC / DI solo otro tipo del localizador de servicios? ¿El matiz está en el hecho de que usamos DI Containers principalmente cuando tenemos muchas dependencias?

preguntado Oct 12 '09, 22:10

¿La inversión de control significa que "un objeto no debería saber cómo construir sus dependencias"? Ese es nuevo para mí. No, de verdad, eso no es lo que significa "inversión de control". Ver martinfowler.com/bliki/InversionOfControl.html Ese artículo incluso proporciona referencias para la etimología del término, que se remonta a la década de 1980. -

Mark Seemann argumenta que Service Locator es anti-patrón (blog.ploeh.dk/2010/02/03/ServiceLocatorisanAnti-Pattern). Además, encontré el diagrama (que se encuentra aquí, stackoverflow.com/a/9503612/1977871) útil para comprender la situación de DI y SL. Espero que esto ayude. -

16 Respuestas

La diferencia puede parecer leve, pero incluso con ServiceLocator, la clase sigue siendo responsable de crear sus dependencias. Simplemente usa el localizador de servicios para hacerlo. Con DI, la clase recibe sus dependencias. No sabe ni le importa de dónde vienen. Un resultado importante de esto es que el ejemplo de DI es mucho más fácil de probar unitariamente, porque puede aprobar implementaciones simuladas de sus objetos dependientes. Puede combinar los dos e inyectar el localizador de servicios (o una fábrica), si lo desea.

Respondido 20 Feb 19, 16:02

Además, puede usar ambos al construir una clase. El constructor predeterminado puede usar el SL para recuperar las dependencias y pasarlas al constructor "real" que recibe esas dependencias. Obtienes lo mejor de ambos mundos. - otorgar palin

No, ServiceLocator es el responsable de crear una instancia de la implementación correcta para una dependencia determinada (complemento). En el caso de DI, el "contenedor" de DI es el responsable de eso. - Rogério

@Rogerio sí, pero la clase todavía tiene que saber sobre el localizador de servicios ... esas son dos dependencias. Además, la mayoría de las veces he visto a Service Locator delegar al contenedor DI para buscar objetos transitorios que necesitan soporte de servicio. - adam caballero

@Adam No dije que el localizador de servicios delegaría en un contenedor DI. Estos son dos patrones mutuamente excluyentes, como se describe en el artículo "oficial". Para mí, Service Locator tiene una gran ventaja sobre DI en la práctica: el uso de un contenedor DI invita al abuso (que he visto repetidamente), mientras que el uso de un Service Locator no lo hace. - Rogério

"Un resultado importante de esto es que el ejemplo de DI es mucho más fácil de probar unitariamente, porque puede aprobar implementaciones simuladas de sus objetos dependientes". No es verdad. En sus pruebas unitarias, se puede usar una llamada a una función de registro en el contenedor del localizador de servicios para agregar fácilmente simulacros al registro. - Drumbeg

Cuando usa un localizador de servicios, cada clase dependerá de su localizador de servicios. Este no es el caso de la inyección de dependencia. El inyector de dependencias normalmente se llamará solo una vez al inicio para inyectar dependencias en alguna clase principal. Las clases de las que depende esta clase principal tendrán sus dependencias inyectadas de forma recursiva, hasta que tenga un gráfico de objetos completo.

Una buena comparativa: http://martinfowler.com/articles/injection.html

Si su inyector de dependencias parece un localizador de servicios, donde las clases llaman directamente al inyector, probablemente no sea un inyector de dependencias, sino más bien un localizador de servicios.

Respondido 13 Oct 09, 02:10

Pero, ¿cómo maneja el caso en el que tiene que crear objetos durante el tiempo de ejecución? Si los crea manualmente con "nuevo", no podrá utilizar DI. Si llama al marco de DI para pedir ayuda, está rompiendo el patrón. Entonces, ¿qué opciones quedan? - Boris

@Boris Tuve el mismo problema y decidí inyectar fábricas específicas de clase. No es bonito, pero hizo el trabajo. Me encantaría ver una solución más bonita. - Charlie Rudenstål

Enlace directo a la comparación: martinfowler.com/articulos/… - teoman shipahi

@Boris Si tuviera que construir nuevos objetos sobre la marcha, inyectaría una fábrica abstracta para dichos objetos. Lo que sería similar a inyectar un localizador de servicios en esta instancia, pero proporciona una interfaz concreta, uniforme y en tiempo de compilación para construir los objetos relevantes y hace que las dependencias sean explícitas. - VivirPasadoElFin

Los localizadores de servicios ocultan dependencias: no se puede saber al mirar un objeto si llega a una base de datos o no (por ejemplo) cuando obtiene conexiones de un localizador. Con la inyección de dependencia (al menos la inyección del constructor), las dependencias son explícitas.

Además, los localizadores de servicios rompen la encapsulación porque proporcionan un punto global de acceso a las dependencias de otros objetos. Con localizador de servicios, como con cualquier singleton:

se vuelve difícil especificar las condiciones previas y posteriores para la interfaz del objeto cliente, porque el funcionamiento de su implementación se puede entrometer desde el exterior.

Con la inyección de dependencias, una vez que se especifican las dependencias de un objeto, están bajo el control del propio objeto.

Respondido el 17 de diciembre de 09 a las 16:12

Prefiero "Singletons Considered Stupid", steve.yegge.googlepages.com/singleton-considered-stupid - charles graham

Amo al viejo Steve Yegge y el título de ese artículo es genial, pero creo que el artículo que cité y "Los solteros son mentirosos patológicos" de Miško Hevery (misko.hevery.com/2008/08/17/los solteros-son-patológicos-mentirosos) presentan un mejor caso contra la maldad particular del localizador de servicios. - jeff esternal

Esta respuesta es la más correcta porque define mejor un localizador de servicios: "Una clase que oculta sus dependencias". Tenga en cuenta que crear una dependencia internamente, aunque a menudo no es algo bueno, no convierte a una clase en un localizador de servicios. Además, asumir una dependencia de un contenedor es un problema, pero no "el" problema que define más claramente un localizador de servicios. - Diana

With dependency injection (at least constructor injection) the dependencies are explicit.. Por favor explique. - FindOutIslamNow

Como arriba, no puedo ver cómo SL hace que las dependencias sean menos explícitas que DI ... - Michał Powłoka

Martin Fowler afirma:

Con el localizador de servicios, la clase de aplicación lo solicita explícitamente mediante un mensaje al localizador. Con la inyección no hay una solicitud explícita, el servicio aparece en la clase de aplicación, de ahí la inversión de control.

En resumen: el localizador de servicios y la inyección de dependencias son solo implementaciones del principio de inversión de dependencias.

El principio importante es "Dependa de abstracciones, no de concreciones". Esto hará que el diseño de su software esté “acoplado libremente”, “extensible”, “flexible”.

Puedes utilizar el que mejor se adapte a tus necesidades. Para una aplicación grande, que tiene una base de código enorme, es mejor que use un localizador de servicios, porque la inyección de dependencia requeriría más cambios en su base de código.

Puedes consultar esta publicación: Inversión de dependencia: localizador de servicios o inyección de dependencia

También el clásico: Inversión de contenedores de control y el patrón de inyección de dependencia por Martin Fowler

Diseño de clases reutilizables por Ralph E. Johnson y Brian Foote

Sin embargo, el que me abrió los ojos fue: ASP.NET MVC: ¿Resolver o inyectar? Ese es el problema ... por Dino Esposito

Respondido el 27 de diciembre de 18 a las 16:12

Resumen fantástico: "El localizador de servicios y la inyección de dependencias son solo implementaciones del principio de inversión de dependencias". Hans

Y también afirma: La diferencia clave es que con un localizador de servicios cada usuario de un servicio tiene una dependencia del localizador. El localizador puede ocultar dependencias de otras implementaciones, pero es necesario que vea el localizador. Entonces, la decisión entre el localizador y el inyector depende de si esa dependencia es un problema. - programas

ServiceLocator y DI no tienen nada que ver con el "Principio de inversión de dependencia" (DIP). DIP es una forma de hacer que un componente de alto nivel sea más reutilizable, reemplazando una dependencia en tiempo de compilación de un componente de bajo nivel con una dependencia de un tipo abstracto definido junto con el componente de alto nivel, que se implementa mediante el componente de bajo nivel. componente de nivel; de esta forma, la dependencia en tiempo de compilación se invierte, ya que ahora es la de bajo nivel la que depende de la de alto nivel. Además, tenga en cuenta que el artículo de Martin Fowler explica que DI e IoC son no la misma cosa. - Rogério

Una clase que usa el constructor DI indica al código consumidor que hay dependencias que satisfacer. Si la clase usa el SL internamente para recuperar tales dependencias, el código consumidor no es consciente de las dependencias. Esto puede parecer mejor en la superficie, pero en realidad es útil conocer las dependencias explícitas. Es mejor desde el punto de vista arquitectónico. Y al realizar las pruebas, debe saber si una clase necesita ciertas dependencias y configurar el SL para proporcionar versiones falsas apropiadas de esas dependencias. Con DI, simplemente pase las falsificaciones. No es una gran diferencia, pero está ahí.

Sin embargo, DI y SL pueden trabajar juntos. Es útil tener una ubicación central para las dependencias comunes (por ejemplo, configuración, registrador, etc.). Dada una clase que usa tales deps, puede crear un constructor "real" que reciba los deps, y un constructor predeterminado (sin parámetro) que recupere del SL y reenvíe al constructor "real".

EDITAR: y, por supuesto, cuando usa el SL, está introduciendo algún acoplamiento a ese componente. Lo cual es irónico, ya que la idea de tal funcionalidad es fomentar las abstracciones y reducir el acoplamiento. Las preocupaciones se pueden equilibrar y depende de cuántos lugares necesitaría para usar el SL. Si se hace como se sugirió anteriormente, solo en el constructor de clase predeterminado.

Respondido 14 Oct 09, 18:10

¡Interesante! Estoy usando DI y SL, pero no con dos constructores. Tres o cuatro dependencias más aburridas que a menudo se necesitan (configuración, etc.) se obtienen del SL sobre la marcha. Todo lo demás se inyecta con el constructor. Es un poco feo, pero pragmático. - maaartinus

Ambos son técnicas de implementación de IoC. También hay otros patrones que implementan Inversión de control:

  • Patrón de fábrica
  • Localizador de servicios
  • Contenedor DI (IoC)
  • Inyección de dependencia (inyección de constructor, inyección de parámetros (si no se requiere), inyección de setter de inyección de interfaz) ...

El localizador de servicios y el contenedor DI parecen más similares, ambos usan un contenedor para definir dependencias, que asigna la abstracción a la implementación concreta.

La principal diferencia es cómo se ubican las dependencias, en Service Locator, el código del cliente solicita las dependencias, en DI Container usamos un contenedor para crear todos los objetos e inyecta la dependencia como parámetros del constructor (o propiedades).

contestado el 10 de mayo de 20 a las 13:05

En mi último proyecto utilizo ambos. Utilizo la inyección de dependencia para la capacidad de prueba de la unidad. Utilizo el localizador de servicios para ocultar la implementación y ser dependiente de mi contenedor de IoC. ¡y si! Una vez que usa uno de los contenedores de IoC (Unity, Ninject, Windsor Castle), depende de él. Y una vez que esté desactualizado o por alguna razón si desea intercambiarlo, es posible que deba cambiar su implementación, al menos la raíz de composición. Pero el localizador de servicios abstrae esa fase.

¿Cómo no dependería de su contenedor IoC? O tendrá que envolverlo usted mismo (lo cual es una mala idea) o usar Service Locator para configurar su contenedor de IoC. Por lo tanto, le indicará al localizador de servicios que obtenga la interfaz que necesita, y llamará al contenedor de IoC configurado para recuperar esa interfaz.

En mi caso, uso Localizador de servicios que es un componente del marco. Y use La Unidad para contenedor IoC. Si en el futuro necesito cambiar mi contenedor IoC a Ninyectar todo lo que necesito hacer es configurar mi localizador de servicios para usar Ninject en lugar de Unity. Fácil migración.

Aquí hay un gran artículo que explica este escenario; http://www.johandekoning.nl/index.php/2013/03/03/dont-wrap-your-ioc-container/

Respondido 03 ago 15, 20:08

El enlace del artículo de johandekoning está roto. - jakej

Creo que los dos trabajan juntos.

La inyección de dependencia significa que empuja alguna clase / interfaz dependiente a una clase consumidora (generalmente a su constructor). Esto desacopla las dos clases a través de una interfaz y significa que la clase consumidora puede trabajar con muchos tipos de implementaciones de "dependencia inyectada".

La función del localizador de servicios es reunir su implementación. Configura un localizador de servicios a través de algunas correas de arranque al inicio de su programa. Bootstrapping es el proceso de asociar un tipo de implementación a un resumen / interfaz particular. Que se crea para usted en tiempo de ejecución. (basado en su configuración o bootstrap). Si no hubiera implementado la inyección de dependencias, sería muy difícil utilizar un localizador de servicios o un contenedor de IOC.

Respondido 19 Feb 11, 14:02

Una razón para agregar, inspirada en una actualización de la documentación que escribimos para el proyecto MEF la semana pasada (ayudo a construir MEF).

Una vez que una aplicación está formada por miles de componentes, puede resultar difícil determinar si se puede crear una instancia correcta de un componente en particular. Por "instanciado correctamente", quiero decir que en este ejemplo basado en el Foo componente, una instancia de IBar y estará disponible, y que el componente que lo proporciona:

  • tener sus dependencias requeridas,
  • no estar involucrado en ningún ciclo de dependencia no válido, y
  • en el caso de MEF, se suministrará con una única instancia.

En el segundo ejemplo que dio, donde el constructor va al contenedor de IoC para recuperar sus dependencias, la única forma en que puede probar que una instancia de Foo podrá ser instanciado correctamente con la configuración de tiempo de ejecución real de su aplicación es realmente construirlo.

Esto tiene todo tipo de efectos secundarios incómodos en el momento de la prueba, porque el código que funcionará en tiempo de ejecución no necesariamente funcionará bajo un arnés de prueba. Los simulacros no sirven, porque la configuración real es lo que necesitamos probar, no una configuración de tiempo de prueba.

La raíz de este problema es la diferencia ya señalada por @Jon: la inyección de dependencias a través del constructor es declarativa, mientras que la segunda versión usa el patrón imperativo Service Locator.

Un contenedor de IoC, cuando se usa con cuidado, puede analizar estáticamente la configuración del tiempo de ejecución de su aplicación sin crear realmente ninguna instancia de los componentes involucrados. Muchos contenedores populares ofrecen alguna variación de esto; Microsoft.Composición, que es la versión de MEF dirigida a aplicaciones web .NET 4.5 y estilo Metro, proporciona una CompositionAssert muestra en la documentación de la wiki. Usándolo, puede escribir código como:

 // Whatever you use at runtime to configure the container
var container = CreateContainer();

CompositionAssert.CanExportSingle<Foo>(container);

(Véase este ejemplo).

Verificando el Raíces de composición de su aplicación en el momento de la prueba, puede detectar algunos errores que, de lo contrario, podrían pasar por alto las pruebas más adelante en el proceso.

¡Espero que esta sea una adición interesante a este conjunto completo de respuestas sobre el tema!

Respondido el 19 de enero de 21 a las 14:01

Nota: no estoy respondiendo exactamente a la pregunta. Pero creo que esto puede ser útil para los nuevos aprendices del patrón de inyección de dependencia que se confunden al respecto con el Patrón de localizador de servicios (anti) que se topan con esta página.

Conozco la diferencia entre el localizador de servicios (ahora parece ser considerado como un anti-patrón) y los patrones de inyección de dependencia y puedo entender ejemplos concretos de cada patrón, sin embargo, estaba confundido por ejemplos que mostraban un localizador de servicios dentro del constructor (supongamos que ' haciendo inyección de constructor).

"Service Locator" se usa a menudo como el nombre de un patrón y como el nombre para referirse al objeto (suponga también) usado en ese patrón para obtener objetos sin usar el nuevo operador. Ahora, ese mismo tipo de objeto también se puede utilizar en el Raíz de composición para realizar la inyección de dependencia, y ahí es donde entra la confusión.

El punto a tener en cuenta es que puede estar utilizando un objeto localizador de servicios dentro de un constructor DI, pero no está utilizando el "patrón de localizador de servicios". Es menos confuso si uno lo refiere como un objeto contenedor de IoC, ya que puede haber adivinado que esencialmente hacen lo mismo (corríjanme si me equivoco).

Ya sea que se lo denomine un localizador de servicios (o simplemente un localizador), o como un contenedor de IoC (o simplemente un contenedor) no hace ninguna diferencia, como ha adivinado, probablemente se refieren a la misma abstracción (corríjame si me equivoco ). Es solo que llamarlo localizador de servicios sugiere que uno está usando el anti-patrón del localizador de servicios junto con el patrón de inyección de dependencia.

En mi humilde opinión, nombrarlo un 'localizador' en lugar de 'ubicación' o 'localización', también puede hacer que a veces se piense que el localizador de servicios en un artículo se refiere al contenedor del localizador de servicios, y no al patrón del localizador de servicios (anti) , especialmente cuando hay un patrón relacionado llamado Inyección de dependencia y no Inyector de dependencia.

Respondido el 19 de enero de 21 a las 14:01

En este caso tan simplificado no hay diferencia y se pueden utilizar indistintamente. Sin embargo, los problemas del mundo real no son tan simples. Simplemente asuma que la propia clase Bar tenía otra dependencia llamada D. En ese caso, su localizador de servicios no podría resolver esa dependencia y tendría que crear una instancia dentro de la clase D; porque es responsabilidad de sus clases instanciar sus dependencias. Incluso empeoraría si la clase D en sí tuviera otras dependencias y, en situaciones del mundo real, generalmente se vuelve aún más complicado que eso. En tales escenarios, DI es una mejor solución que ServiceLocator.

contestado el 31 de mayo de 16 a las 13:05

Hmm, no estoy de acuerdo: el localizador de servicios ex. muestra claramente que todavía hay una dependencia allí ... el localizador de servicios. Si el bar la clase en sí tiene una dependencia, entonces bar también tendrá un localizador de servicios, ese es el objetivo de usar DI / IoC. - GFoley83

¿Cuál es la diferencia (si existe) entre la inyección de dependencia y el localizador de servicios? Ambos patrones son buenos para implementar el principio de inversión de dependencia. El patrón Service Locator es más fácil de usar en una base de código existente, ya que hace que el diseño general sea más flexible sin forzar cambios en la interfaz pública. Por esta misma razón, el código que se basa en el patrón del localizador de servicios es menos legible que el código equivalente que se basa en la inyección de dependencia.

El patrón de inyección de dependencia deja en claro desde la firma qué dependencias va a tener una clase (o un método). Por esta razón, el código resultante es más limpio y legible.

Respondido el 23 de enero de 17 a las 11:01

Seguir una concepción simple me dio una comprensión más clara de la diferencia entre Service Locator y DI Container:

  • Localizador de servicios se utiliza en el consumidor y tirones servicios por ID de algún almacenamiento por solicitud directa del consumidor

  • Contenedor DI está ubicado en algún lugar exterior y toma servicios de algún almacenamiento y empuja al consumidor (no importa a través del constructor o del método)

Sin embargo, podemos hablar de la diferencia entre estos solo en el contexto del uso concreto del consumidor. Cuando se utilizan Service Locator y DI Container en la raíz de composición, son casi similares.

contestado el 10 de mayo de 20 a las 12:05

El localizador de servicios y la inyección de dependencias son Patrón de acceso a objetos implementación que obedece a la Principio de inversión de dependencia


La inyección de dependencia es un patrón de acceso a objetos [estático / global]

El localizador de servicios es un patrón de acceso a objetos [dinámico]


Si necesita manejar [estructura dinámica] como [ui árbol] o cualquier [aplicación diseñada por fractal], es posible que necesite Service Locator.

Ejemplo:

  • createContext / useContext de React
  • proporcionar / inyectar Vue
  • Proveedores de angular

Si solo desea obtener una instancia de su clase que no me importa la jerarquía de la aplicación y la posición de la instancia en esa jerarquía, debe utilizar DI.

Ejemplo:

  • Anotación en C # / Java

El localizador de servicios se usa cuando no sabe el proveedor real del servicio antes del tiempo de ejecución.

DI se usa cuando sabes que es el contenedor estático que proporciona ese servicio.


El patrón del localizador de servicios se parece más a un nivel de módulo Proveedores de dependencia, mientras que DI es nivel global.

Es muy útil cuando hay un submódulo que declaran la dependencia de un servicio que debería ser proporcionado por su módulo de padres en lugar de un tipo de resolución estática (singleton / transitorio / de ámbito estático).

Puede ser implementado por un patrón de inyección con alcance de DI mientras que el alcance está definido por la estructura / relación del módulo de la aplicación.


Sugerencia personal:

  1. utilice DI siempre que sea posible.
  2. use Service Locator si tiene que lidiar con la resolución del servicio dinámico / en tiempo de ejecución en una estructura fractal.
  3. encapsular el localizador de servicios como una DI con ámbito, por ejemplo: @inject ({scope: 'contextual'})
  4. Busque el servicio por interfaz, en lugar de la clase / constructor.

Información detallada: https://docs.microsoft.com/zh-cn/dotnet/core/extensions/dependency-injection-guidelines#recommendations

Respondido el 07 de enero de 21 a las 03:01

El contenedor DI es un superconjunto de localizador de servicios. Se puede utilizar para localizar un servicio, con capacidad adicional de ensamblar (cablear) las inyecciones de dependencia.

Respondido 21 Abr '20, 21:04

Para el registro

//Foo Needs an IBar
public class Foo
{
  private IBar bar;

  public Foo(IBar bar)
  {
    this.bar = bar;
  }

  //...
}

A menos que realmente necesite una interfaz (la interfaz es utilizada por más de una clase), NO DEBE UTILIZARLA. En este caso, IBar permite utilizar cualquier clase de servicio que lo implemente. Sin embargo, por lo general, esta interfaz será utilizada por una sola clase.

¿Por qué es una mala idea utilizar una interfaz ?. Porque es muy difícil de depurar.

Por ejemplo, digamos que la instancia "barra" falló, pregunta: ¿Qué clase falló ?. ¿Qué código debo corregir? Una vista simple, conduce a una interfaz, y es aquí donde termina mi camino.

En cambio, si el código utiliza una dependencia estricta, es fácil depurar un error.

//Foo Needs an IBar
public class Foo
{
  private BarService bar;

  public Foo(IBar bar)
  {
    this.bar = bar;
  }

  //...
}

Si "bar" falla, entonces debería verificar y activar la clase BarService.

Respondido el 30 de Septiembre de 18 a las 01:09

Una clase es un plan para construir un objeto específico. Por otro lado, una interfaz es un contract y simplemente define un comportamiento, no la acción. En lugar de pasar el objeto real, solo se comparte la interfaz para que el consumidor no acceda al resto de su objeto. Además, para las pruebas unitarias, es útil probar solo la parte que debe probarse. Supongo que con el tiempo comprenderás su utilidad. - Gunhan

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.