A Fluent Interface é um padrão de design que permite encadear métodos para criar um código mais legível, parecido com uma linguagem natural. Esse padrão é particularmente útil em cenários complexos, como o de configuração e emissão de documentos fiscais, onde diversos parâmetros precisam ser definidos antes de uma ação ser executada.
Neste artigo, exploraremos como implementar esse padrão em Delphi, aplicando-o a um caso real: a emissão de uma Nota Fiscal Eletrônica (NF-e). A implementação demonstrará como utilizar métodos fluentes para configurar dados obrigatórios, como emitente, destinatário e itens da nota, finalizando com a validação e geração da NF-e.
Implementação de Fluent Interface para NF-e
Vamos imaginar um cenário de emissão de uma NF-e, onde precisamos configurar diferentes aspectos: emitente, destinatário, itens e tributos. Abaixo está a implementação com métodos fluentes.
A Classe Principal TNFeBuilder
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65  | 
						type   TNFeBuilder = class   private     FEmitente: string;     FDestinatario: string;     FItens: TStringList;     FTotal: Currency;   public     constructor Create;     destructor Destroy; override;     function SetEmitente(const AEmitente: string): TNFeBuilder;     function SetDestinatario(const ADestinatario: string): TNFeBuilder;     function AddItem(const ADescricao: string; AQuantidade: Integer; AValorUnitario: Currency): TNFeBuilder;     function CalcularTotal: TNFeBuilder;     function GerarXML: string;   end; implementation constructor TNFeBuilder.Create; begin   FItens := TStringList.Create;   FTotal := 0.0; end; destructor TNFeBuilder.Destroy; begin   FItens.Free;   inherited; end; function TNFeBuilder.SetEmitente(const AEmitente: string): TNFeBuilder; begin   FEmitente := AEmitente;   Result := Self; end; function TNFeBuilder.SetDestinatario(const ADestinatario: string): TNFeBuilder; begin   FDestinatario := ADestinatario;   Result := Self; end; function TNFeBuilder.AddItem(const ADescricao: string; AQuantidade: Integer; AValorUnitario: Currency): TNFeBuilder; begin   FItens.Add(Format('%s | Qtd: %d | Unit: %.2f', [ADescricao, AQuantidade, AValorUnitario]));   FTotal := FTotal + (AQuantidade * AValorUnitario);   Result := Self; end; function TNFeBuilder.CalcularTotal: TNFeBuilder; begin   // Aqui podem ser aplicados cálculos de impostos ou descontos   Result := Self; end; function TNFeBuilder.GerarXML: string; begin   Result := Format(     '<NFe><Emitente>%s</Emitente><Destinatario>%s</Destinatario><Itens>%s</Itens><Total>%.2f</Total></NFe>',     [FEmitente, FDestinatario, FItens.Text, FTotal]   ); end;  | 
					
Exemplo de Uso:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20  | 
						var   NFe: TNFeBuilder;   XML: string; begin   NFe := TNFeBuilder.Create;   try     XML := NFe.SetEmitente('Empresa ABC LTDA')               .SetDestinatario('João da Silva')               .AddItem('Notebook', 1, 3500.00)               .AddItem('Mouse', 2, 120.00)               .CalcularTotal               .GerarXML;     Writeln(XML);   finally     NFe.Free;   end; end;  | 
					
Saída do XML Gerado
| 
					 1 2 3 4 5 6 7 8 9 10 11  | 
						<NFe>   <Emitente>Empresa ABC LTDA</Emitente>   <Destinatario>João da Silva</Destinatario>   <Itens>     Notebook | Qtd: 1 | Unit: 3500.00     Mouse | Qtd: 2 | Unit: 120.00   </Itens>   <Total>3740.00</Total> </NFe>  | 
					
Explicando o Código: Conceitos Aplicados
O padrão Fluent Interface utiliza uma técnica simples, porém poderosa, para possibilitar o encadeamento de métodos: retornar a própria instância da classe (Self). Isso permite que cada método execute sua operação e, ao mesmo tempo, devolva o controle ao objeto que iniciou a sequência. Aqui estão os principais conceitos aplicados no código:
- O Uso de 
Selfpara Encadeamento 
No código, cada método que modifica ou configura a classe termina com a seguinte linha:
| 
					 1 2 3  | 
						Result := Self;  | 
					
