leanmind logo leanmind text logo

Blog

Refactorización Avanzada

Dominar el refactoring productivo para maximizar el retorno de la inversión.

Desacoplando distintos artefactos en el código

Por Luis Rodríguez

En nuestro día a día, resolviendo con código los problemas de los clientes, nos encontramos con pequeños detalles que nos llevan a tomar decisiones, que algunas veces no son las más acertadas. Esas decisiones desafortunadas mantenidas en el tiempo, hacen que los proyectos lleguen a ser verdaderos quebraderos de cabeza. Una forma de cuidarlos, es tener especial atención al acoplamiento.

En los estudios reglados, nos han enseñado que en todo proyecto de software hay dos grandes máximas: acoplamiento y cohesión. Según la Wikipedia el acoplamiento es:

En informática, el acoplamiento es la forma y nivel de interdependencia entre módulos de software; una medida de qué tan cercanamente conectados están dos rutinas o módulos de software; así como el grado de fuerza de la relación entre módulos.

Nota: Para los ejemplos de código he usado C#, como librerías de mock: NSubstitute y RichardSzalay.MockHttp (para el objeto httpClient)

Un día cualquiera haciendo pair-programming con otro leanminder, nos encontramos con este trozo de código.

 public class PaymentService
 {
     private readonly HttpClient httpClient;
     private readonly Configuration configuration;

     public PaymentService(HttpClient httpClient, Configuration configuration)
     {
         this.httpClient = httpClient;
         this.configuration = configuration;
     }

     public async Task Pay()
     {
         var url = configuration.GetPaymentUrl();
         var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, url);
         
         var response = await httpClient.SendAsync(httpRequestMessage);

         if(response.Content.Headers.ContentType != new MediaTypeHeaderValue("application/json"))
         {
             throw new Exception("Content type not supported");
         }
     }
 }

En principio, todo parece normal: se envía una petición http a una determinada dirección y si la respuesta no está en formato json, se lanza una simple excepción con un mensaje.

En LeanMind estamos comprometidos con la calidad y el buen funcionamiento del código, por lo tanto en nuestro ADN como leanminders está el hacer buenos tests. Como podemos ver en la siguientes líneas.

  public class PaymentServiceShould
  {
      private const string anUrl = "https://an.example";
      private Configuration configuration;
      private MockHttpMessageHandler httpClient;
      private PaymentService service;

      public PaymentServiceShould()
      {
          configuration = Substitute.For<Configuration>();
          configuration.GetPaymentUrl().Returns(anUrl);
          httpClient = new MockHttpMessageHandler();
          service = new PaymentService(httpClient.ToHttpClient(), configuration);
      }

      [Fact]
      public async Task ThrowExceptionWhenResponseIsNotJson()
      {
          httpClient.When(anUrl).Respond("text/plain", "a simple response");

          await Assert.ThrowsAsync<Exception>(() => service.Pay());
      }

      [Fact]
      public async Task DoNothingWhenResponseIsJson()
      {
          httpClient.When(anUrl).Respond("application/json", "{}");

          var result = await Record.ExceptionAsync(async () => await service.Pay());
          Assert.Null(result);
      }

  }

Volviendo a las líneas de código del principio, podemos ver el acoplamiento entre la respuesta http y el servicio. Esto se debe a que el servicio no tiene que conocer que la respuesta http, tiene un objeto llamado Content, que a su vez tiene un objeto Headers y a su vez tiene una propiedad que es ContentType, porque la clase servicio no está íntimamente relacionada con la clase respuesta http. Nos estamos refiriendo a las siguientes líneas de código dentro de la clase PaymentService y el método Pay

response.Content.Headers.ContentType

Una posible solución que podemos aplicar, es crear una clase de extensión para minimizar el acoplamiento, como se muestra a continuación:

 public static class HttpResponseMessageExtensions
 {
     public static bool IsJsonContent(this HttpResponseMessage response)
     {
         return response.Content.Headers.ContentType.MediaType == MediaTypeNames.Application.Json;
     }
 }

Entonces el servicio, quedaría de la siguiente forma:

 public class PaymentService
 {
     private readonly HttpClient httpClient;
     private readonly Configuration configuration;

     public PaymentService(HttpClient httpClient, Configuration configuration)
     {
         this.httpClient = httpClient;
         this.configuration = configuration;
     }

     public async Task Pay()
     {
         var url = configuration.GetPaymentUrl();
         var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, url);
         
         var response = await httpClient.SendAsync(httpRequestMessage);


         if(!response.IsJsonContent())
         {
             throw new Exception("Content type not supported");
         }
     }
 }

Volvemos a ejecutar los tests, y vemos que todo está en verde.

Conclusión: Es importante detenerse en los detalles de acoplamiento entre los distintos objetos, para poder tener un proyecto sano.

Espero que haya sido este artículo de vuestro agrado Happy coding!!!

Publicado el 12/09/2023 por Luis Rodríguez

¿Quieres más? te invitamos a suscribirte a nuestro boletín para avisarte cada vez que recopilemos contenido de calidad que compartir.

Si disfrutas leyendo nuestro blog, ¿imaginas lo divertido que sería trabajar con nosotros? ¿te gustaría?

Impulsamos el crecimiento profesional de tu equipo de developers