c# Dependency Injection – ¿Qué estrategias existen para inyectar dependencias?

Si alguna vez te has preguntado ¿Qué es la Inyección de dependencias? y ¿Cómo lo puedo implementar? te recomiendo que leas este post.
Dependency Injection
Dependency Injection

Este término fue introducido por Martin Fowler y sigue el principio Hollywood: “No nos llames, nosotros te llamaremos“. Es un patrón de diseño orientado a objetos en el que se pasan las referencias necesarias a una clase en lugar que esta se encargue de crearlas.

Mark Seeman nos da la siguiente definición en su libro Dependency Injection in .Net:

La inyección de dependencias es un conjunto de principios de diseño y patrones de software que nos permiten desarrollar código desacoplado.

Beneficios de la inyección de dependencias:

1. Late Binding (Enlace tardío)

Permite escoger que componentes se van a usar en tiempo de ejecución en lugar del tiempo de compilación sin modificar el código.

2. Extensibility (Extensibilidad)

Permite extender y rehusar el código para agregar nuevas funcionalidades.

3. Parallel Development (Desarrollo paralelo)

Permite que muchas personas trabajen sobre el mismo proyecto en paralelo.

4. Maintainability (Mantenimiento)

Permite tener clases con responsabilidades claras.

5. Testeability (Testeabilidad)

Permite tener clases que se pueden probar unitariamente.

Nota: En aplicaciones pequeñas aplicar inyección de dependencias puede verse como una sobre ingeniería, en aplicaciones grandes recién se ve el beneficio de usar esta técnica.

Tipos de dependencias

Existen dos tipos de dependencias las volátiles y las estables. Las dependencias volátiles son las que tienden a cambiar o aún no están definidas, todo lo demás son dependencias estables. Se recomienda usar la inyección de dependencias para las dependencias volátiles.

Dependencias estables
  • Tipos BCL.
  • Clases o módulos que ya existen.
  • Cuando crees que un módulo nunca será cambiado o reemplazado por otro.
  • Tipos con algoritmos deterministas.
Dependencias volátiles
  • Temas de infraestructura (Base de datos, servicios web, sistema de archivos, etc).
  • Dependencias que aún no existen, clases que aun falten desarrollar.
  • Dependencias que no están instaladas en todas las máquinas de desarrollo, componentes de terceros.
  • Tipos con algoritmos no deterministas (Por ejemplo: System.Random, System.DateTime, etc)

Estrategias para inyectar código:

Constructor injection (Inyección por constructor)

Esta tipo requiere que se pasen las referencias en el constructor del objeto para que las guarde para un uso futuro. Este es el usado por defecto.

public class Cliente
{
    public void Ejecutar()
    {
        var repositorioUsuario = new RepositorioUsuario();
        var servicioAutenticacion = 
              new ServicioAutenticacion(repositorioUsuario);
        servicioAutenticacion.IniciarSesion(new Credenciales());
    }
}
public class ServicioAutenticacion
{
    private readonly IRepositorioUsuario _repositorioUsuario;

    public ServicioAutenticacion(IRepositorioUsuario repositorioUsuario)
    {
        _repositorioUsuario = repositorioUsuario;
    }
}
Pros
  • Las clases auto documentan lo que necesitan para hacer su trabajo.
  • Se puede usar con o sin un contenedor de inversión de control.
  • Una vez creadas las clases estas se encuentran en un estado valido.
Contras
  • El constructor puede tener muchos parámetros (Code Smell).
  • No trabaja bien con la Serialización se debe definir un constructor sin parámetros.
  • No todos los métodos requieren los objetos que otros métodos requieren (Baja cohesión).
Property injection o setter injection (Inyección por propiedad)

Esta tipo requiere que se cree una propiedad o un método para asignar una dependencia.


public class Cliente
{
    public void Ejecutar()
    {
        var servicioAutenticacion = new ServicioAutenticacion();
        
        servicioAutenticacion.ServicioEncriptacion = 
                   new ServicioEncriptacionMD5();
        servicioAutenticacion.IniciarSesion(new Credenciales());

        servicioAutenticacion.ServicioEncriptacion = 
                   new ServicioEncriptacionSha1();
        servicioAutenticacion.IniciarSesion(new Credenciales());
    }
}

public class ServicioAutenticacion
{

    public IServicioEncriptacion ServicioEncriptacion { get; set; }
    
    public ResultadoInicioSesion IniciarSesion(Credenciales credenciales)
    {
        //Codigo
        var passwordHash = 
            ServicioEncriptacion.ObtenerHash(credenciales.Password);
        //Codigo
    }

}
Pros
  • Muy flexible.
  • La dependencia puede cambiar en cualquier momento.
Contras
  • Menos intuitivos.
  • Los objetos pueden encontrarse en un estado inválido desde la construcción hasta la asignación de la dependencia.
Parameter injection (Inyección por parámetro)

Esta tipo requiere que se agregue un parámetro en la firma de un método por cada dependencia que se necesite.

public class Cliente
{
    public void Ejecutar()
    {
        var servicioAutenticacion = new ServicioAutenticacion();
        servicioAutenticacion.IniciarSesion(new Credenciales(),
                                     new ServicioEncriptacionMD5());
    }
}

public class ServicioAutenticacion
{

    public ResultadoInicioSesion IniciarSesion(Credenciales credenciales, 
                                  IServicioEncriptacion servicioEncriptacion)
    {
        //Codigo
        var passwordHash = 
                   servicioEncriptacion.ObtenerHash(credenciales.Password);
        //Codigo
    }
}
Pros
  • Más granular.
  • Más flexible.
  • No requiere cambiar el resto de la clase.
Contras
  • El método puede tener muchos parámetros (Code Smell).
  • Cambia la firma de un método.

Nota: Además de las estrategias mencionadas anteriormente también se pueden utilizar: Ambient Context y Service Locator.

Si  les interesa aprender más cosas relacionadas a la inyección de dependencias les recomiendo que lean el libro Dependency Injection in .Net de Mark Seeman, no se arrepentirán.

Conclusión

La inyección de dependencias es un patrón de diseño orientado a objetos que nos permite desarrollar código desacoplado. El usar este patrón trae como beneficios: testeabilidad, extensibilidad, mantenimiento, desarrollo en paralelo y el encale tardío de las piezas de software. Pero también se debe tener cuidado al momento de implementar esta decisión de diseño para no caer en una sobre ingeniería. Se recomienda usar cuando existen tipos de dependencias volátiles. Finalmente, recordemos que existen cinco formas de poder implementarlo: por constructor, por propiedad, por parámetro, usando Ambient Context o Service Locator.

Referencias:
Metal Tip:

Este artículo lo escribí escuchando la canción Speed of Light de la banda Stratovarius de Finlandia, les comparto el enlace.

Happy coding and Stay Heavy lml

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s