O Self é uma referência para a própria instância do objeto que está executando o método. Quando o método retorna essa instância, é possível continuar chamando outros métodos sem sair do contexto do objeto. Isso cria o efeito de “fluidez” que caracteriza o padrão Fluent Interface.
Exemplo prático:
No método SetEmitente, configuramos o emitente e retornamos a instância:
| 
					 1 2 3 4 5 6 7  | 
						function TNFeBuilder.SetEmitente(const AEmitente: string): TNFeBuilder; begin   FEmitente := AEmitente; // Configura o emitente   Result := Self;         // Retorna a própria instância end;  | 
					
Ao encadear chamadas, como em:
| 
					 1 2 3 4  | 
						NFe.SetEmitente('Empresa ABC LTDA')    .SetDestinatario('João da Silva');  | 
					
2. Manutenção do Estado Interno do Objeto
Cada método modifica o estado interno do objeto (TNFeBuilder), atualizando os atributos privados. Isso assegura que os dados sejam acumulados à medida que os métodos são encadeados.
Exemplo:
O método AddItem adiciona um item à lista interna e recalcula o total:
| 
					 1 2 3 4 5 6 7 8  | 
						function TNFeBuilder.AddItem(const ADescricao: string; AQuantidade: Integer; AValorUnitario: Currency): TNFeBuilder; begin   FItens.Add(Format('%s | Qtd: %d | Unit: %.2f', [ADescricao, AQuantidade, AValorUnitario]));   FTotal := FTotal + (AQuantidade * AValorUnitario);   Result := Self; end;  | 
					
Ao encadear múltiplos itens:
| 
					 1 2 3 4  | 
						NFe.AddItem('Notebook', 1, 3500.00)    .AddItem('Mouse', 2, 120.00);  | 
					
Os itens são adicionados à lista interna FItens, e o total é atualizado após cada adição.
3. Retorno Diferente no Último Método
O último método no encadeamento (GerarXML) não retorna a instância da classe. Em vez disso, ele retorna um resultado final (uma string com o XML). Essa abordagem permite finalizar o fluxo com o dado esperado, sem interromper o padrão fluente.
Exemplo:
| 
					 1 2 3 4 5 6 7 8 9  | 
						function TNFeBuilder.GerarXML: string; begin   Result := Format(     '<NFe><Emitente>%s</Emitente><Destinatario>%s</Destinatario><Itens>%s</Itens><Total>%.2f</Total></NFe>',     [FEmitente, FDestinatario, FItens.Text, FTotal]   ); end;  | 
					
Essa distinção garante que, enquanto os métodos de configuração e processamento retornam a instância para continuar o encadeamento, o método final entrega o resultado da operação.
4. Vantagens do Fluent Interface
- Legibilidade: O código resultante é claro, com métodos que se comportam como frases descritivas.
 - Manutenção: As configurações são aplicadas no mesmo objeto, centralizando o estado e reduzindo variáveis temporárias.
 - Encapsulamento: Todos os detalhes de implementação são ocultos. O desenvolvedor interage apenas com os métodos necessários.
 
Comparação:
Sem Fluent Interface:
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13  | 
						var   NFe: TNFeBuilder; begin   NFe := TNFeBuilder.Create;   NFe.SetEmitente('Empresa ABC LTDA');   NFe.SetDestinatario('João da Silva');   NFe.AddItem('Notebook', 1, 3500.00);   NFe.AddItem('Mouse', 2, 120.00);   NFe.CalcularTotal;   Writeln(NFe.GerarXML); end;  | 
					
Com Fluent Interface:
| 
					 1 2 3 4 5 6 7 8 9 10 11  | 
						Writeln(   TNFeBuilder.Create     .SetEmitente('Empresa ABC LTDA')     .SetDestinatario('João da Silva')     .AddItem('Notebook', 1, 3500.00)     .AddItem('Mouse', 2, 120.00)     .CalcularTotal     .GerarXML );  | 
					
