Boa noite, pessoal!
Sejam bem-vindos à “quinta temporada” de artigos! Dessa vez não fiz a pausa, já que fiquei ausente um bom tempo há alguns meses.
Antes de retornar aos assuntos envolvendo Engenharia de Software, decidi apresentar uma série de pequenos artigos sobre Delphi. Neste primeiro artigo, o assunto é Fluent Interface!
Já ouviu falar em Fluent Interface? Trata-se de um padrão de desenvolvimento de software que permite o encadeamento de métodos para facilitar a escrita e leitura do código. Em outras palavras, é uma técnica que possibilita chamadas seriais dentro do escopo de um mesmo objeto. Um dos propósitos deste padrão é aproximar o código-fonte de uma linguagem natural, como se estivéssemos codificando uma “frase”.
Exemplo 1
Sem delongas, apresentarei um exemplo pequeno como introdução deste assunto.
Suponha que seja necessário importar alguns dados, como nomes de cidades, que estejam em um formato fora do padrão semelhante ao texto abaixo:
1 |
" Rio+de+janeiro " |
Durante essa importação, deseja-se aplicar três regras:
- Remover os espaços em branco no inÃcio e no final do texto;
- Converter as letras para maiúsculas;
- Substituir um caracter (“+” por espaço).
Como você escreveria este código? Com Trim
, UpperCase
e StringReplace
, certo?
1 2 3 4 5 6 7 8 |
var Cidade: string; begin Cidade := ' Rio+de+janeiro '; Cidade := Trim(Cidade); Cidade := UpperCase(Cidade); Cidade := StringReplace(Cidade, '+', ' ', [rfReplaceAll]); end; |
Embora o código acima seja válido, há uma maneira de melhorá-lo. Nas versões mais recentes do Delphi, é possÃvel usar Fluent Interface para “encadear” estes três métodos:
1 2 3 4 5 6 |
var Cidade: string; begin Cidade := ' Rio+de+janeiro '; Cidade := Cidade.Trim.ToUpper.Replace('+', ' ', [rfReplaceAll]); end; |
Bem melhor, não? Todo o código fica em apenas uma linha sem comprometer a leitura. Na verdade, pode-se dizer que o código tornou-se mais legÃvel, não acham?
Exemplo 2
Dessa vez, imagine que seja preciso copiar as 10 primeiras letras de um texto e verificar se existe a palavra “INFO”. Vejam só:
1 2 3 4 5 6 7 8 |
var Texto: string; begin Texto := 'StatusInfoJunho2018LogProcessamento'; if Copy(Texto, 1, 10).ToUpper.Contains('INFO') then ShowMessage('Existe a palavra INFO!'); end; |
Fácil, hein? 🙂
Fluent Interface na RTL
Agora que vocês já conhecem o Fluent Interface, certamente encontrará algumas aplicações na própria RTL do Delphi. A classe TStringBuilder
é um exemplo clássico:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
var StringBuilder: TStringBuilder; begin StringBuilder := TStringBuilder.Create; StringBuilder .Append('Atalhos do Sistema:') .AppendLine.AppendLine .Append('F1 - Ajuda').AppendLine .Append('F4 - Relatórios').AppendLine .Append('F10 - Opções'); ShowMessage(StringBuilder.ToString); StringBuilder.Free; end; |
O código acima produz o seguinte resultado:
Desenvolvendo classes com Fluent Interface
Ao invés de apenas consumir este recurso, também podemos criá-lo em nossas classes. Para isso, basta que as suas funções retornem o próprio objeto. Na classe de exemplo abaixo, responsável por armazenar colunas de uma instrução SQL, foi aplicado o Fluent Interface no método AddColumn
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
type TSQLBuilder = class private FColumns: TStringList; public function AddColumn(const aColumn: string): TSQLBuilder; end; { TSQLBuilder } function TSQLBuilder.AddColumn(const aColumn: string): TSQLBuilder; begin FColumns.Add(aColumn); result := Self; // retorna a própria instância end; |
Como a função retorna o próprio objeto (Self
), podemos utilizar a classe dessa forma:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
var SQLBuilder: TSQLBuilder; begin SQLBuilder := TSQLBuilder.Create; SQLBuilder .AddColumn('Codigo') .AddColumn('Nome') .AddColumn('Cidade') .AddColumn('UF'); { ... } end; |
Espero que tirem bom proveito deste recurso, pessoal.
Grande abraço e até logo!
Boa noite, André,
Recurso muito interessando e útil, e por coincidência, hoje tive que usar o “Trim” e “UpperCase” para um tratamento… graças a você, e por mais um ensinamento, é mais uma melhoria que irei aplicar no meu sistema.
Parabéns e grande abraço.
Boa noite, Daniel! Sempre acompanhando o blog! 🙂
É isso aÃ. Aproveite bastante esses recursos. Já já tem mais!
Obrigado! Abração!
Que show em
Parabéns André. Sempre bem didático, como em todos seus posts.
Obrigado, Jocimar! Grande abraço!
Show, como sempre trazendo uma informação nova e útil!
Abraço
Obrigado, Ricardo!
Abração!
Grande André!
Mais um excelente post neste blog que já é referência entre os desenvolvedores Delphi.
Só uma pena que este recurso esteja presente somente nas versões mais novas, pois aqui na empresa eu utilizo o Delphi 2010.
Continue com seu ótimo trabalho.
Abraço.
Olá, Cleo!
Muito obrigado pelo comentário!
Seria muito bom se este recurso estivesse disponÃvel em versões mais inferiores do Delphi. Por outro lado, essa novidade pode trazer as empresas para as versões mais recentes! 🙂
Grande abraço!
Só é uma pena não podermos acrescentar mais de uma unit contendo helpers na cláusula USES. Infelizmente pelo que vi, somente a última declaração é que vale. Será que existe uma forma de contornar isso sem ter que colocar todos os helpers na mesma unit?
Boa tarde, Eduardo, tudo bem?
Desconheço essa restrição. Utilizo a versão Tokyo e consigo referenciar e usar duas ou mais units com helpers na cláusula uses. Talvez essa restrição exista na versão que você está utilizando.
Abraço!
Boa tarde André
Uso hoje a versão Seattle, mas vejo a limitação ocorrer à várias versões. Para reproduzir faça o seguinte:
– Declare no USES a unit System.SysUtils, que contém helpers para o tipo String.
– Declare esse helper e implemente o método TESTE.
Type
TTeste = record helper for String
public
function Teste: String;
end;
Pronto! Tente usar os helpers que estão contidos na unit System.SysUtils e verá que somente o método Teste do helper declarado está visÃvel. Se o helper estiver em outra unit, experimente brincar com a ordem da declaração no uses entre System.SysUtils e a unit que contém o helper TTeste.
Abraço!
Tem razão, Eduardo.
Como apenas uso Helpers para classes (class helpers) e não para tipos (record helpers), eu ainda não havia encontrado essa limitação.
Infelizmente não há uma forma simples de contorná-la. O tópico abaixo do StackOverflow entra em mais detalhes:
Is it possible to use two record helpers for the string type?
Abraço!