No ecossistema Delphi, é comum lidarmos com bibliotecas que atravessam décadas. O desafio surge quando tentamos implementar arquiteturas modernas, como Clean Architecture ou DDD, e nos deparamos com componentes de terceiros ou DLLs legadas que não conhecem interfaces ou tipos genéricos.
O padrão Adapter (ou Adaptador) resolve este conflito ao converter a interface de uma classe na interface que o seu sistema espera. Ele permite que classes com interfaces incompatíveis trabalhem juntas, funcionando como um “tradutor” técnico.
1. O Problema da Incompatibilidade e a “Camada de Anticorrupção”
O Perigo do Acoplamento Direto
Muitos sistemas Delphi sofrem do que chamamos de “Código Refém”. Isso acontece quando a lógica de negócio está espalhada e dependente de componentes visuais ou bibliotecas de acesso a dados específicas.
Impactos negativos no Delphi 13:
- Violação do OCP (Open/Closed Principle): Se precisar de trocar um componente de exportação de PDF, terá de abrir e modificar todas as unidades que o utilizam.
- Inviabilidade de Testes Unitários: Se a sua lógica depende de uma DLL de balança física, não conseguirá testar o código num ambiente de Integração Contínua (CI).
- Poluição de Tipos: O seu domínio acaba por importar units pesadas de terceiros apenas para referenciar um tipo de dado simples.
2. Implementação Técnica: O Adapter como Interface de Domínio
No Delphi 13 (Athens), a melhor forma de implementar o Adapter é através da composição (Object Adapter). Definimos uma interface que representa a nossa necessidade de negócio e criamos uma classe que “embrulha” o componente legado.
Exemplo Profissional: Processamento de Pagamentos
Imagine que o seu sistema precisa processar pagamentos. Hoje utiliza o “Provedor A”, mas quer estar pronto para o “Provedor B” sem reescrever o motor de vendas.
unit Regys.Patterns.Adapter;
interface
uses
System.SysUtils;
type
// 1. A Interface de Domínio (O que o meu sistema entende)
IProcessadorPagamento = interface
['{D4C3B2A1-F6E5-4789-B1A0-C2D3E4F5A6B7}']
procedure Processar(const AValor: Currency);
end;
// 2. O Adaptee (A classe de terceiros, complexa e incompatível)
TLegacyBankSDK = class
public
procedure ExecuteTransaction(AmountInCents: Integer; CurrencyCode: string);
end;
// 3. O Adapter (O Tradutor)
TBankAdapter = class(TInterfacedObject, IProcessadorPagamento)
private
FSDK: TLegacyBankSDK;
public
constructor Create(ASDK: TLegacyBankSDK);
destructor Destroy; override;
procedure Processar(const AValor: Currency);
end;
implementation
{ TBankAdapter }
constructor TBankAdapter.Create(ASDK: TLegacyBankSDK);
begin
FSDK := ASDK;
end;
destructor TBankAdapter.Destroy;
begin
FSDK.Free;
inherited;
end;
procedure TBankAdapter.Processar(const AValor: Currency);
begin
// O Adapter converte o nosso 'Currency' para o 'Cents' esperado pelo SDK
// e abstrai parâmetros fixos como o 'CurrencyCode'
FSDK.ExecuteTransaction(Round(AValor * 100), 'BRL');
end;
{ TLegacyBankSDK }
procedure TLegacyBankSDK.ExecuteTransaction(AmountInCents: Integer; CurrencyCode: string);
begin
// Lógica complexa da DLL de terceiros aqui...
Writeln(Format('Transação de %d cents via SDK Legado.', [AmountInCents]));
end;
end.
3. Quando Utilizar o Adapter no Seu Projeto Delphi?
O padrão Adapter não deve ser visto apenas como um “remendo” para código velho. No Delphi 13, ele é uma ferramenta estratégica de arquitetura. Ele atua como uma Anticorruption Layer, garantindo que conceitos externos (como o modelo de dados de um fornecedor de nuvem ou a estrutura de uma DLL de balança) não “vazem” para dentro do seu modelo de domínio.
A. Wrappers de Componentes VCL e FireDAC
Um erro comum no Delphi é espalhar componentes como o TFDQuery ou TRESTClient por toda a aplicação.
- Cenário de Uso: Imagine que você utiliza um componente de terceiro para gerar boletos. Em vez de sua classe
TFinanceiroconhecer as propriedades específicas desse componente, você cria a interfaceIBoletoAdapter. - Resultado: Se amanhã você decidir trocar o componente (ex: de Fortes Report para FastReport), a sua lógica de negócio permanecerá intacta. Apenas o Adapter será reescrito.
B. Consumo de APIs REST com Múltiplas Versões
No mundo moderno, APIs mudam de versão constantemente. Ao utilizar o Adapter no Delphi 13:
- Você define uma interface única para o serviço (ex:
IEntregasService). - Implementa o
TLoggiAdapterV1e oTLoggiAdapterV2. - Através de uma Factory ou Injeção de Dependência, o seu sistema escolhe qual adaptador usar com base em uma configuração, sem que o restante do código saiba qual versão da API está sendo consumida.
C. Mocks e Testabilidade no Delphi 13
Talvez o maior ganho do dia a dia seja a testabilidade. Se o seu código depende de um hardware físico (como uma impressora térmica ou ECF), você nunca conseguiria escrever um teste unitário que rode em um servidor de CI (Integração Contínua).
- A Transição Clean: Ao usar o Adapter, você cria um
TImpressoraMock. Durante os testes, o sistema “acha” que está imprimindo, mas o Adapter apenas valida se os dados chegaram corretamente, permitindo testes rápidos, determinísticos e sem dependência de hardware.
D. Unificação de Bibliotecas Multiplataforma
Se você trabalha com projetos Cross-Platform (VCL e FMX), o Adapter pode unificar comportamentos que são diferentes entre o Windows e o Android. Você pode ter um Adapter que lida com notificações push usando bibliotecas diferentes em cada sistema operacional, expondo uma interface INotification única para o seu código principal.
4. Nuances Técnicas e Performance no Delphi 13
Para fechar o Artigo 05 com o selo de qualidade do regys.com.br, a Seção 4 deve focar na “escovação de bits” e nas decisões arquiteturais que um desenvolvedor sênior toma ao implementar o Adapter no Delphi 13. Vamos detalhar como o compilador lida com essas camadas extras e como gerenciar a memória de forma impecável.
4. Nuances Técnicas, Performance e Memória no Delphi 13
Implementar um Adapter não é apenas “envelopar” uma classe. No Delphi 13 (Athens), precisamos considerar como o compilador otimiza essas chamadas e, principalmente, como evitar os armadilhas de gerenciamento de memória ao misturar objetos (TObject) com interfaces (IInterface).
A. Composição vs. Herança (Object vs. Class Adapter)
Embora a literatura clássica cite o Class Adapter (baseado em herança múltipla), no Delphi 13 focamos quase exclusivamente no Object Adapter (composição).
- Por que? O Delphi não suporta herança múltipla de classes. Ao usar composição, o seu Adapter pode herdar de
TInterfacedObject, ganhando contagem de referência automática (ARC), enquanto mantém o objeto legado (Adaptee) encapsulado de forma privada.
B. O Desafio do Gerenciamento de Memória
Este é o ponto onde muitos desenvolvedores falham. Se o seu Adapter cria a instância do objeto legado internamente, ele deve ser responsável por destruí-la.
destructor TBankAdapter.Destroy;
begin
if Assigned(FSDK) then
FSDK.Free; // Garante que o objeto legado morra com o Adapter
inherited;
end;
No Delphi 13, se você estiver passando o objeto legado via injeção no construtor, deve ficar claro quem detém a posse (ownership). Se o Adapter for o dono, ele limpa; se não, ele apenas referencia. O uso do atributo [Weak] pode ser necessário se houver risco de referências circulares em estruturas mais complexas.
C. Overhead e Performance: O Mito da Lentidão
Muitos temem que adicionar uma camada de tradução (Adapter) degrade a performance. No entanto:
- Inline Expansion: O compilador do Delphi 13 é extremamente eficiente. Em muitos casos, se o método do Adapter for simples, a chamada pode ser otimizada.
- Custo de Indireção: O custo de uma chamada de método virtual através de uma interface é medido em nanosegundos. Comparado ao tempo de processamento de uma regra de negócio, acesso a banco de dados ou chamada de API, o impacto de um Adapter é estatisticamente irrelevante. O ganho em manutenibilidade supera qualquer custo de CPU nesse cenário.
D. Generics e Adapters de Coleção
Um recurso poderoso do Delphi 13 é criar Adapters Genéricos. Imagine converter um TDataSet (legado) em uma IEnumerable<T> (moderno). Com Generics, você escreve um único Adapter capaz de traduzir diferentes estruturas de dados antigas para as novas coleções do System.Generics.Collections, mantendo o seu código de UI ou Business totalmente limpo e tipado.
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.
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.
TEIXEIRA, Marcello. Delphi High Performance: Build fast Delphi applications using concurrency, parallel programming and memory management. 1. ed. Birmingham: Packt Publishing, 2018.
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.