No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Aplicación del Principio de Inversión de Dependencias en C#

14/16
Recursos

¿Cómo funciona el principio de inversión de dependencias en la configuración del código?

El principio de inversión de dependencias es fundamental para crear un código más flexible y mantenible. En este contexto, se analiza una API que utiliza un StudentController en el que se evidencian dependencias directas con StudentRepository y Logbook. Estas dependencias directas violan el principio de inversión de dependencias porque implican una fuerte vinculación entre las clases, lo que dificulta la modificación o expansión del código.

¿Qué representa la API y cómo está estructurada?

La API examinada tiene una estructura base con métodos GET y ADD. Para ejecutar estos métodos, utiliza StudentRepository y Logbook. Aquí se observa que, al buscar o añadir estudiantes, se registra un evento en Logbook, lo que implica que el StudentController depende fuertemente de estas clases.

¿Cuál es el problema con las pruebas unitarias?

Las pruebas unitarias actuales verifican métodos como get sin evitar las interacciones con el sistema, como la escritura de datos en un archivo mediante Logbook. Esto puede resultar problemático ya que las pruebas unitarias no deberían realizar operaciones de entrada/salida. Idealmente, estas pruebas deberían enfocarse solo en la lógica, sin efectos colaterales o dependencias externas.

¿Cómo se establece una solución utilizando la abstracción de interfaces?

Para solventar el problema de la dependencia directa, se introducen interfaces para cada clase dependiente. Crear una interfaz abstracta para StudentRepository y Logbook desacopla el controlador de sus implementaciones concretas. Así, el StudentController no depende directamente de un tipo específico de StudentRepository o Logbook, sino de un contrato común definido por las interfaces.

Proceso para crear una interfaz

  1. Crear la interfaz: Defina la interfaz IStudentRepository con los métodos requeridos:

    public interface IStudentRepository {
        IEnumerable<Student> GetAll();
        void Add(Student student);
    }
    
  2. Implementar la interfaz: Aplique esta interfaz a la clase concreta:

    public class StudentRepository : IStudentRepository {
        public IEnumerable<Student> GetAll() {
            // Implementación...
        }
        public void Add(Student student) {
            // Implementación...
        }
    }
    

La misma lógica se aplica a Logbook, creando una interfaz ILogbook para encapsular su comportamiento.

¿Cómo se inyectan las dependencias?

La inyección de dependencias se realiza mediante constructor en el StudentController. Donde antes se generaban objetos concretos con new, ahora se espera recibir las interfaces en el constructor. Esto logra que el StudentController actúe independiente de las implementaciones específicas.

public class StudentController {
    private readonly IStudentRepository _studentRepository;
    private readonly ILogbook _logbook;

    public StudentController(IStudentRepository studentRepo, ILogbook log) {
        _studentRepository = studentRepo;
        _logbook = log;
    }
    // Métodos del controlador...
}

Configuración en .NET

Para optimizar la arquitectura y facilitar la configuración global de las dependencias se utiliza el contenedor de dependencias de .NET, donde se registran las interfaces y sus implementaciones concretas.

services.AddScoped<IStudentRepository, StudentRepository>();
services.AddScoped<ILogbook, Logbook>();

Esta configuración asegura que los servicios estén disponibles globalmente para cualquier clase que lo requiera.

¿Cómo se evalúa el impacto en las pruebas unitarias?

Gracias a la abstracción proporcionada por las interfaces, es posible introducir stubs o mocks para las pruebas unitarias, eliminando cualquier efecto colateral no deseado, como la creación de archivos o acceso a bases de datos.

Esto incrementa la robustez de las pruebas, permitiendo que se ejecuten en cualquier ambiente sin dependencias de sistemas externos o interacciones complejas. La inyección de dependencias hace que el StudentController pueda ser probado con cualquier implementación que siga los contratos de las interfaces.


Implementar el principio de inversión de dependencias mediante interfaces y patrones de inyección no solo potencializa la flexibilidad del código sino también su capacidad de ser probado de manera uniforme y controlada. La meta es lograr un código que se adapta al cambio fácilmente, aislando y reutilizando componentes a través de contratos bien definidos.

Aportes 13

Preguntas 6

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

Esta deberia ser la parte uno y la otra la parte dos

Para las versiones de .NET Core y .NET para agregar los servicios, se usa el incluido nativamente por Microsoft, pero tambien hay otros nugets como Autofact: https://autofac.org/

Este video es: Aplicando el principio de inversión de la dependencia - Parte I

La mejor explicación que he visto de inyección de dependencias.

