Extrair uma validação da tela e reconhecer a direção das dependências
Tutorial · prática orientada · FundamentosSaber criar um projeto e adicionar uma unit
Unit principal compilada e exercitada com DCC32 37.0, build 37.0.59082.6021. Referência editorial: Delphi 11.3 e 13.2; essas duas versões não foram verificadas individualmente.Documentação, instalação e limites de Fundamentos em seis units
Quando uma aplicação cresce, é comum criar pastas como Forms, Utils e Services. Isso ajuda a localizar arquivos, mas não garante que o código esteja organizado: um formulário ainda pode conter validação, SQL e integração HTTP no mesmo evento.
Uma unit é uma fronteira de código. O primeiro trabalho é decidir o que ela oferece e quais detalhes deve esconder. Neste tutorial, vamos extrair a validação de um nome, sem depender de componentes visuais.
Interface e implementation têm papéis diferentes
A seção interface declara o que outras units podem utilizar. A seção implementation contém a implementação e pode manter auxiliares que não fazem parte do contrato público. A declaração de uma função e seu corpo não são duas funções distintas. A documentação apresenta essa estrutura no guia da linguagem Delphi.
Pense em um formulário que precisa conferir um nome antes de salvar. Expor uma função pequena permite que qualquer interface use a mesma regra. Não é necessário expor um formulário para validar uma string.
Extraia uma unit pequena
Crie Cadastro.Nomes.pas com este conteúdo. A regra adotada é didática: exige algum texto após remover espaços nas extremidades. Não pretende validar todos os nomes pessoais possíveis.
unit Cadastro.Nomes;
interface
function NomePreenchido(const Nome: string): Boolean;
implementation
uses
System.SysUtils;
function NomePreenchido(const Nome: string): Boolean;
begin
Result := Trim(Nome) <> '';
end;
end.System.SysUtils fica na implementação porque nenhum tipo público desse exemplo exige a unit. Quem chama NomePreenchido precisa conhecer seu parâmetro e seu retorno, não a função usada internamente.
No formulário, adicione Cadastro.Nomes ao uses e chame NomePreenchido(EditNome.Text). A tela continua responsável por obter o texto, apresentar a mensagem e posicionar o foco. A regra não precisa conhecer essas decisões.
A direção da dependência importa
O formulário pode depender de Cadastro.Nomes. A unit de nomes não deve passar a depender do formulário apenas para mostrar uma mensagem. Essa segunda dependência criaria um caminho de volta à apresentação e dificultaria reutilizar a validação.
Um teste mental útil é substituir a tela por um comando de console. A regra ainda funciona? Nesse exemplo, sim. Se a unit precisa de EditNome, ShowMessage ou do formulário principal, a fronteira ficou incompleta.
Evite usar uma unit Utils como destino de todo código extraído. Uma coleção de funções sem relação fica difícil de descobrir e revisar. Prefira nomes que revelem a responsabilidade: validação de nomes, cálculo de frete ou leitura de configuração.
Experimente sem abrir uma tela
Em um projeto de console, inclua a unit e execute as chamadas abaixo no bloco principal:
Writeln(NomePreenchido('Ana')); // esperado: TRUE
Writeln(NomePreenchido(' ')); // esperado: FALSE
Writeln(NomePreenchido(' Ana ')); // esperado: TRUEConfira também uma string vazia. As quatro entradas de nomes foram executadas no projeto de verificações e produziram o comportamento esperado. A unit completa foi compilada e exercitada no projeto de verificações disponível abaixo.
Quando não vale extrair
Nem toda linha de um evento precisa virar uma classe ou uma unit. Alterar a legenda de um botão em resposta a um clique é apresentação. Extraia quando existir uma responsabilidade que precise ser compreendida, reutilizada ou testada separadamente.
A estrutura também não precisa começar com quatro camadas e dezenas de arquivos. Comece com uma fronteira útil e observe se as próximas alterações ficam mais simples de localizar.
Desafio e próxima leitura
Acrescente uma segunda função que imponha um limite de tamanho definido por um contrato de cadastro. Decida se esse limite deve contar unidades UTF-16 ou caracteres percebidos pelo usuário antes de implementar; Length de uma string Delphi não resolve todas as questões de Unicode.
Depois, avance para Interfaces na prática para separar uma necessidade da forma de implementá-la.
Extração: antes, depois e contrato Unicode
Antes: evento decide validade e mensagem. Depois: Cadastro.Nomes decide NomePreenchido; a tela traduz o resultado.
- Liste entrada string e saída Boolean; não passe TEdit ao domínio.
- Extraia a função e crie console com nome, vazio e espaços.
- Mantenha mensagem na tela e remova dependências UI da unit.
- Compare console e tela chamando a mesma regra.
Árvore: Projeto.dpr → Cadastro.Nomes.pas; Formulario.pas → Cadastro.Nomes.pas. A dependência não volta para a tela.
Trim não define todas as categorias Unicode de espaço. Antes de ampliar NomePreenchido, declare política para tabulação, quebra de linha, espaço não separável e caracteres combinantes. Não mude contrato sem casos novos.