Encadeamento Avançado com Retorno à Classe Pai
Vamos avançar um pouco mais? E se quisermos ter outras classes para preenchimento?
O conceito de encadeamento avançado com retorno à classe pai resolve um problema recorrente em APIs fluentes: a necessidade de navegar entre configurações de componentes subordinados sem perder o contexto do objeto principal. Esse modelo é especialmente útil em cenários onde partes específicas de uma configuração (como itens em uma nota fiscal) têm regras ou estruturas próprias, mas ainda precisam fazer parte de um contexto maior, representado pela classe pai.
Motivo para Fazer Isso
A abordagem de retornar à classe pai é necessária quando:
- Modularidade: Partes do sistema, como itens, tributos ou pagamentos, possuem lógica própria e precisam ser organizadas em classes separadas para melhorar a coesão. Isso evita que a classe principal fique sobrecarregada com responsabilidades que poderiam ser delegadas.
 - Encadeamento Natural: Para que a API fluente seja contínua e legível, os métodos de uma classe subordinada (como 
AddItem) precisam retornar ao fluxo principal ao final de suas operações. O uso de um método como&Endpermite retornar à classe pai sem quebrar o padrão fluente. - Reuso de Componentes: Com componentes subordinados encapsulados em classes específicas (como 
TItens), é possível reutilizá-los em diferentes contextos, sem necessidade de replicar lógica na classe principal. - Hierarquia de Configuração: No exemplo de emissão de NF-e, as configurações possuem uma hierarquia natural: o cabeçalho pertence ao documento principal, enquanto os itens e seus detalhes pertencem a uma parte específica dessa estrutura. Essa hierarquia deve ser refletida no design das classes.
 
Vantagens do Retorno à Classe Pai
- Legibilidade e Fluidez: O código gerado é mais próximo de uma linguagem natural. Desenvolvedores podem configurar diferentes partes do objeto sem perder o encadeamento ou criar blocos confusos.Exemplo:
 
| 
					 1 2 3 4 5 6 7 8  | 
						NFe   .Itens     .AddItem('001', 3500.00, 1)     .AddItem('002', 120.00, 2)   .&End   .GerarXML;  | 
					
Esse fluxo é muito mais legível do que manter todas as configurações em uma única classe.
Encapsulamento: Cada componente, como TItens, gerencia seu próprio estado e lógica, sem expor detalhes à classe principal. Isso melhora o design orientado a objetos, facilitando a manutenção e extensão do código.
Organização Modular: Com as classes subordinadas, a lógica específica fica isolada. Por exemplo, a lógica para adicionar itens à nota fiscal está completamente encapsulada em TItens. A classe principal (TNFeBuilder) foca em tarefas de alto nível, como gerar o XML.
Extensibilidade: Esse design facilita a adição de novos componentes ou funcionalidades. Caso seja necessário adicionar tributos ou pagamentos à NF-e, bastaria criar novas classes subordinadas e vinculá-las ao fluxo principal.
Menor Acoplamento: A classe subordinada (TItens) mantém uma referência à classe pai, mas o oposto não é verdadeiro. Isso reduz o acoplamento e melhora a flexibilidade do código.
Melhoria de Testabilidade: Componentes encapsulados podem ser testados isoladamente. Por exemplo, a funcionalidade de adicionar itens e calcular o total pode ser testada diretamente em TItens, sem interferir na lógica da classe principal.
Resumo
O encadeamento avançado com retorno à classe pai não é apenas uma questão de melhorar a legibilidade. Ele reflete boas práticas de design orientado a objetos, dividindo responsabilidades, reduzindo o acoplamento e tornando o código mais modular e testável. Em sistemas complexos, essa abordagem oferece uma combinação poderosa de flexibilidade, clareza e escalabilidade, essencial para aplicações robustas e fáceis de manter.
Vamos a um exemplo prático:
1. Classe TItens para Gerenciar os Itens
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16  | 
						type   TNFeBuilder = class; // Declaração forward da classe pai   TItens = class   private     FParent: TNFeBuilder; // Referência à classe pai     FItens: TStringList;   public     constructor Create(AParent: TNFeBuilder);     destructor Destroy; override;     function AddItem(const AID: string; APreco: Currency; AQuantidade: Integer): TItens;     function &End: TNFeBuilder; // Retorna à classe pai   end;  | 
					
