El patrón Factory Method es uno de los patrones de diseño más conocidos en el mundo de desarrollo de software 🏭. Sin embargo, la mayoría de los ejemplos disponibles se limitan a aplicaciones de consola básicas 🤢, lo que puede resultar desalentador para aquellos que buscan implementarlo en aplicaciones más complejas. 

En este artículo, exploraremos una implementación avanzada del patrón Factory Method en el contexto de Asp NET Core, mostrando cómo puede ser aplicado de manera efectiva en situaciones más desafiantes.🤯

Inspiración

La inspiración para este artículo proviene de un recurso que descubrí hace aproximadamente dos años, titulado "Factory Pattern Without Switch and If-Else". Desafortunadamente, no tengo acceso para volver a leerlo ni compartirlo con otros desarrolladores 😥. Por lo tanto, he decidido escribir mi propia versión de este contenido, con la esperanza de proporcionar una referencia útil y accesible para aquellos interesados en este tema.✨

¿En qué consiste el patrón Factory Method?

Si deseas un resumen rápido sobre este patrón, sus problemas resueltos y cuándo utilizarlo, te recomiendo consultar la explicación detallada y los ejemplos en Refactoring Guru. En este artículo, utilizaremos el ejemplo de una empresa de logística inspirado en el sitio web mencionado.

Implementando Factory Method con C#

Para nuestra aventura, nos sumergiremos en el mundo de una empresa de logística que realiza envíos por tierra y mar. Cuando la empresa necesita enviar un producto, requiere un tipo específico de transporte: ya sea un camión para rutas terrestres o un barco para rutas marítimas. Nuestra implementación del Factory Method se centrará en la creación de estos transportes a medida. 🚚⛴️

La estructura de las clases creadoras

La estructura de la jerarquía de productos

Comenzaremos creando una interfaz llamada ITransport que define el comportamiento de los transportes.

public interface ITransport
{
    void Deliver();
}

Luego, agregaremos dos clases concretas: Truck y Ship, ambas implementando la interfaz ITransport.

public class Truck : ITransport
{
    public void Deliver()
    {
         // Realizar acciones de entrega por carretera
    }
}

public class Ship : ITransport
{
    public void Deliver()
    {
         // Realizar acciones de entrega marítima
    }
}

Ahora, crearemos la clase más importante, TransportFactory, que se encargará de instanciar estas clases cuando sea necesario.

Primero, definiremos un enumerado para identificar los tipos de transporte.

public enum TransportType
{
     Road,
     Sea
}

A continuación, crearemos la interfaz ITransportFactory y la clase TransportFactory junto con su diccionario para registrar los tipos de transporte y las fábricas correspondientes.

public interface ITransportFactory
{
     void RegisterTransport(TransportType transportType, Func<ITransport> factoryMethod);

     ITransport CreateTransport(TransportType transportType);
}
public class TransportFactory : ITransportFactory
{
     private readonly Dictionary<TransportType, Func<ITransport>> transports;

     public TransportFactory()
     {
         transports = new();
     }

     public ITransport CreateTransport(TransportType transportType)
     {
         Func<ITransport> funcion = transports[transportType];

         ITransport transport = funcion();

         return transport;
     }


     public void RegisterTransport(TransportType transportType, Func<ITransport> factoryMethod)
     {
         transports.Add(transportType, factoryMethod);
     }
}

Funcionamiento de la Implementación de la Clase TransportFactory

La clave del funcionamiento de esta implementación es la utilización de un diccionario (Dictionary) llamado transports para almacenar las fábricas de transporte registradas.

El diccionario asocia tipos de transporte (TransportType) con funciones (Func<ITransport>) que se encargan de crear las instancias correspondientes de objetos ITransport.

Usar TransportFactory en Asp NET Core

Sin modificar los archivos anteriores, vamos a implementar la integración de este patrón en Asp NET Core. Lo haremos utilizando la clase TransportFactoryBuilder y sus extensiones. Esta implementación está inspirada en IMVCBuilder. 🤩

Primero, crearemos la clase TransportFactoryBuilder y su interfaz asociada.

public interface ITransportFactoryBuilder
{
     IServiceCollection Services { get; }
}

public class TransportFactoryBuilder : ITransportFactoryBuilder
{
     public TransportFactoryBuilder(IServiceCollection services)
     {
         Services = services;
     }

     public IServiceCollection Services { get; }
}

Luego, agregaremos la clase TransportFactoryBuilderExtensions, que contiene dos métodos de extensión para construir el Factory y registrar transportes.

Comentario aparte: Me costó construir este código para que funcione correctamente, pero valió totalmente la pena 😊

using Microsoft.Extensions.DependencyInjection.Extensions;

public static class TransportFactoryBuilderExtensions
{
     public static ITransportFactoryBuilder AddTransportFactory(this IServiceCollection services)
     {

         TransportFactoryBuilder builder = new(services);

         services.AddSingleton<ITransportFactory, TransportFactory>();

         return builder;
     }

     public static ITransportFactoryBuilder AddTransport<T>(this ITransportFactoryBuilder builder, TransportType transport) where T : class, ITransport
     {
         builder.Services.AddScoped<T>();

         var provider = builder.Services.BuildServiceProvider();

         var factory = provider.GetRequiredService<ITransportFactory>();

         factory.RegisterTransport(transport, () => provider.GetRequiredService<T>());


         builder.Services.Replace(ServiceDescriptor.Singleton(factory));

         return builder;
     }
}

Uso de TransportFactory en una WebApi de Asp NET Core

¡Ahora sí! Ya tenemos todas las clases, todas las interfaces y un enum. Todo lo necesario para que el equipo de desarrollo pueda utilizar esta implementación del patrón Factory Method.

No pueden negar que se ve muy prolijo el código así y respeta el principio de Open/Closed de SOLID 😍

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddTransportFactory()
         .AddTransport<Truck>(TransportType.Road)
         .AddTransport<Ship>(TransportType.Sea);

Para utilizar la clase TransportFactory en nuestra lógica de negocio, simplemente inyectamos la interfaz ITransportFactory donde sea necesario.

A continuación, se muestra un ejemplo de un controlador en Asp NET Core que utiliza esta implementación.

[ApiController]
[Route("[controller]")]
public class TransportsController : ControllerBase
{
     private readonly ITransportFactory _factory;

     public TransportsController(ITransportFactory factory)
     {
         _factory = factory;
     }

     [HttpGet]
     public IActionResult Get(TransportType trasportType)
     {
         ITransport transport = _factory.CreateTransport(trasportType);

         return Ok(transport);
     }
}

 

Conclusión

En este artículo, vimos cómo implementar el patrón Factory Method de manera avanzada en Asp NET Core 😉. Esta implementación te permite construir una fábrica de objetos de manera flexible y escalable.

Si te interesa explorar más a fondo esta implementación, puedes encontrar el repositorio de GitHub con el proyecto completo.

Si encuentras útil este trabajo y deseas mostrar tu apoyo, siempre puedes invitarme a un buen café ☕.

Buy Me A Coffee

Recuerda que la implementación avanzada del Factory Method en Asp NET Core puede ser una herramienta valiosa para la construcción de aplicaciones escalables y mantenibles de aca a 10 años.💪