2009-10-16 19 views
39

He tenido una cierta sensación estos últimos días de que la inyección de dependencia realmente debería llamarse "no puedo decidirme". Sé que esto puede sonar tonto, pero en realidad se trata del razonamiento detrás de por qué debería usar Dependency Injection (DI). A menudo se dice que debería usar DI, para lograr un mayor nivel de acoplamiento libre, y obtengo esa parte. Pero realmente ... ¿con qué frecuencia cambio mi base de datos, una vez que mi elección ha caído en MS SQL o MySQL ... Muy raramente, ¿verdad?Cuándo utilizar Dependency Injection

¿Alguien tiene razones muy convincentes por las que DI es el camino a seguir?

+3

Me gustan todas tus razones hasta ahora ... Entiendo que son los programadores veteranos los que "obtienen" DI. Según entiendo, DI se trata de calidad. Nosotros, como programadores, queremos producir el mayor nivel de software que podamos, y probarlo ayuda. He escrito MUCHO código a lo largo de los años, y me complace decir que 10 años pasados, más del 50% sigue funcionando ... y eso sin DI. Tal vez eso te ayude a entender por qué estoy un poco reservado sobre DI. – CodeMonkey

+0

Obviamente DI es relativamente nuevo. Así que, por supuesto, puede escribir software de alta calidad sin él, pero con el avance de cosas como los frameworks de prueba y TDD, DI puede hacer que sea mucho más fácil escribir pruebas y escribir código que pueda mantenerse para el futuro (acoplamiento). – digiarnie

Respuesta

44

Dos palabras, pruebas de unidad.

Una de las razones más convincentes para DI es permitir una prueba de unidad más fácil sin tener que acceder a una base de datos y preocuparse por la configuración de datos de "prueba".

+2

Así que estás hablando de desacoplamiento de la base de datos, y "fingirlo", usando algunos objetos falsos en lugar de cambiar mi DB durante las pruebas de mi unidad ... ¡mmm ... suena como un argumento razonable en realidad! – CodeMonkey

+0

Pero dado que es una base de datos de prueba, ¿realmente importa que mi base de datos se actualice? – CodeMonkey

+5

Sus pruebas se ejecutarían mucho más rápido sin tener que acceder a una base de datos real. Además, cuando esté probando casos específicos, le resultará mucho más fácil configurar esos casos específicos en un objeto simulado que tener que preocuparse si su base de datos de prueba está en un estado correcto o no. –

1

Aparte del acoplamiento flojo, la prueba de cualquier tipo se logra con mucha mayor facilidad gracias a DI. Puede reemplazar una dependencia existente de una clase bajo prueba con un simulacro, un simulado o incluso otra versión. Si se crea una clase con sus dependencias directamente instanciadas, a menudo puede ser difícil o incluso imposible "anularlas" si es necesario.

+0

¿Podría elaborar en los términos "stub" y "burlarse" ... Supongo que "simulacro" es lo que me refería como "falso" -objetos – CodeMonkey

+2

Por código auxiliar Sólo me refería a reemplazar cualquier dependencia con algo "else" para pruebas. Por simulacro me refiero al uso de una biblioteca como jmock o mockito (si estás usando Java, por supuesto) para crear un proxy donde declaras expectativas sobre ese objeto "simulado". También podría crear una versión ficticia de una clase implementando la interfaz de la dependencia. – digiarnie

+0

Cuando se burla del objeto, ¿lo registra en el registro o simplemente pasa el objeto ficticio? a través del constructor, por ejemplo. – liang

5

Si bien estoy de acuerdo con usted con el ejemplo DB, una de las grandes cosas que encontré útil para usar DI es ayudarme a probar la capa que construyo en la parte superior de la base de datos.

He aquí un ejemplo ...

Usted tiene su base de datos.

Usted tiene su código que accede a la base de datos y devuelve objetos

tiene objetos de dominio de negocio que tienen los objetos del elemento anterior y hacer algo de lógica con ellos.

Si combina el acceso a los datos con la lógica del dominio de su empresa, los objetos de su dominio pueden ser difíciles de probar. DI le permite inyectar sus propios objetos de acceso a datos en su dominio para que no dependa de la base de datos para probar o posiblemente realizar demostraciones (ejecutó una demostración donde se extrajeron algunos datos de xml en lugar de una base de datos).

