registration-forms-lazarus
Desktop registration system built with Lazarus and Object Pascal, demonstrating form inheritance, polymorphism and reusable validation.
- Active · last change 8-17-2026
- ★ 1 on GitHub
README
Registration Forms
Aplicação desktop desenvolvida em Lazarus com Free Pascal para demonstrar a criação e reutilização de formulários por meio de herança, polimorfismo e separação de responsabilidades.
O projeto parte de um formulário base de cadastro e utiliza essa estrutura para diferentes tipos de registros, evitando a repetição de componentes e comportamentos comuns.
Além do requisito de herança entre formulários, a aplicação foi expandida com múltiplas telas de cadastro, validações reutilizáveis, criação centralizada de formulários e integração com uma API externa para consulta de CEP.
Funcionalidades
A aplicação possui uma tela principal responsável pela navegação entre 10 tipos de cadastro:
- Cliente
- Freelancer
- Funcionário
- Fornecedor
- Empresa
- Prestador de Serviço
- Aluno
- Instrutor
- Parceiro
- Representante Comercial
Os formulários compartilham uma estrutura base contendo:
- Cabeçalho;
- Área de conteúdo;
- Botão Salvar;
- Botão Cancelar;
- Comportamento comum de validação;
- Padronização visual.
Também foram implementadas:
- validação de CPF;
- validação de CNPJ;
- validação de CEP;
- validação de e-mail;
- validação de valores numéricos positivos;
- consulta de endereço por CEP;
- preenchimento automático de endereço;
- criação centralizada dos formulários;
- navegação reutilizável.
Tecnologias
- Lazarus IDE
- Free Pascal
- LCL (Lazarus Component Library)
- HTTP Client
- JSON
- ViaCEP
- Git
- GitHub
Arquitetura
O projeto foi dividido em responsabilidades específicas para evitar que regras diferentes ficassem concentradas nos formulários.
De forma simplificada:
MainForm
|
v
FormFactory
|
+-------------------------------+
| |
v v
RegistrationForm Formulários filhos
|
+-------------+-------------+
| |
v v
ValidationUtils CepService
|
v
ViaCEPCada parte possui uma responsabilidade definida.
MainForm
MainForm é responsável pela navegação da aplicação.
A tela principal não implementa as regras específicas dos cadastros e não precisa conhecer o processo interno de criação de cada formulário.
Os botões utilizam um evento compartilhado e a propriedade Tag para identificar o tipo de formulário que deve ser aberto.
Isso evita a criação de diversos eventos praticamente idênticos.
RegistrationForm
RegistrationForm funciona como formulário base para as telas de cadastro.
Ele concentra os elementos e comportamentos compartilhados, como:
- estrutura visual;
- cabeçalho;
- painel de conteúdo;
- painel de ações;
- botão Salvar;
- botão Cancelar;
- fluxo comum de validação.
Os demais formulários herdam dessa classe.
Exemplo conceitual:
TfrmCustomer = class(TRegistrationForm)Dessa forma, não foi necessário recriar a estrutura comum em cada nova tela.
Herança de formulários
Um dos principais objetivos do projeto foi utilizar herança visual e comportamental.
Foram desenvolvidos 10 formulários derivados da mesma estrutura base:
TRegistrationForm
│
├── TfrmCustomer
├── TfrmFreelancer
├── TfrmEmployee
├── TfrmSupplier
├── TfrmCompany
├── TfrmServiceProvider
├── TfrmStudent
├── TfrmInstructor
├── TfrmPartner
└── TfrmSalesRepresentativeCada formulário adiciona apenas os componentes e regras necessários ao seu próprio contexto.
Isso permite que alterações comuns sejam centralizadas no formulário pai, reduzindo duplicação e facilitando manutenção.
Polimorfismo e validação
O formulário base declara um método virtual de validação:
function ValidateData: Boolean; virtual;Os formulários especializados sobrescrevem esse comportamento:
function ValidateData: Boolean; override;O botão Salvar pertence ao formulário base e executa a validação sem precisar saber qual formulário está aberto.
Conceitualmente:
Salvar
|
v
ValidateData
|
+--> Cliente -> valida dados do cliente
+--> Fornecedor -> valida dados do fornecedor
+--> Funcionário -> valida dados do funcionário
+--> ...Essa abordagem permite reutilizar o fluxo comum e especializar somente a regra que realmente muda.
Form Factory
Com o aumento da quantidade de formulários, a criação direta das telas no MainForm começaria a gerar código repetitivo.
Para centralizar essa responsabilidade foi criada a TFormFactory.
Os tipos disponíveis são representados por um enum:
TRegistrationFormTypeA tela principal informa qual cadastro deseja abrir e a Factory determina qual classe deve ser instanciada.
Fluxo:
MainForm
|
| rftCustomer
v
TFormFactory
|
v
TfrmCustomer.CreateIsso mantém a criação dos formulários em um ponto central e deixa a tela principal focada na navegação.
Navegação
Inicialmente, cada botão poderia possuir seu próprio evento de abertura.
Com 10 formulários isso produziria diversos métodos com praticamente o mesmo comportamento.
A navegação foi então centralizada utilizando um único evento e a propriedade Tag dos botões.
Cada Tag corresponde a um valor de TRegistrationFormType.
O fluxo passa a ser:
Botão
|
v
Tag
|
v
TRegistrationFormType
|
v
OpenRegistrationForm
|
v
TFormFactoryA solução reduz repetição e facilita a inclusão de novos formulários.
Integração com ViaCEP
O projeto realiza consumo de uma API externa utilizando o serviço ViaCEP.
A integração é utilizada nos cadastros de:
- Cliente;
- Fornecedor.
O usuário informa um CEP e solicita a consulta. Quando encontrado, os dados de endereço são preenchidos automaticamente.
Fluxo:
CEP informado
|
v
CustomerForm / SupplierForm
|
v
TCepService
|
v
Requisição HTTP
|
v
ViaCEP
|
v
JSON
|
v
TAddress
|
v
Preenchimento do formulárioCepService
A comunicação HTTP não foi implementada diretamente nos formulários.
Essa responsabilidade pertence ao TCepService.
Os formulários apenas solicitam:
Address := TCepService.FindByZipCode(ZipCode);O serviço fica responsável pela comunicação com a API e pelo tratamento da resposta.
Essa separação evita que detalhes de HTTP e JSON sejam misturados com código da interface gráfica.
Além disso, o mesmo serviço pode ser reutilizado por diferentes formulários.
AddressModel
Os dados retornados pela consulta de CEP são representados por TAddress.
O modelo permite transportar os dados de endereço entre o serviço e a interface sem fazer os formulários trabalharem diretamente com a estrutura JSON retornada pela API.
Entre os dados utilizados estão:
CEP
Logradouro
Bairro
Cidade
UFValidações reutilizáveis
Regras comuns foram centralizadas em TValidationUtils.
Foram implementadas validações para:
CPF
CNPJ
CEP
E-mail
Número positivoEm vez de cada formulário possuir sua própria implementação do algoritmo de CPF, por exemplo, ele apenas solicita:
TValidationUtils.IsValidCpf(edtCpf.Text)O mesmo princípio é utilizado para as demais validações.
Isso evita duplicação de regras e mantém os formulários responsáveis principalmente pelo fluxo da interface.
Separação de responsabilidades
Durante o desenvolvimento foi adotada uma separação simples entre as responsabilidades da aplicação.
Interface
Responsável por:
- interação com o usuário;
- leitura dos campos;
- exibição de mensagens;
- apresentação dos dados.
RegistrationForm
Responsável por:
- comportamento comum dos cadastros;
- estrutura compartilhada;
- ações comuns;
- fluxo de validação.
FormFactory
Responsável por:
- decidir qual formulário deve ser instanciado;
- centralizar a criação das telas.
ValidationUtils
Responsável por:
- regras reutilizáveis de validação.
CepService
Responsável por:
- comunicação HTTP;
- consulta de CEP;
- processamento dos dados da API.
AddressModel
Responsável por:
- representar os dados de endereço utilizados pela aplicação.
Decisões técnicas
Utilização de um formulário base
Foi escolhido um formulário base porque as telas de cadastro possuem estrutura e comportamentos semelhantes.
Sem herança, elementos como Salvar, Cancelar, cabeçalho e fluxo de validação precisariam ser repetidos em cada tela.
Criação de 10 formulários derivados
Apesar de o requisito inicial poder ser demonstrado com uma quantidade menor de formulários, foram desenvolvidos 10 exemplos para demonstrar de forma mais clara a escalabilidade da solução.
A inclusão de novas telas exige principalmente a implementação das características específicas do novo cadastro, enquanto a estrutura comum continua sendo herdada.
Uso de métodos virtuais
ValidateData foi definido como virtual para permitir que o formulário pai controle o fluxo de salvamento enquanto cada descendente implementa suas próprias regras.
Criação da FormFactory
A Factory foi introduzida depois que vários formulários já existiam.
A decisão surgiu da identificação de repetição no processo de criação e abertura das telas.
Em vez de espalhar chamadas de criação pelo MainForm, essa responsabilidade passou a possuir um ponto central.
Serviço separado para CEP
A integração com ViaCEP foi isolada da interface para evitar que requisições HTTP e processamento JSON fossem implementados dentro dos eventos dos botões.
Isso também permitiu reutilizar a mesma integração nos cadastros de Cliente e Fornecedor.
Centralização das validações
Validações compartilhadas foram retiradas dos formulários e centralizadas em uma classe utilitária.
A decisão evita múltiplas implementações da mesma regra.
Simplicidade
Apesar da aplicação de conceitos como herança, polimorfismo e Factory, foi evitada a criação de abstrações sem necessidade.
O objetivo foi manter o projeto simples, legível e fácil de explicar, utilizando abstrações somente onde havia uma responsabilidade compartilhada ou repetição concreta.
Boas práticas aplicadas
Durante o desenvolvimento foram considerados:
- nomes de classes e componentes relacionados às suas responsabilidades;
- separação entre interface, serviço, modelo e validação;
- reutilização de código;
- redução de duplicação;
- métodos pequenos e objetivos;
- encapsulamento de responsabilidades;
- herança para comportamentos realmente compartilhados;
- polimorfismo para comportamentos especializados;
- tratamento de exceções na integração externa;
- liberação de formulários utilizando
try...finally; - validação antes do envio de dados para serviços externos;
- padronização visual entre formulários;
- versionamento incremental com Git.
Dificuldades encontradas
Uma das principais dificuldades durante o desenvolvimento esteve relacionada à herança visual do Lazarus.
Algumas propriedades de componentes herdados ficaram registradas nos formulários descendentes, fazendo com que alterações realizadas no formulário pai nem sempre fossem refletidas visualmente da forma esperada no Designer.
Para manter o comportamento compartilhado e evitar ajustes repetidos nos formulários filhos, parte do posicionamento comum foi centralizada no próprio comportamento do formulário base.
Também ocorreram situações em que alterações no Designer não eram imediatamente refletidas como esperado, exigindo revisão dos arquivos .lfm, reconstrução do projeto e reabertura da IDE.
Outra atenção necessária foi manter sincronizados os nomes das classes Pascal e as declarações existentes nos arquivos .lfm, principalmente durante a criação dos formulários herdados.
Essas situações ajudaram a compreender melhor a relação entre:
.pas
.lfm
classe do formulário
herança visual
Object InspectorUso de Inteligência Artificial
A inteligência artificial foi utilizada como ferramenta de apoio durante o desenvolvimento, principalmente para:
- planejamento das etapas;
- discussão de arquitetura;
- sugestões de nomenclatura;
- revisão de código;
- identificação de duplicações;
- apoio na implementação da integração HTTP;
- organização do versionamento;
- análise de erros apresentados pelo compilador.
As sugestões não foram aplicadas de forma automática.
Durante o desenvolvimento ocorreram situações em que soluções sugeridas precisaram ser revisadas porque não correspondiam ao comportamento real do Lazarus, especialmente em questões relacionadas ao Designer e à herança visual.
Nesses casos, o comportamento da aplicação e as mensagens do compilador foram utilizados para identificar a causa real do problema e ajustar a implementação.
A IA foi utilizada como ferramenta auxiliar, enquanto as decisões e validações finais foram realizadas com base no funcionamento do projeto.
Estrutura do projeto
De forma simplificada, o projeto contém:
RegistrationForms/
│
├── RegistrationForms.lpr
│
├── MainForm.pas
├── MainForm.lfm
│
├── RegistrationForm.pas
├── RegistrationForm.lfm
│
├── FormFactory.pas
├── ValidationUtils.pas
├── CepService.pas
├── AddressModel.pas
│
├── CustomerForm.pas
├── CustomerForm.lfm
│
├── FreelancerForm.pas
├── FreelancerForm.lfm
│
├── EmployeeForm.pas
├── EmployeeForm.lfm
│
├── SupplierForm.pas
├── SupplierForm.lfm
│
├── CompanyForm.pas
├── CompanyForm.lfm
│
├── ServiceProviderForm.pas
├── ServiceProviderForm.lfm
│
├── StudentForm.pas
├── StudentForm.lfm
│
├── InstructorForm.pas
├── InstructorForm.lfm
│
├── PartnerForm.pas
├── PartnerForm.lfm
│
├── SalesRepresentativeForm.pas
└── SalesRepresentativeForm.lfmArquivos gerados automaticamente durante compilação podem variar conforme a configuração do Lazarus e não fazem parte da lógica principal da aplicação.
Como executar
Pré-requisitos
- Lazarus IDE;
- Free Pascal Compiler;
- conexão com a internet para utilizar a consulta de CEP.
Execução
- Clone o repositório.
- Abra o arquivo do projeto
.lpino Lazarus. - Compile o projeto.
- Execute a aplicação.
- Escolha um dos tipos de cadastro na tela principal.
Para testar a integração com ViaCEP:
- Abra o cadastro de Cliente ou Fornecedor.
- Informe um CEP válido.
- Clique em
Buscar CEP. - Os dados de endereço serão preenchidos com a resposta da API.
Versionamento
O desenvolvimento foi dividido em etapas utilizando branches específicas para cada responsabilidade.
Foram utilizados commits incrementais e Pull Requests para integrar as funcionalidades à branch principal.
Esse fluxo permitiu manter as mudanças isoladas durante o desenvolvimento e registrar a evolução da aplicação.
Considerações finais
O projeto demonstra que herança de formulários pode ser utilizada não apenas para reutilizar componentes visuais, mas também para compartilhar comportamentos.
A partir de um formulário base, foram construídas 10 telas especializadas mantendo uma estrutura comum.
A aplicação também demonstra outros conceitos complementares, como:
- herança;
- sobrescrita de métodos;
- polimorfismo;
- Factory;
- reutilização;
- separação de responsabilidades;
- validações centralizadas;
- consumo de API REST;
- processamento JSON;
- tratamento de exceções.
A solução foi mantida propositalmente simples: cada abstração foi adicionada para resolver uma responsabilidade ou duplicação identificada durante o desenvolvimento, evitando complexidade desnecessária.

