Tecnologia além do código.
Régys Borges da Silveira
← Voltar para explorar os artigos

Design Patterns em Delphi 13: Factory Method e Abstract Factory

Neste artigo 9 tópicos

Se você já se viu escrevendo um código onde, para cada novo modelo de impressora ou novo gateway de pagamento, você precisa abrir a Unit principal e adicionar mais um case de criação, você está sofrendo com o Alto Acoplamento.

Neste terceiro artigo da nossa série, vamos aprender como os padrões de fábrica (Factories) no Delphi 13 podem isolar a lógica de criação de objetos, permitindo que seu software cresça sem que as camadas de negócio sequer saibam quais classes concretas estão sendo utilizadas.

1. O Problema da Instanciação Direta

O Modo Antigo (The Tight Coupling Way)

No desenvolvimento legado, o programador costuma instanciar classes concretas diretamente na regra de negócio: Em sistemas legados, a criação de objetos é espalhada por toda a aplicação. Sempre que uma nova funcionalidade surge, o desenvolvedor é forçado a caçar blocos if-then-else ou case gigantescos.

// MODO LEGADO - EVITE
if TipoPagamento = 'Boleto' then
  Pagamento := TPagamentoBoleto.Create
else if TipoPagamento = 'Cartao' then
  Pagamento := TPagamentoCartao.Create;

Por que isso é um erro grave?

  1. Violação do OCP (Open/Closed Principle): Para cada novo método de pagamento, você altera o código existente.
  2. Dificuldade de Teste: Você não consegue testar a lógica de “venda” sem carregar todas as dependências das classes de “pagamento”.
  3. Dificuldade de Mocking: Em testes unitários, você fica refém da instância real, impossibilitando testes isolados de lógica de negócio.
  4. Explosão de Uses: Sua Unit de negócio precisa conhecer todas as Units de pagamento.
  5. Rigidez: Adicionar um novo tipo exige recompilar o núcleo do sistema.
  6. Poluição de Interface: Sua camada de UI acaba herdando dependências de infraestrutura (como bibliotecas de banco ou APIs de terceiros) só para poder dar um Create.

2. Factory Method no Delphi 13: Elegância com Generics

O Factory Method define uma interface para criar um objeto, mas deixa as subclasses decidirem qual classe instanciar. No Delphi 13, podemos tornar isso ainda mais poderoso usando Generics. O Factory Method moderno no Delphi 13 não deve ser apenas um método com um case. Ele deve atuar como um Service Locator especializado, onde as classes se registram de forma plugável.

Implementação Moderna com Registro Dinâmico

Utilizamos TDictionary e métodos anônimos para criar uma fábrica que é estendida sem nunca ser modificada (Princípio Aberto/Fechado).

type
  IPagamento = interface
    ['{D4B1C2A3-E5F6-47A8-B9C0-D1E2F3A4B5C6}']
    function Processar(AValor: Currency): string;
  end;

  TFactoryFunc = TFunc<IPagamento>;

  TPagamentoFactory = class
  private
    class var FRegistros: TDictionary<string, TFactoryFunc>;
    class constructor Create;
    class destructor Destroy;
  public
    // Permite que novas Units registrem suas implementações dinamicamente
    class procedure Registrar(const ATipo: string; AFunc: TFactoryFunc);
    class function Criar(const ATipo: string): IPagamento;
  end;

class function TPagamentoFactory.Criar(const ATipo: string): IPagamento;
var
  LFunc: TFactoryFunc;
begin
  if not FRegistros.TryGetValue(ATipo.ToLower, LFunc) then
    throw Exception.CreateFmt('Tipo "%s" não registrado na Factory.', [ATipo]);
  Result := LFunc();
end;

3. Abstract Factory: Famílias de Objetos

Enquanto o Factory Method foca em um produto, a Abstract Factory resolve o problema de criar famílias de objetos que devem trabalhar juntos. No Delphi 13, isso é vital para aplicações multi-plataforma (VCL vs FMX) ou multi-banco.

Aplicação no Dia a Dia: Multi-Tenancy e Versões de API