2. Classe TNFeBuilder para a NF-e
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24  | 
						type   TNFeBuilder = class   private     FEmitente: string;     FCNPJ: string;     FEndereco: string;     FItens: TItens; // Instância de `TItens`   public     constructor Create;     destructor Destroy; override;     // Métodos fluentes para o cabeçalho     function SetEmitente(const ARazaoSocial: string): TNFeBuilder;     function SetCNPJ(const ACNPJ: string): TNFeBuilder;     function SetEndereco(const AEndereco: string): TNFeBuilder;     // Método fluente para acessar os itens     function Itens: TItens;     // Finaliza e gera o XML     function GerarXML: string;   end;  | 
					
3. Implementação dos Métodos
Métodos da Classe TItens
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24  | 
						constructor TItens.Create(AParent: TNFeBuilder); begin   FParent := AParent; // Armazena a referência à classe pai   FItens := TStringList.Create; end; destructor TItens.Destroy; begin   FItens.Free;   inherited; end; function TItens.AddItem(const AID: string; APreco: Currency; AQuantidade: Integer): TItens; begin   FItens.Add(Format('ID: %s | Preço: %.2f | Quantidade: %d', [AID, APreco, AQuantidade]));   Result := Self; // Permite continuar adicionando itens end; function TItens.&End: TNFeBuilder; begin   Result := FParent; // Retorna à classe pai para continuar o encadeamento end;  | 
					
4. Exemplo de Uso
| 
					 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23  | 
						var   NFe: TNFeBuilder;   XML: string; begin   NFe := TNFeBuilder.Create;   try     XML := NFe       .SetEmitente('Empresa ABC LTDA')       .SetCNPJ('12.345.678/0001-99')       .SetEndereco('Rua das Flores, 123')       .Itens         .AddItem('001', 3500.00, 1)         .AddItem('002', 120.00, 2)       .&End       .GerarXML;     Writeln(XML);   finally     NFe.Free;   end; end;  | 
					
Saída do XML Gerado
| 
					 1 2 3 4 5 6 7 8 9 10 11  | 
						<NFe>   <Emitente>Empresa ABC LTDA</Emitente>   <CNPJ>12.345.678/0001-99</CNPJ>   <Endereco>Rua das Flores, 123</Endereco>   <Itens>     ID: 001 | Preço: 3500.00 | Quantidade: 1     ID: 002 | Preço: 120.00 | Quantidade: 2   </Itens> </NFe>  | 
					
Explicando o Funcionamento
- Retorno à Classe Pai (
&End):
A função&Endda classeTItensretorna a instância da classeTNFeBuilder, armazenada emFParent. Isso permite continuar o encadeamento no objeto principal após configurar os itens. - Referência Cruzada:
A classeTItensconhece sua classe pai (TNFeBuilder), mas o oposto não ocorre diretamente. Isso garante encapsulamento, permitindo queTItensadministre apenas os dados relacionados aos itens. - Legibilidade:
Esse modelo de encadeamento dentro de encadeamentos melhora a organização e torna o código mais expressivo e próximo da linguagem natural. 
Participe da Comunidade no Telegram
🚀 Quer continuar essa discussão e trocar ideias com outros desenvolvedores? Junte-se à nossa comunidade no Telegram! Lá, você pode comentar sobre o que achou deste artigo, tirar suas dúvidas e compartilhar suas experiências com Delphi e ainda discutir ou tirar suas dúvidas sobre os mais variados temas em uma comunidade com mais de 1.000 desenvolvedores.
🔗 Clique aqui para entrar na comunidade
Te vejo lá!
Conclusão
O padrão Fluent Interface, combinado com o uso de Self, é uma ferramenta poderosa para criar APIs legíveis e práticas. No exemplo de emissão de NF-e, os métodos encadeados simplificam a configuração e emissão, proporcionando um fluxo natural. Ao dominar esse padrão, você melhora significativamente a qualidade e a clareza do código, beneficiando todo o ciclo de desenvolvimento.

            
                            
                            
                            
Gosto de utilizar esse padrão, nem sabia que tinha um nome. Muito bom artigo.
Obrigado, vamos juntos.