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?
- Violação do OCP (Open/Closed Principle): Para cada novo método de pagamento, você altera o código existente.
- Dificuldade de Teste: Você não consegue testar a lógica de “venda” sem carregar todas as dependências das classes de “pagamento”.
- Dificuldade de Mocking: Em testes unitários, você fica refém da instância real, impossibilitando testes isolados de lógica de negócio.
- Explosão de Uses: Sua Unit de negócio precisa conhecer todas as Units de pagamento.
- Rigidez: Adicionar um novo tipo exige recompilar o núcleo do sistema.
- 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 umTClienteRepositorySQLou umTClienteRepositoryREST, 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
Registrarna seçãoinitialization. - Performance de Dicionário: O uso de
TDictionarycom chaves string no Delphi 13 é altamente otimizado, mas para sistemas de altíssima performance (como backends Horse), considere usar umTDictionary<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.
Descubra mais sobre Régys Borges da Silveira
Assine para receber nossas notícias mais recentes por e-mail.
Dê-nos sua opinião, seu comentário ajuda o site a crescer e melhorar a qualidade dos artigos.