Dependency Injection

“As a result I think we need a more specific name for this pattern. Inversion of Control is too generic a term, and thus people find it confusing. As a result with a lot of discussion with various IoC advocates we settled on the name Dependency Injection.”

 

A: ¿Detrás de cuantas cosas de programación esta Fowler? y en general los que hicieron el Agile Manifesto?

D: No sé, pero es el padre de muchas cosas.

A: Menuda cabeza (por fin estoy leyendo su articulo), pero habla de tres tipos de DI, dos usan librerías externas así que no le veo mucho interés. A ver la tercera.

D: Yo quiero hacer algo parecido a DI. Tener algo que instancie al comienzo de la ejecución, y decirle get(NombreDeLaInterface::class) y que me la dé. De esa manera no meto todas esas instanciaciones en 1000 sitios. Al fin y al cabo es como tener un factory que me dé las dependencias. No será una librería de inyección de dependencias, pero así tengo todo ese tipo de instanciaciones en un solo sitio. Y como configuré los environments en CI (production, development y testing), pues que dependiendo del environment que se esté ejecutando, poder cambiar.

A: Parece lo que Fowler llama Injector . Define una interfaz para el.

D: Me ayudó mucho a entenderlo usando la librería que se usa en Android para ello “Dagger”, vas creando “Componentes”, que tienen métodos con la anotación @Provides y lo que retorne, luego en tiempo de compilación se resuelven todas las dependencias. En las clases de cliente, añades un @Inject Repository userRepository y ya el inyector hace su trabajo. Si he notado que al principio con pocas cositas, es fácil de llevar, pero cuando se hace mas grande, ya tienes que ir dividiendo esos componentes, para quizás encapsular los conceptos del dominio. UserComponent, ProductComponent etc…

A: me recuerda a ng2 eso que dices, diría que es la misma filosofía.

D: Dagger 1, fue desarrollado por Sqare, y Dagger2 le hizo fork Google, quizás hayan usado un poco esa filosofía en los laboratorios oscuros de google…

A: pues para no variar no entiendo el ejemplo de Fowler…

D: jajaja

A: supongo que es Java verbosity… “Then, as usual, I need some configuration code to wire up the implementations. For simplicity’s sake I’ll do it in code.” No se lo que es el container.

D: mas abajo lo usa en registerComponents() pero es eso, un contenedor. Parece que internamente, en el container registra un nombre y una clase.

A: si, pero no me dice nada eso. Es decir, que lo veo mágico, no se como funciona ni veo el ejemplo.

D: bueno imagino que ahí está esa magia. Es un simple ejemplo.

A: pues no me vale ! xD

D: Si se pone a sacar el container, saldría una librería ahí xD “Martindency Fowler Injection”

A: luego hay gente para la que DI es simplemente pasar en el constructor las dependencias

D: yo diferenciaría entre DI (Dependency Inversion) y DI (Dependency Injection) donde Dependency Inversion es el patrón, y Dependency Injection la herramienta para llevar a cabo las inyecciones.

A: si, pero debe haber confusión entre la gente, porque veo por ejemplo un articulo aquí donde se dice que que dependency injection es pasar por parámetro… asi que no se, un lio.

D: al menos yo personalmente intento separar ambos. Creo que es igual que MVC, para algunos es una arquitectura para todo y para otros es solo una arquitectura de la capa de presentación. Creo que el software es un cúmulo de ideas con doble sentido.

A: veo que en general la gente usa el termino “dependency injection” refiriéndose a “constructor injection” o “setter injection” cosa que no tiene nada de especial. Yo cuando pienso en dependency injection pienso en “interface injection”, cosa que no entiendo.

D: yo el setter injection no lo he usado nunca

UserRepository repo = new SQLUserRepository();
repo.setMapper(new UserMapper());

vs

UserRepository repo = new SQLUserRepository(new UserMapper));

se consigue lo mismo, pero y con interface ? a que te refieres con interfaces? uhum al InterfaceInjection como explica el?

A: si

A: osea lo que hace es crear una interfaz que define por ejemplo un Finder, luego las implementaciones concretas, ok pero porque la clase que usa la implementacion debe implementar tb la interfaz ? no se, ya te digo que no lo entiendo. He encontrado esto: What is dependency injection? a ver si me ayuda.

D: creo que es un ejemplo de como hacer un inyector mas bien no?, al que tu le pasas un algo que necesita inyección (que implemente el InjectFinder)

A: “Dependency Injection is where components are given their dependencies through their constructors, methods, or directly into fields.”
vale, entonces es solo eso…

D: el Interface Injection que expone creo que es solo una forma de hacerlo

A: vale. No se, pensaba que era algo mas sofisticado, yo hago DI desde el principio! antes de saber como se llamaba.