Resumiendo componentes de terceros y marcos como este también te pueden ayudar.

Aparte del ejemplo de prueba, hay algunos lugares donde DI se puede utilizar a través de un enfoque de diseño por contrato. Puede encontrar apropiado crear un tipo de motor de procesamiento que invoca métodos de los objetos que está inyectando en él. Si bien no puede realmente "procesarlo", ejecuta los métodos que tienen una implementación diferente en cada objeto que usted proporciona.

Vi un ejemplo de esto donde el objeto de dominio de todos los negocios tenía una función "Guardar" que se llamaba después de haber sido inyectado en el procesador. El procesador modificó el componente con información de configuración y Save manejó el estado primario del objeto. En esencia, DI complementó la implementación del método polimórfico de los objetos que se ajustaban a la interfaz.

6

Incluso si no cambia la estructura de su programa durante las fases de desarrollo, descubrirá que necesita acceder a varios subsistemas de diferentes partes de su programa. Con DI, cada una de sus clases solo necesita solicitar servicios y no tiene que proporcionar todo el cableado de forma manual.

Esto realmente me ayuda a concentrarme en la interacción de las cosas en el diseño del software y no en "quién necesita llevar algo porque alguien más lo necesita más tarde".

Además, también ahorra MUCHO trabajo escribiendo código repetitivo. ¿Necesito un singleton? Solo configuro una clase para ser uno. ¿Puedo probar con un "singleton"? Sí, todavía puedo (dado que CONFIGURÉ que existe solo una vez, pero la prueba puede instanciar una implementación alternativa).

Pero, por cierto, antes de usar DI no entendía realmente su valor, pero probarlo fue una verdadera revelación para mí: mis diseños están mucho más orientados a objetos como lo han sido antes. Por cierto, con la aplicación actual NO HAGO una prueba unitaria (mal, mal) pero aún así no pude vivir con DI. Es mucho más fácil mover las cosas y mantener las clases pequeñas y simples.

+1

+1 por mencionar cómo los marcos DI/IoC Container pueden reemplazar el malvado patrón Singleton. – TrueWill

+0

La mayoría de los marcos DI que veo tienen al menos más de 2000 líneas de código, eso parece mucho código. Además, no parece que pueda usar el marco sin modificar el código existente. En su experiencia, ¿es fácil cambiar un marco DI por otro? – Boon

+0

@TrueWill ¿Por qué es Singleton malvado mientras que la versión DI de él, bueno? – zehelvion

4

Dependency Injection le ofrece la posibilidad de probar unidades específicas de código de forma aislada.

Digamos que tengo una clase Foo por ejemplo que toma una instancia de una clase Bar en su constructor. Uno de los métodos en Foo podría verificar que un Valor de propiedad de Bar es uno que permite algún otro procesamiento de Bar.

public class Foo 
{ 
    private Bar _bar; 

    public Foo(Bar bar) 
    { 
     _bar = bar; 
    } 

    public bool IsPropertyOfBarValid() 
    { 
     return _bar.SomeProperty == PropertyEnum.ValidProperty; 
    } 
} 

Ahora vamos a decir que Bar se instancia y sus propiedades se ajustan a los datos de alguna fuente de datos en él de constructor. ¿Cómo podría probar el método IsPropertyOfBarValid() de Foo (ignorando el hecho de que este es un ejemplo increíblemente simple)? Bueno, Foo depende de la instancia de Bar pasada al constructor, que a su vez depende de los datos de la fuente de datos en la que están establecidas sus propiedades. Lo que nos gustaría hacer es aislar Foo de los recursos de los que depende para que podamos probarlo en forma aislada

Aquí es donde entra en juego la Inyección de Dependencia. Lo que queremos es tener una forma de simular una instancia de Bar pasado a Foo para que podamos controlar las propiedades establecidas en este Bar falso y lograr lo que nos propusimos hacer, probar que la implementación de IsPropertyOfBarValid() hace lo que esperamos que haga, es decir, devolver verdadero cuando Bar.SomeProperty == PropertyEnum.ValidProperty y falso para cualquier otro valor