usualmente me ha tocado trabajar con inyección de dependencias en algunos proyectos y en la mayoria cuando vamos declaramos los tipos interfaces las declaramos como privadas y el nombre en este caso logbook por ejemplo lo manejamos comos _logbook.

Quedando en el constructor como
_logbook = log … en un caso como el que yo he visto quedaría _logbook = logbook.

Esto es solo un comment

la parte uno esta mal, esta en el lugar 15, debe estar en la 14

Ya está en el orden correcto, gracias !!!

El orden de las partes está mal, esta es el 14 y la anterior la 15

Hola Karol, aún está en el orden incorrecto. En el orden numérico del curso:
El 14 es la segunda parte.
El 15 es la primera parte.

sigue el problema

para los que aún tienen dudas o no queda claro el tema de inversión:public interface ICerveza { void Producir(); } El ejemplo de la cerveza: la interfaz (contrato de implementación) indica lo que cada clase que quiera obtener una cerveza debe tener (propiedades y/o metodos): ```c# public interface ICerveza { void Producir(); } ```luego, quieres implementar una cerveza, ejemplo Pilsen. se defina la clase que implementa la interfaz : ```js public class CervezaPilsen : ICerveza { public void Producir() { Console.WriteLine("Produciendo cerveza Pilsen."); } } ```luego, la cerveceria (otra clase,controlador,etc) quiere llevar a producción la cerveza, pero no requiere realizar nuevamente todo el proceso previo (creación de cerveza individual (ya que si se creara otro tipo de cerveza con otras propiedas, se deberia modificar toda la implementación)). por lo cual, mediante el uso de inyección de dependencia, se generaliza el proceso de producción, inyectando (introduciendo desde afuera (valga el pleonasmo)) el proceso de producción especifico para cada cerveza: ```js public class Cervecera { private readonly ICerveza cerveza; public Cervecera(ICerveza cerveza) { this.cerveza = cerveza; } public void IniciarProduccion() { Console.WriteLine("Iniciando producción..."); cerveza.Producir(); } } ``` esto, elimina la responsabilidad del controlador o clase especifica de realizar tareas que no le compente ( crear la cerveza pilsen ) para luego llevarla a producción. Espero sirva, o me rectifiquen en caso contrario. Gracias.
En la inyección de dependencia de .NET hay tres duraciones principales: **Singleton**: su duración es desde que se ejecuta la aplicación hasta que se tenga, mantiene el estado durante todo el ciclo de vida de la aplicación: **Scope**: Se crea una instancia para cada solicitud HTTP, pero usa la misma instancia en las otras llamadas dentro de la misma solicitud web. **Transient**: Los servicios transitorios se crean cada vez que se haga una instancia, en una misma solicitud http puede cambiar cada vez que se haga una instancia.
Me sale este error al querer reproducir esta clase: ![](https://static.platzi.com/media/user_upload/image-74a41db0-04f5-461d-a762-ee7fc765e7e6.jpg)
Holaa, tengo este error: ```js Determinando los proyectos que se van a restaurar... Todos los proyectos están actualizados para la restauración. D:\ANDRES\Documents\GitHub\curso-principios-solid-csharp\5-DependencyInversion\Api\Program.cs(3,5): error CS1002: Se esperaba ; [D:\ANDRES\Docume nts\GitHub\curso-principios-solid-csharp\5-DependencyInversion\Api\DependencyInversion.csproj] ERROR al compilar. D:\ANDRES\Documents\GitHub\curso-principios-solid-csharp\5-DependencyInversion\Api\Program.cs(3,5): error CS1002: Se esperaba ; [D:\ANDRES\Docume nts\GitHub\curso-principios-solid-csharp\5-DependencyInversion\Api\DependencyInversion.csproj] 0 Advertencia(s) 1 Errores ``` Determinando los proyectos que se van a restaurar... Todos los proyectos están actualizados para la restauración. D:\ANDRES\Documents\GitHub\curso-principios-solid-csharp\5-DependencyInversion\Api\Program.cs(3,5): error CS1002: Se esperaba ; \[D:\ANDRES\Docume nts\GitHub\curso-principios-solid-csharp\5-DependencyInversion\Api\DependencyInversion.csproj] ERROR al compilar. D:\ANDRES\Documents\GitHub\curso-principios-solid-csharp\5-DependencyInversion\Api\Program.cs(3,5): error CS1002: Se esperaba ; \[D:\ANDRES\Docume nts\GitHub\curso-principios-solid-csharp\5-DependencyInversion\Api\DependencyInversion.csproj] 0 Advertencia(s) 1 Errores