D: seguramente el container, le pides una clase, que en el ejemplo que pone es MovieLister que tiene para inyectar un MovieFinder y un FinderFileName. El container imagino que instancia el MovieLister, y le hará el “injectFinder(loquesea) y injectFileName(loquesea) de manera que para cuando tu tienes el movielister, ya tiene todo inyectado

A: lo que pasa es lo que te decía, que Fowler siempre complica un poco todo mas de lo que es…. es su forma de pensar, es un tío complejo. Lo que no veo ahora es la ventaja de hacer eso frente a usar el constructor o el setter o directamente la propiedad. (Tener mas de una implementacion ?)

D: Ahí ya supongo que es cuestión de necesidad.

A: Injection Container parece que es eso, un contenedor que inyecta las dependencias allí donde se necesite no?

D: le veo la lógica en el caso de que quieras obtener un MovieLister:

public void testIface() {
    configureContainer();
    MovieLister lister =     
       (MovieLister)container.lookup("MovieLister");
    Movie[] movies = lister.moviesDirectedBy("Sergio Leone");
    assertEquals(
       "Once Upon a Time in the West", 
       movies[0].getTitle()
    );
}

Imagina que hace:

MovieLister lister = (MovieLister)  
container.lookup("IMDBMovieLister");

ese container puede usar la misma instancia del movieLister que tenías, pero le inyecta el MovieFinder de IMDB de modo que ahora el movieLister ha cambiado dependencias, sin tener que crear otra instancia, con inyección por constructor, esto no hubieras podido hacerlo es el caso de uso que se me ha ocurrido para este tipo.

A: es decir la ventaja aquí es usar una misma instancia de movieLister. Inyectando diferentes “finders” en una clase aparte llamada “container” . Con constructor no funciona, pero yo puedo hacer un setter cada vez del finder, sin cambiar tampoco la instancia de MovieLister.

D: no sé si en realidad Fowler lo ha hecho por esto, pero podría resolver este caso de uso.

A: la única utilidad que le veo es en aplicaciones enormes donde merezca la pena tener el container.

D: date cuenta, que cuando usas la herramienta como Dependency Injection, vas a depender de un “Container” siempre. Porque te quitas un boilerplate en código cliente y porque es bastante cómodo, y esto no afectaría a tu core si no lo haces “intrusivo”

A: entonces lo que aplica internamente el container es dependency injection de forma automatizada digamos, es un patrón de diseño ? o es un framework ?

D: ahí está la diferencia que te dije antes. El Container es la herramienta, y la inversión de dependencia, el caso de uso, el patrón.

A: el patrón es ultra simple, el framework no.

D: tu puedes desatornillar un tornillo con un destornillador, o con una taladradora a la que le has puesto el cabezal para desatornillar. Ahora bien, desatornillar el tornillo de la tapa del ordenador, no es lo mismo que desatornillar el tornillo de un camión, pero en general, es Desatornillar, sabes que para un lado aprietas y para otro aflojas.

A: Interesante metáfora. El ejemplo de container que pone aquí me queda mas claro. Es simplemente una clase que asocia una instancia a sus dependencias y la entrega al cliente.

D: crees que debería hacerme poeta? siempre suspendía Lengua Castellana y Literatura, y ahora encuentro pequeños resquicios en mis metáforas. xDDD

A: jajaja pues oye, es un campo a explorar, igual encuentras placer en ello.

D: pero lo que decías, es que en general es eso. Algo a lo que le dices “Dame esto”, y te lo dá. No es muy diferente a un Factory como ves.

A: “Last, but not the least, each time I want to get a mailer, I don’t need a new instance of it. So, the container can be changed to always return the same object:” si te fijas, también habla de eso, es también otra de las cosas que controla el container? Puedes evitar “singletons” que tiran de comprobar si existe una instancia con la variable static. Solo necesitarías (en un ejemplo básico) un singleton, el simple Container. A cambio de depender del container.. cosa que tampoco mola no ? pero supongo que no se puede tener todo.

D: Bueno, supongo que depende del lenguaje y la aplicación

A: Como siempre es un compromiso.

D: el container es eso, si le pides algo y no lo tiene, lo crea, por lo que el impacto de construirlo (si no es complejo o no necesita nada externo), es diminuto

A: bueno esta bien, los posts y esta conversación me han aclarado el tema, menos mal que te tengo aquí.

D: a mi me ha enseñado el inyector de android tras pegarme muchos golpes contra la pared. Tiene unas anotaciones, que se llaman @singleton pero en realidad hace que las dependencias no sean singleton, sino que solo habrá 1 en el graph de objetos. Sin embargo, si no lo tiene, te va a dar siempre una nueva pero esto, ya es algo que la herramienta te ofrece. El container, como te lo montes, da igual, el te va a dar las cosas, a tu código cliente le da igual, como se instancien!

