O que é o padrão Factory e por que era tão usado

O padrão de projeto Factory Method (ou Abstract Factory) é um dos Design Patterns clássicos consagrados pelo livro do Gang of Four (GoF). Durante décadas, ele foi considerado uma peça fundamental da arquitetura orientada a objetos para desacoplar a criação de objetos da sua utilização concreta.

Em sistemas legados de C# e .NET, a classe Factory servia para encapsular a lógica complexa de instanciação, permitindo que componentes de alto nível solicitassem instâncias de interfaces sem conhecer a classe concreta que implementava aquela regra de negócio.

No entanto, com a evolução da linguagem C# e o amadurecimento dos frameworks modernos de backend, muitas das abstrações que criávamos manualmente no passado se tornaram código redundante e desnecessário (conhecido como boilerplate).

Como funciona a refatoração do Factory no C#

A substituição do padrão Factory em projetos C# modernos assenta em dois pilares: o container de Injeção de Dependência (DI) nativo do ASP.NET Core e os novos recursos de sintaxe do C#.

Em vez de instanciar manualmente objetos através de uma classe estática ou serviço de fábrica, registramos os serviços e suas estratégias diretamente no container de injeção de dependência durante a inicialização da aplicação.

Dessa forma, o runtime do .NET assume a responsabilidade de resolver e injetar as dependências no momento em que as classes necessitam delas, eliminando a necessidade de manter classes Factory intermediárias.

Principais recursos da injeção de dependência moderna

A arquitetura simplificada sem o padrão Factory oferece vantagens diretas na manutenção do projeto:

  • Resolução automática de escopo: Suporte nativo para ciclos de vida Transient, Scoped e Singleton sem código manual.
  • Código mais limpo e legível: Eliminação de dezenas de arquivos de classes de fábrica que serviam apenas como repasse.
  • Substituição por Delegates e Func: Uso de funções de fábrica (Func<T>) registradas diretamente na configuração da aplicação.
  • Testabilidade aprimorada: Mocks e stubs podem ser injetados em testes unitários sem necessidade de mockar fábricas intermediárias.

Esses recursos permitem que desenvolvedores se concentrem na lógica de negócio essencial em vez de escrever infraestrutura de instanciação.

Como começar: removendo o Factory passo a passo

Para simplificar o código da sua aplicação .NET e deletar a classe Factory com segurança, siga esta sequência de refatoração.

Certifique-se de que sua suite de testes automatizados esteja rodando antes de iniciar as alterações no container de dependências.

// PASSO 1: Registro original com classe Factory no Program.cs
// builder.Services.AddSingleton<IPaymentFactory, PaymentFactory>();

// PASSO 2: Registro direto usando Func ou Keyed Services no .NET 8/9
builder.Services.AddKeyedTransient<IPaymentService, CreditCardService>("credit");
builder.Services.AddKeyedTransient<IPaymentService, PixService>("pix");

Com essa mudança no container, os controllers ou serviços que consumiam o Factory passam a receber diretamente a implementação correta usando chaveamento nativo.

// PASSO 3: Uso no construtor da classe compradora
public class OrderProcessor([FromKeyedServices("pix")] IPaymentService paymentService)
{
    public async Task ProcessAsync(Order order)
    {
        await paymentService.ProcessPaymentAsync(order);
    }
}

Exemplo prático

Considere uma aplicação que anteriormente utilizava um InvoiceFactory para criar geradores de notas fiscais com base no tipo de cliente (Pessoa Física ou Pessoa Jurídica).

Após a refatoração, eliminamos a classe de fábrica e passamos o resolvedor diretamente via delegate ou registro no container do ASP.NET Core.

// Antes: InvoiceFactory.Create(CustomerType.Enterprise) -> exigia classe Factory
// Agora: Injeção direta com pattern matching no C# 12/13

public interface IInvoiceService
{
    Task GenerateAsync(Order order);
}

public class InvoiceResolver(IServiceProvider provider)
{
    public IInvoiceService GetService(CustomerType type) => type switch
    {
        CustomerType.Enterprise => provider.GetRequiredService<EnterpriseInvoiceService>(),
        CustomerType.Individual => provider.GetRequiredService<IndividualInvoiceService>(),
        _ => throw new ArgumentOutOfRangeException(nameof(type), type, null)
    };
}

Essa abordagem mantém a separação de responsabilidade, porém reduz a complexidade e facilita a navegação do código pela equipe.

Comparação com alternativas no ecossistema .NET

Comparando as abordagens de instanciação de objetos em projetos C#:

  • Classe Factory tradicional: Alta verbosidade, exige criar interface e classe para cada fábrica. Útil apenas em casos de criação muito complexa com estado.
  • Keyed Services (.NET 8+): Resolução nativa por chave de texto ou enum no container de DI. Leve, rápido e padronizado.
  • Func<TKey, TService> delegate: Injeção de uma função lambda que resolve o serviço dinamicamente sem classes extras.

Para a grande maioria dos projetos Web API e microserviços em .NET, os Keyed Services nativos substituem com ganho claro o padrão Factory manual.

Pontos positivos e limitações da abordagem simplificada

Refatorar e deletar a classe Factory traz diversos benefícios, mas exige atenção a certos cenários.

Pontos positivos: Menos linhas de código para manter, menor tempo de leitura para novos devs no time e aproveitamento máximo do ecossistema nativo do .NET.

⚠️
Atenção

Se a criação de um objeto envolver parsing complexo de arquivos, validação de regras de infraestrutura pesadas ou chamadas de I/O em tempo de criação, um padrão Builder ou Factory ainda pode ser justificável.

O objetivo não é proibir o padrão Factory, mas sim evitar seu uso cego em cenários onde o container de DI resolve o problema nativamente.

Casos de uso reais

Essa refatoração é ideal para:

  • APIs REST em ASP.NET Core: Simplificação de controllers e serviços de aplicação que alternam fornecedores (ex: gateway de pagamento, envio de email, storage S3/Blob).
  • Microsserviços em .NET: Redução da pegada de código e diminuição da complexidade das suites de testes automatizados.
  • Modernização de código legado: Migração de aplicações .NET Framework (4.x) para .NET 8/9 com limpeza de abstrações desnecessárias.

Dicas e boas práticas

💡
Dica

Utilize os novos Keyed Services nativos a partir do .NET 8 para registrar variações de uma mesma interface com chaves descritivas.

🚀
Pro tip

Aproveite os Primary Constructors do C# 12 junto com a injeção de dependência para reduzir ainda mais o código boilerplate dos seus serviços.

🔴
Cuidado

Evite transformar o container de DI em um Service Locator oculto dentro das classes de negócio. Passe apenas as dependências específicas que a classe necessita.

Vale a pena eliminar o Factory?

Na grande maioria das aplicações C# modernas rodando em .NET 8 ou superior, a resposta é sim, vale muito a pena.

Deletar classes Factory redundantes reduz a carga cognitiva da equipe, acelera a onboarding de novos programadores e deixa o projeto alinhado com as melhores práticas atuais do ecossistema .NET.

Avalie os pontos onde o padrão Factory está sendo usado apenas para repassar chamadas e simplifique o design do seu software hoje mesmo!