java - pom - El propósito de Guice
guice provides (2)
Yo (creo que) entiendo el propósito de la inyección de dependencia, pero no entiendo por qué necesito algo como Guice para hacerlo (bueno, obviamente no necesito Guice, pero quiero decir por qué sería beneficioso usarlo) ). Digamos que tengo un código existente (no de Guice) que es algo como esto:
public SomeBarFooerImplementation(Foo foo, Bar bar) {
this.foo = foo;
this.bar = bar;
}
public void fooThatBar() {
foo.fooify(bar);
}
Y en algún nivel superior, tal vez en mi main (), tengo:
public static void main(String[] args) {
Foo foo = new SomeFooImplementation();
Bar bar = new SomeBarImplementation();
BarFooer barFooer = new SomeBarFooerImplementation(foo, bar);
barFooer.fooThatBar();
}
Ahora básicamente tengo los beneficios de la inyección de dependencia, ¿verdad? Testabilidad más fácil y así sucesivamente? Por supuesto, si lo desea, puede cambiar fácilmente main () para obtener los nombres de clase de implementación desde la configuración en lugar de la codificación.
Como lo entiendo, para hacer lo mismo en Guice, haría algo como:
public SomeModule extends AbstractModule {
@Override
protected void configure() {
bind(Foo.class).to(SomeFooImplementation.class);
bind(Bar.class).to(SomeBarImplementation.class);
bind(BarFooer.class).to(SomeBarFooerImplementation.class);
}
}
@Inject
public SomeBarFooerImplementation(Foo foo, Bar, bar) {
this.foo = foo;
this.bar = bar;
}
public static void main(String[] args) {
Injector injector = Guice.createInjector(new SomeModule());
Foo foo = injector.getInstance(Foo.class);
barFooer.fooThatBar();
}
¿Es asi? Me parece que solo es azúcar sintáctica, y no es muy útil el azúcar sintáctica. Y si hay alguna ventaja de dividir las cosas "nueva Implementación Xxx ()" en un módulo separado en lugar de hacerlo directamente en main (), eso es fácil de hacer sin Guice.
Así que tengo la sensación de que me falta algo muy básico. ¿Podría por favor explicarme cómo la manera Guice es ventajosa?
Gracias por adelantado.
En la vida real y en aplicaciones más grandes, el nivel de los componentes no es solo 2 (como en su ejemplo). Puede ser 10 o más. También los requisitos y dependencias pueden cambiar a su antojo.
Al hacer "DI a mano", esto significa que debe cambiar todos los caminos a todos los componentes y sus dependencias transitivas a mano. Eso podría ser bastante trabajo.
Con "DI real" (es decir, Guice, Spring, CDI) simplemente cambia uno o dos componentes afectados y el cableado (el Module
en Guice) y eso es todo.
Además: imagine que algunos de sus componentes profundamente anidados están en fábricas a las que deberían llamar otros componentes. En Guice acaba de inyectar un Provider<Foo>
y eso es todo. Al hacerlo a mano, debe arrastrar las dependencias de las fábricas y sus instancias a lo largo de su código.
Eso es aún más frustrante.
Editar Para aclarar el último punto: Imagen, tiene una lógica profundamente anidada (10 o más niveles). En la parte inferior, su lógica creará una fábrica basada en algún valor, por ejemplo, la temperatura real: si está "caliente", entonces se busca un Cooler
, si "frío", entonces un Heater
. Ambos tienen la interfaz TemperatureAdjustor
. Tanto Cooler
como Heater
necesitan muchas dependencias diferentes para realizar su trabajo.
Ya que el tipo de instancia del factor no puede crear uno en main
y transferirlo a donde sea necesario. Así que terminas entregando las dependencias de ambas fábricas al lugar donde se necesitan. ("muchas dependencias" más "muchas dependencias" es "demasiadas para mantenerse sanas")
Con "DI real" como Guice, puede usar AssistedInject
para evitar el desorden de dependencias y dar solo el valor comercial real (aquí: temperatura) a la fábrica y listo. El cableado de la fábrica se realiza en el módulo.
Handcoded DI es real DI.
Cuando considera la complejidad de un contenedor de IOC, entonces tiene que mirar más profundamente para justificar por qué lo usaría. Fui anti-IOC durante mucho tiempo, pudiendo diseñar e implementar DI reales, limpias, rápidas y ordenadas a mano. Pero me entregué a la idea de contenedores porque ayudan a estandarizar la implementación y proporcionan un mecanismo más basado en configuración para agregar nuevas decisiones a la ecuación de dependencia. Y alguien más ya los escribió para que no tengas que dedicar tiempo a la codificación manual.
Muchos dicen usar contenedores en aplicaciones grandes y códigos de mano en pequeños. Yo digo lo contrario. Las aplicaciones pequeñas no están tan limitadas en el rendimiento. Vaya a la velocidad de implementación de usar un contenedor empaquetado. En aplicaciones grandes en las que pasará mucho tiempo optimizando, considere la codificación manual de su DI.
edit: pero permítame agregar eso, IOC Container o codificado a mano, siempre use DI y el código en una interfaz, no importa lo simples que sean las aplicaciones en las que está trabajando. Escribe gran código incluso en pequeñas aplicaciones.