A: respecto a los tests…. si haces tdd, nunca tendrás el container en los tests no ? a no ser que lo estés creando… digo que por ejemplo el test instanciara el mailer y el transport y quiza otro test probara el container.

D: pues a mi parecer ya depende si es un end to end, si vas a hacer uso del container pero en test unitarios, a no ser que sean tests del Container, no lo necesitarías

A: si, eso pienso yo tb

D: es decir, yo el container no lo veo en el core o domain

A: es una utilidad no ? como lo situarías ? capa de infraestructura ?

D: Pues no sabría donde situarlo porque lo veo mas externo a la aplicación en sí.

A: entonces, desde el punto de vista de hexagonal seria capa de infraestructura yo creo… metiendo ddd no se donde se quedaría, me anoto la duda.

D: basándome en el rosco de Clean Architecture, quizás lo pondría en la verde. Y no estaría seguro, es algo que te resuelve las dependencias.

A: es como una metaclase que organiza otras clases.

A: como adaptador ?

D: una librería que instancia cosas xD

A: pero si lo pones en la capa exterior, tu core depende de algo externo (prohibido). Tendrás que aplicar DI al container.

D: estaría fuera del application, basándome en el dibujito

A: vale, yo a eso lo llamo infraestructura. El pb es resolver la dependencia del core sobre una clase externa.

D: a que te refieres?, algún ejemplo?

A: tu core depende del container para obtener las clases que sea con sus “injecciones” y eso no es posible. Como dice U Bob, los módulos solo pueden depender sobre módulos mas internos, no al contrario.

D: yo en mis apps con Clean (que no brillan, las cosas como son), no he necesitado usar el framework en el core nunca. Desde el punto de vista de clean architecture, iría en aquello que invoca los use cases.

A: sea… por ejemplo… en un controlador web

D: tu cuando obtienes el “Interactor” del CA (Clean Architecture), ya tiene sus dependencias resueltas. No deberías usar el inyector mas. Otra cosa es cuando se plantea el problema de tener que elegir entre diferentes implementaciones en runtime.

A: en ese caso pensaba antes, al hacerte la pregunta.

D: es algo que inevitablemente no he logrado averiguar, pero me ha pasado. Pero sabes que pienso?, que si por ejemplo guardar algo en un sitio u otro, es una regla de negocio, eso lo convierte en un caso de uso, no sé si me explico.

A: si, que si encuentras ese problema, tal vez tienes un code smell, un problema de diseño. Y debes sacar esa decisión.

D: si los usuarios con nombre Pepe van a guardarse en un sitio, y los que se llaman Paco en otro, esto es una regla de negocio y vas a tener que tener algún tipo de orquestador que decida donde van las cosas. Lo cual puede ser un caso de uso por ejemplo. Suena feo, porque es como si tu lógica de negocio tuviera que saber sobre la infraestructura

A: si, obligas al core a preocuparse por un detalle, cosa que no es factible, pero si tu core usa un container, entonces no hay ese problema no ? Pero el container debería formar parte del core, o bien invertir la dependencia para que el core no dependa en una capa de la cebolla superior, es decir el core solo se preocupa de llamar un método guardaX() y nada sabe de la implementacion, lo cual esta bien. El container facilita eso. Yo creo que desde el punto de vista de hexagonal tendrías un puerto de salida con su adaptador… puede que el adaptador se ocupara de recuperar la implementacion por ejemplo. Tu core solo dice guarda lo que sea! y va al output port con un objeto command. Y el adaptador puede por ejemplo examinar ese objeto command y obtener la implementacion que lo guarda.

D: hombre, tu adapter, puede tener algún mecanismo de decisión. Puedes tener un OrquestorUserRepository que implementa la interface de UserRepository el cual para crearse necesita un UserRepositoryFactory y en el save(user);

if (user.name === "Paco"){
    userRepositoryFactory->getSQLRepository()->save(user))
} else if (user.name === "Manolo") {
    userRepositoryFactory->getCloudRepository()->save(user));
}

es una forma de hacerlo. Es como cuando tienes que obtener de un repo y cachear, está claro que eso es algo que se puede controlar en esa capa.

A: en clean code, el libro, Uncle Bob dice lo mismo sobre DI…. y habla de containers muy someramente (cita spring para java) y como toooodo el mundo, hace link del articulo de Fowler. Fowler is God.

D: Fowler tio, fowler. Joder es que ese tío es un crack en serio. Creo que es uno de los grandes de la historia del IT.

A: sin duda. Le he dado otra vuelta a su articulo y lo veo mas claro.