Interfaces fluentes são uma abordagem poderosa para simplificar a interação com objetos em linguagens de programação. Baseada no conceito de encadeamento de métodos, essa técnica busca tornar o código mais legível e expressivo, eliminando a necessidade de intermediários desnecessários ao configurar ou manipular objetos.
No Delphi, combinar a abordagem de Fluent Interface com Generics oferece um ganho significativo em flexibilidade e reutilização. Generics permitem trabalhar com tipos parametrizados, eliminando a rigidez de tipos estáticos e proporcionando soluções adaptáveis.
Imagine uma estrutura que manipule listas genéricas, permitindo operações como adição, remoção, ordenação e busca, tudo em um único encadeamento de chamadas. Este artigo explora como implementar essa funcionalidade em Delphi, respeitando princípios de Clean Code e boas práticas recomendadas, como as Normas de Codificação já citadas aqui nesse blog.
Preparação e Cenário
Antes de mergulharmos na implementação, garantiremos que o conceito esteja claro. Se você já utiliza Generics e métodos fluentes, esta abordagem ampliará seu domínio, mostrando como uni-los eficientemente para criar sistemas elegantes e robustos.
Fluent Interface com Generics em Delphi
O que são Fluent Interfaces?
Fluent Interface é um padrão de design comportamental que permite o encadeamento de métodos em chamadas sequenciais. Seu principal objetivo é melhorar a legibilidade e reduzir a complexidade ao lidar com APIs ou objetos complexos.
No contexto do Delphi, métodos fluentes geralmente retornam a própria instância do objeto (Self
) após cada chamada, permitindo que operações subsequentes sejam executadas diretamente no mesmo objeto.
Um exemplo simples de uma Fluent Interface seria uma classe de builder:
1 2 3 4 5 6 7 8 |
TBuilder = class public function SetName(const AName: string): TBuilder; function SetAge(const AAge: Integer): TBuilder; function Build: TObject; end; |
O encadeamento ocorre ao retornar Self
:
1 2 3 4 5 6 |
TBuilder.Create .SetName('João') .SetAge(30) .Build; |
O papel dos Generics
Generics são uma funcionalidade que permite criar classes e métodos parametrizados por tipos. Eles aumentam a flexibilidade do código ao eliminar a necessidade de criar várias implementações para diferentes tipos.
Em Delphi, Generics são definidos usando a sintaxe <T>
para classes, métodos ou até mesmo interfaces:
1 2 3 4 5 6 7 8 9 |
TGenericList<T> = class private FItems: TArray<T>; public procedure Add(const Item: T); function Get(Index: Integer): T; end; |
Ao utilizar Generics, uma mesma classe pode operar com qualquer tipo, desde inteiros até classes complexas:
1 2 3 4 5 6 7 8 |
var List: TGenericList<Integer>; begin List := TGenericList<Integer>.Create; List.Add(10); end; |
Unindo Fluent Interfaces e Generics
A combinação de Fluent Interfaces com Generics possibilita o desenvolvimento de estruturas extremamente poderosas e reutilizáveis. Vamos criar uma classe que implemente uma lista genérica com métodos fluentes para:
- Adicionar elementos à lista.
- Remover elementos.
- Ordenar os itens.
- Buscar elementos.
Além disso, o código seguirá práticas como:
- Respeitar margens e indentação, conforme as Normas de Codificação.
- Nomenclatura clara para métodos e parâmetros, usando Pascal Case e prefixos.
- Modularidade para evitar métodos monolíticos.
Implementação da Estrutura Fluente com Generics
Estrutura da Classe Base
Começaremos criando a classe TFluentList<T>
, que será responsável por armazenar os elementos em uma lista genérica e oferecer métodos fluentes.
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 66 67 68 69 70 71 72 73 74 75 |
unit FluentList; interface uses System.Generics.Collections, System.SysUtils; type TFluentList<T> = class private FItems: TList<T>; public constructor Create; destructor Destroy; override; function Add(const AItem: T): TFluentList<T>; function Remove(const AItem: T): TFluentList<T>; function Sort(Comparison: TComparison<T>): TFluentList<T>; function Find(const Predicate: TFunc<T, Boolean>): T; function Items: TArray<T>; end; implementation { TFluentList<T> } constructor TFluentList<T>.Create; begin inherited; FItems := TList<T>.Create; end; destructor TFluentList<T>.Destroy; begin FItems.Free; inherited; end; function TFluentList<T>.Add(const AItem: T): TFluentList<T>; begin FItems.Add(AItem); Result := Self; // Retorna a própria instância end; function TFluentList<T>.Remove(const AItem: T): TFluentList<T>; begin FItems.Remove(AItem); Result := Self; end; function TFluentList<T>.Sort(Comparison: TComparison<T>): TFluentList<T>; begin FItems.Sort(TComparer<T>.Construct(Comparison)); Result := Self; end; function TFluentList<T>.Find(const Predicate: TFunc<T, Boolean>): T; var Item: T; begin for Item in FItems do if Predicate(Item) then Exit(Item); Result := Default(T); end; function TFluentList<T>.Items: TArray<T>; begin Result := FItems.ToArray; end; end. |
Detalhamento dos Métodos
1. Add
:
Adiciona um item à lista e retorna a instância da classe (Self
), permitindo o encadeamento.
1 2 3 4 5 6 7 |
function TFluentList<T>.Add(const AItem: T): TFluentList<T>; begin FItems.Add(AItem); Result := Self; end; |
2. Remove
:
Remove um item específico da lista usando o método padrão Remove
do TList<T>
.
1 2 3 4 5 6 7 |
function TFluentList<T>.Remove(const AItem: T): TFluentList<T>; begin FItems.Remove(AItem); Result := Self; end; |
3. Sort
:
Ordena a lista usando uma função de comparação passada como parâmetro.
1 2 3 4 5 6 7 |
function TFluentList<T>.Sort(Comparison: TComparison<T>): TFluentList<T>; begin FItems.Sort(TComparer<T>.Construct(Comparison)); Result := Self; end; |
4. Find
:
Realiza uma busca na lista com base em uma função de predicado, retornando o primeiro item que atender ao critério.
1 2 3 4 5 6 7 8 9 10 11 |
function TFluentList<T>.Find(const Predicate: TFunc<T, Boolean>): T; var Item: T; begin for Item in FItems do if Predicate(Item) then Exit(Item); Result := Default(T); end; |
5. Items
:
Retorna todos os itens da lista como um array, sem modificar o estado da classe.
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 |
uses FluentList, System.SysUtils; type TPerson = record Name: string; Age: Integer; end; var List: TFluentList<TPerson>; Found: TPerson; begin List := TFluentList<TPerson>.Create; try List.Add(TPerson.Create('Alice', 30)) .Add(TPerson.Create('Bob', 25)) .Add(TPerson.Create('Charlie', 35)) .Sort( function(const A, B: TPerson): Integer begin Result := CompareText(A.Name, B.Name); end ); Found := List.Find( function(const Person: TPerson): Boolean begin Result := Person.Name = 'Alice'; end ); WriteLn(Format('Found: %s, Age: %d', [Found.Name, Found.Age])); // Exibe todos os itens da lista for var Person in List.Items do WriteLn(Format('%s, %d', [Person.Name, Person.Age])); finally List.Free; end; end; |
Explicação do Exemplo
- Adição: Usamos o método
Add
para incluir três registros na lista. - Ordenação: O método
Sort
organiza a lista por nome, utilizandoCompareText
para comparação. - Busca:
Find
localiza o registro cujo nome é “Alice”. - Iteração: O método
Items
retorna todos os itens, permitindo iterar sobre eles.
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
A combinação de Fluent Interfaces com Generics em Delphi proporciona uma abordagem eficiente e elegante para manipulação de objetos, tornando o código mais legível, flexível e adaptável. O exemplo apresentado mostra como implementar uma estrutura fluente que opera sobre listas genéricas, com métodos para adicionar, remover, ordenar e buscar itens de forma encadeada.
Ao adotar essa técnica, você melhora a expressividade de suas APIs, facilita a manutenção e aumenta a reutilização de código. A utilização de boas práticas, como nomes significativos, modularidade e clareza, garante que a implementação atenda aos padrões de qualidade esperados em projetos modernos.
Gostou do conteúdo? Considere aplicar essa técnica em seus projetos e explorar outras combinações com Generics para criar soluções ainda mais robustas.
Adriano Santos