Hay dos tipos de objeto falso, Mocks y Stubs. Los talones proporcionan información para la aplicación bajo prueba para que la prueba se pueda realizar en otra cosa. Por otro lado, los simulacros proporcionan información para la prueba para decidir si se pasa/falla.

Martin Fowler has a great article on the difference between Mocks and Stubs

1

creo que DI es digno de usar cuando se tiene muchos servicios/componentes cuyas implementaciones deben ser seleccionados en tiempo de ejecución dependiendo de la configuración externa. (Tenga en cuenta que dicha configuración puede tomar la forma de un archivo XML o una combinación de anotaciones de código y clases separadas; elija lo que sea más conveniente.)

De lo contrario, simplemente usaría un ServiceLocator, que es mucho más "ligero" y más fácil de entender que un marco DI completo.

Para probar la unidad, prefiero usar una API de simulación que puede simular objetos a demanda, en lugar de requerir que se "inyecte" en la unidad probada de una prueba. Para Java, una de esas bibliotecas es la mía, JMockit.

+0

+1 por mencionar servicelocator. Creo que a veces DI parece excesivo cuando las personas solo usan el aspecto de "ubicación de servicio". –

0

Acabo de entender esta noche. Para mí, inyección de dependencias es un método para instanciar objetos que requieren una gran cantidad de parámetros para trabajar en un contexto específico.

¿Cuándo se debe usar la inyección de dependencia? Puede usar inyección de dependencias si instancia de forma estática un objeto. Por ejemplo, si usa una clase que puede convertir objetos en archivos XML o JSON y si solo necesita el archivo XML. Deberás instanciar el objeto y configurar muchas cosas si no usas la inyección de dependencia.

¿Cuándo no debería usar la inyección de dedandancy? Si un objeto se instancia con parámetros de solicitud (después de un formulario de envío), no se debe utilizar la inyección de depandancy porque el objeto no se instancia de forma estática.

+0

Hice esta pregunta hace muchos años, y he aprendido mucho desde entonces. Lo que está describiendo se resolverá bien utilizando un contenedor IoC e inyectar el objeto creado usando DI. Entonces, por ejemplo, configura su objeto "complicado" en el registro del contenedor IoC y "solicita" una instancia de lo que inyecta en el código donde lo necesita. Estoy de acuerdo en que esa también es una razón válida para usar DI. – CodeMonkey

9

DI es muy útil para desacoplar su sistema. Si todo lo que está usando es para desacoplar la implementación de la base de datos del resto de su aplicación, entonces su aplicación es bastante simple o necesita hacer mucho más análisis en el dominio del problema y descubrir qué componentes dentro de su dominio problemático están el más probable de cambiar y los componentes dentro de su sistema que tienen una gran cantidad de acoplamiento.

DI es muy útil cuando busca la reutilización de código, la versatilidad y la solidez a los cambios en el dominio de su problema.

Lo relevante que es para su proyecto depende de la esperanza de vida esperada de su código. Dependiendo del tipo de trabajo que estés haciendo cero, la reutilización de un proyecto al siguiente para la mayoría del código que estás escribiendo podría ser bastante aceptable.

Un ejemplo para usar el uso de DI es crear una aplicación que pueda implementarse para varios clientes que usan DI para inyectar personalizaciones para el cliente, lo que también podría describirse como el patrón de estrategia de GOF. Muchos de los patrones GOF se pueden facilitar con el uso de un marco DI.

DI es más relevante para el desarrollo de aplicaciones empresariales en el que tiene una gran cantidad de código, requisitos empresariales complicados y una expectativa (o esperanza) de que el sistema se mantendrá durante muchos años o décadas.

+0

También es útil durante el desarrollo rápido si buscas flexibilidad (porque deseas realizar experimentación). Puede conectar fácilmente diferentes vistas, estructuras de datos, menús, esquemas de control y otras cosas. Básicamente (si lo entiendo y lo uso correctamente), le permite administrar "dependencias" de una manera más flexible y organizada con menos ruido de código de placa de caldera. Por supuesto, podría ser mal utilizado y de alguna manera hacer que el código sea potencialmente más complicado. – zehelvion

Cuestiones relacionadas