Imagine que seu software Delphi 13 atende a dois clientes: um utiliza banco de dados SQL Server e outro consome uma API REST.

  • A Abstract Factory permite que, no início da aplicação, você defina a “Fábrica de Dados” ativa.
  • Todo o seu sistema chamará Fabrica.GetRepositorioCliente, e receberá ou um TClienteRepositorySQL ou um TClienteRepositoryREST, sem que o formulário saiba a diferença.

Transição para o Clean Code: A transição aqui é clara: saímos de um código que “sabe demais” (conhece classes de banco, componentes de conexão e regras específicas) para um código que apenas “pede o que precisa” a uma fábrica abstrata. No modo antigo, você teria if UseSQL then... em cada tela. No Delphi 13, você define uma variável global ou injeta uma IDataFactory. O sistema apenas chama Fabrica.GetRepositoryCliente, sem nunca saber se o dado vem de um SELECT ou de um JSON.

Caso de Uso: Abstração de Persistência (SQL vs NoSQL)

Imagine que sua aplicação precisa rodar em clientes com Firebird (SQL) e clientes que usam MongoDB (NoSQL). A Abstract Factory garante que, se você usar a fábrica SQL, todos os repositórios (Cliente, Produto, Venda) serão compatíveis entre si.

A Transição Clean: No modo antigo, você teria if UseSQL then... em cada tela. No Delphi 13, você define uma variável global ou injeta uma IDataFactory. O sistema apenas chama Fabrica.GetRepositoryCliente, sem nunca saber se o dado vem de um SELECT ou de um JSON.

4. Gerenciamento de Memória e Ciclo de Vida em Fábricas

No Delphi 13, ao utilizar fábricas que retornam interfaces (IInterface), o gerenciamento de memória torna-se automático via contagem de referência. No entanto, se sua fábrica retornar objetos (TObject), a responsabilidade de destruição é de quem chamou a fábrica.

Dica de Arquitetura Sênior: Para evitar vazamentos de memória (Memory Leaks), sempre que possível, faça suas Factories retornarem Interfaces. No Delphi 13, isso permite que você use o objeto dentro de um escopo e ele seja liberado assim que a variável sair de escopo, mantendo o initialization e finalization das suas fábricas (que são Singletons por natureza) limpos e seguros.

  • Reference Counting: Ao retornar IInterface, a fábrica entrega um objeto cujo ciclo de vida é gerenciado automaticamente. Isso elimina o risco de Memory Leaks que ocorriam no modelo antigo de retornar objetos brutos.
  • RTTI e Attributes: Uma melhoria sênior no Delphi 13 é usar Custom Attributes para registrar classes na Factory automaticamente via RTTI no startup da aplicação, eliminando a necessidade de chamadas manuais de Registrar na seção initialization.
  • Performance de Dicionário: O uso de TDictionary com chaves string no Delphi 13 é altamente otimizado, mas para sistemas de altíssima performance (como backends Horse), considere usar um TDictionary<TEnum, TFactoryFunc> para evitar a alocação de strings em cada chamada de criação.

Referências

FLICK, Stefan. Design Patterns with Delphi: Build enterprise-grade applications with modern Delphi and the right design patterns. 1. ed. Birmingham: Packt Publishing, 2024.

GAMMA, Erich; HELM, Richard; JOHNSON, Ralph; VLISSIDES, John. Padrões de Projeto: Soluções reutilizáveis de software orientado a objetos. 1. ed. Porto Alegre: Bookman, 2000.

MARTIN, Robert C. Arquitetura Limpa: O guia do artesão para estrutura e design de software. 1. ed. Rio de Janeiro: Alta Books, 2019.

NOGUEIRA, Rodrigo. Delphi e Clean Architecture: Princípios e práticas para software escalável. 2. ed. São Paulo: Editora Engenharia de Software, 2025.

Compartilhar por e-mail

0 comentário(s)

Dê-nos sua opinião, seu comentário ajuda o site a crescer e melhorar a qualidade dos artigos.

Este site utiliza o Akismet para reduzir spam. Saiba como seus dados em comentários são processados.

Um próximo passo para cada interesse.

Continue estudando com os livros, acompanhe as videoaulas ou consulte as soluções para o seu projeto.

Descubra mais sobre Régys Borges da Silveira

Assine agora mesmo para continuar lendo e ter acesso ao arquivo completo.

Continuar lendo