Ambos patrones parecen una implementación del principio de inversión de control. Es decir, que un objeto no debe saber cómo construir sus dependencias.
La inyección de dependencias (DI) parece usar un constructor o setter para "inyectar" sus dependencias.
Ejemplo de uso de inyección de constructor:
//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 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 incluso más dependencias, y así sucesivamente. Así nació la Inversión de Control Container (o DI Container). Ejemplos: Castillo Windsor, Ninject, Mapa de Estructura, Primavera, etc.)
Pero un contenedor IOC / DI se ve exactamente como un localizador de servicios. ¿Es un mal nombre llamarlo contenedor DI? ¿Es un contenedor IOC / DI simplemente otro tipo de localizador de servicios? ¿Es el matiz el hecho de que usamos contenedores DI principalmente cuando tenemos muchas dependencias?
fuente
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 tiene sus dependencias. No sabe ni le importa de dónde vienen. Un resultado importante de esto es que el ejemplo DI es mucho más fácil de realizar pruebas unitarias, porque puede pasar simulaciones de implementaciones de sus objetos dependientes. Puede combinar los dos e inyectar el localizador de servicios (o una fábrica), si lo desea.
fuente
Cuando utiliza un localizador de servicios, cada clase dependerá de su localizador de servicios. Este no es el caso con la inyección de dependencia. El inyector de dependencia generalmente se llamará solo una vez en el inicio para inyectar dependencias en alguna clase principal. Las clases de las que depende esta clase principal se inyectarán recursivamente sus dependencias, hasta que tenga un gráfico de objeto completo.
Una buena comparación: http://martinfowler.com/articles/injection.html
Si su inyector de dependencia se parece a un localizador de servicios, donde las clases llaman directamente al inyector, probablemente no sea un inyector de dependencias, sino un localizador de servicios.
fuente
Los localizadores de servicios ocultan dependencias: al observar un objeto no se puede saber si golpea o no una base de datos (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 de acceso global a las dependencias de otros objetos. Con el localizador de servicios, como con cualquier singleton :
Con la inyección de dependencias, una vez que se especifican las dependencias de un objeto, están bajo el control del objeto mismo.
fuente
With dependency injection (at least constructor injection) the dependencies are explicit.
. Por favor explique.Martin Fowler afirma :
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 "Depende de las abstracciones, no de las concreciones". Esto hará que su diseño de software esté "débilmente acoplado", "extensible", "flexible".
Puede usar el que mejor se adapte a sus necesidades. Para una aplicación grande, que tiene una gran base de código, es mejor que use un Localizador de servicios, porque la Inyección de dependencias requeriría más cambios en su base de código.
Puede consultar esta publicación: Inversión de dependencia: Localizador de servicio 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
fuente
Una clase que usa el constructor DI indica al código consumidor que hay dependencias que deben satisfacerse. Si la clase usa el SL internamente para recuperar tales dependencias, el código consumidor no tiene conocimiento de las dependencias. Esto puede parecer mejor en la superficie, pero en realidad es útil saber de cualquier dependencia explícita. Es mejor desde una vista arquitectónica. Y al hacer pruebas, debe saber si una clase necesita ciertas dependencias y configurar el SL para proporcionar versiones falsas apropiadas de esas dependencias. Con DI, solo pasa las falsificaciones. No es una gran diferencia, pero está ahí.
Sin embargo, DI y SL pueden trabajar juntas. Es útil tener una ubicación central para dependencias comunes (por ejemplo, configuraciones, registrador, etc.). Dada una clase que usa tales deps, puede crear un constructor "real" que recibe los deps, y un constructor por defecto (sin parámetro) que recupera del SL y lo reenvía 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 dicha funcionalidad es fomentar las abstracciones y reducir el acoplamiento. Las preocupaciones pueden ser equilibradas, y depende de cuántos lugares necesite usar el SL. Si se hace como se sugirió anteriormente, solo en el constructor de clase predeterminado.
fuente
Ambos son técnicas de implementación de IoC. También hay otros patrones que implementan Inversion of Control:
El localizador de servicios y el contenedor DI parecen más similares, ambos usan un contenedor para definir dependencias, que mapea 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 dependencia como parámetros (o propiedades) del constructor.
fuente
En mi último proyecto utilizo ambos. Yo uso la inyección de dependencia para la capacidad de prueba de la unidad. Uso el localizador de servicios para ocultar la implementación y ser dependiente de mi contenedor de IoC. ¡y si! Una vez que utiliza 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, tendrá que cambiar su implementación, al menos la raíz de la composición. Pero el localizador de servicios abstrae esa fase.
¿Cómo no dependería de su contenedor de IoC? O deberá envolverlo usted mismo (lo cual es una mala idea) o utilizar el Localizador de servicios para configurar su contenedor IoC. Entonces le dirá al localizador de servicios que obtenga la interfaz que necesita, y llamará al contenedor IoC configurado para recuperar esa interfaz.
En mi caso, uso ServiceLocator, que es un componente de marco. Y use Unity para el contenedor IoC. Si en el futuro necesito cambiar mi contenedor de IoC a Ninject 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/
fuente
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".
El rol del localizador de servicios es reunir su implementación. Configura un localizador de servicios mediante algunas correas de arranque al inicio de su programa. Bootstrapping es el proceso de asociar un tipo de implementación a un resumen / interfaz en particular. Que se crea para ti en tiempo de ejecución. (basado en su configuración o bootstrap). Si no hubiera implementado la inyección de dependencia, sería muy difícil utilizar un localizador de servicios o un contenedor IOC.
fuente
Una razón para agregar, inspirada en una actualización de documentación que escribimos para el proyecto MEF la semana pasada (ayudo a construir MEF).
Una vez que una aplicación se compone de potencialmente miles de componentes, puede ser difícil determinar si algún componente en particular se puede instanciar correctamente. Por "instanciado correctamente", quiero decir que en este ejemplo basado en el
Foo
componente, una instancia deIBar
y estará disponible, y que el componente que lo proporciona: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
se podrá instanciar correctamente con la configuración de tiempo de ejecución real de su aplicación es construir realmente eso .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 funcionarán, 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 del Localizador de servicios.
Un contenedor de IoC, cuando se usa con cuidado, puede analizar estáticamente la configuración de tiempo de ejecución de su aplicación sin crear realmente ninguna instancia de los componentes involucrados. Muchos contenedores populares proporcionan alguna variación de esto; Microsoft.Composition , que es la versión de MEF dirigida a aplicaciones web y de estilo Metro .NET 4.5, proporciona una
CompositionAssert
muestra en la documentación wiki. Al usarlo, puede escribir código como:(Ver este ejemplo ).
Al verificar las 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 de respuestas sobre el tema!
fuente
Nota: no estoy respondiendo exactamente la pregunta. Pero creo que esto puede ser útil para los nuevos aprendices del patrón de Inyección de dependencia que están confundidos con el patrón Localizador de servicios (anti) que tropiezan con esta página.
Sé la diferencia entre el Localizador de servicios (parece que ahora se considera como un antipatrón) y los patrones de Inyección de dependencias y puedo entender ejemplos concretos de cada patrón, pero me confundieron los ejemplos que muestran un localizador de servicios dentro del constructor (supongamos que ' re haciendo inyección de constructor).
El "Localizador de servicios" a menudo se usa como el nombre de un patrón y como el nombre para referirse al objeto (supongamos 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 usar en la raíz de la composición para realizar una inyección de dependencia, y ahí es donde entra la confusión.
Lo importante es que puede estar utilizando un objeto localizador de servicios dentro de un constructor DI, pero no está utilizando el "patrón 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íjame si me equivoco).
Si se conoce como un localizador de servicios (o solo un localizador), o como un contenedor de IoC (o solo contenedor) no hace ninguna diferencia, ya que supones que probablemente se refieren a la misma abstracción (corrígeme si me equivoco ) Es solo que llamarlo un localizador de servicios sugiere que uno está usando el antipatrón Localizador de servicios junto con el patrón de Inyección de dependencias.
En mi humilde opinión, nombrarlo como 'localizador' en lugar de 'ubicación' o 'ubicación', también puede hacer que uno piense a veces que el localizador de servicios en un artículo se refiere al contenedor del Localizador de servicios, y no al patrón Localizador de servicios (anti) , especialmente cuando hay un patrón relacionado llamado Inyección de dependencia y no Inyector de dependencia.
fuente
En este caso excesivamente simplificado, no hay diferencia y se pueden usar indistintamente. Sin embargo, los problemas del mundo real no son tan simples. Simplemente suponga que la clase Bar en sí 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.
fuente
bar
clase en sí misma tiene una dependencia,bar
también tendrá un localizador de servicios, ese es el punto de usar DI / IoC.¿Cuál es la diferencia (si la hay) entre Inyección de dependencias y Localizador de servicios? Ambos patrones son buenos para implementar el principio de Inversión de dependencia. El patrón del Localizador de servicios 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 dependencias.
El patrón de inyección de dependencias deja en claro desde la firma qué dependencias tendrá una clase (o un método). Por esta razón, el código resultante es más limpio y más legible.
fuente
Seguir una concepción simple me dio una comprensión más clara de la diferencia entre el Localizador de servicios y el Contenedor DI:
Service Locator se usa en el consumidor y extrae los servicios por ID de algún almacenamiento por solicitud directa del consumidor
DI Container está ubicado en algún lugar afuera y toma servicios de algún almacenamiento y los envía al consumidor (no importa a través del constructor o el método)
Sin embargo, podemos hablar sobre la diferencia entre estos solo en el contexto del uso concreto del consumidor. Cuando Service Locator y DI Container se utilizan en la raíz de la composición, son casi similares.
fuente
El contenedor DI es un superconjunto de localizador de servicios. Se puede usar para localizar un servicio , con capacidad adicional de ensamblar (cablear) las inyecciones de dependencia .
fuente
Para el registro
A menos que realmente necesite una interfaz (la interfaz es utilizada por más de una clase), NO DEBE USARLA . En este caso, IBar permite utilizar cualquier clase de servicio que la implemente. Sin embargo, por lo general, esta interfaz será utilizada por una sola clase.
¿Por qué es una mala idea usar una interfaz? Porque es realmente difícil de depurar.
Por ejemplo, supongamos que la instancia "bar" falló, pregunta: ¿ qué clase falló? ¿Qué código debo arreglar? Una vista simple, conduce a una interfaz, y es aquí donde termina mi camino.
En cambio, si el código usa una dependencia rígida, entonces es fácil depurar un error.
Si "bar" falla, entonces debería verificar y activar la clase BarService.
fuente
contract
y simplemente define un comportamiento, no la acción. En lugar de pasar alrededor del objeto real, solo se comparte la interfaz para que el consumidor no acceda al resto de su objeto. También para las pruebas unitarias, ayuda probar solo la parte que necesita ser probada. Supongo que a tiempo entenderías su utilidad.