Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Catalog · How the catalog works
Anything · Lazarus or Delphi
Random projectRandom project

lazarus-calculator

A desktop calculator developed with Lazarus and Object Pascal.

View on GitHub

Member rating

No ratings yet.

Sign in to rate this project.

README

Calculadora

Calculadora desktop desenvolvida em Object Pascal utilizando a IDE Lazarus.

O projeto foi desenvolvido como parte de um desafio de programação cujo objetivo é implementar uma calculadora funcional capaz de realizar as quatro operações aritméticas fundamentais, aplicando também boas práticas de desenvolvimento de software, como desenvolvimento incremental, separação de responsabilidades, validação, versionamento e organização do código.

Funcionalidades

A calculadora possui suporte a:

  • Entrada numérica de 0 a 9;
  • Adição;
  • Subtração;
  • Multiplicação;
  • Divisão;
  • Execução do cálculo;
  • Limpeza e reinicialização da calculadora;
  • Validação de divisão por zero;
  • Exibição da expressão aritmética;
  • Substituição de operadores consecutivos;
  • Controle de habilitação do botão de resultado.

Tecnologias utilizadas

  • Object Pascal;
  • Lazarus IDE;
  • Free Pascal Compiler (FPC);
  • Git;
  • GitHub.

Arquitetura e decisões de projeto

Embora a aplicação possua um escopo relativamente pequeno, sua estrutura foi desenvolvida considerando organização, manutenibilidade e possibilidade de evolução futura.

Separação de responsabilidades

Durante a implementação inicial, as operações aritméticas eram realizadas diretamente pelo formulário principal da aplicação.

Com a evolução do projeto, a lógica responsável pelos cálculos foi extraída para uma unit dedicada chamada CalculationEngine.

A estrutura resultante separa as principais responsabilidades da aplicação:

text
CalculatorForm
    |
    |-- Interface com o usuário
    |-- Entrada de dados
    |-- Estado da calculadora
    |-- Validações
    |-- Exibição da expressão
    |
    v
CalculationEngine
    |
    |-- Execução das operações aritméticas

O CalculatorForm permanece responsável pela interação com o usuário e pelo estado da interface, enquanto o CalculationEngine é responsável pela execução das operações matemáticas.

Essa decisão reduz o acoplamento entre a interface gráfica e a lógica de cálculo, além de facilitar futuras alterações e extensões.

Reutilização de eventos

Em vez de criar um evento individual para cada botão numérico, os botões de 0 a 9 compartilham o mesmo manipulador:

pascal
NumberButtonClick

O componente responsável pelo evento é identificado através do parâmetro Sender.

A mesma estratégia foi aplicada aos operadores aritméticos através de:

pascal
OperatorButtonClick

Dessa forma, evita-se a criação de diversos métodos com comportamentos praticamente idênticos.

Essa decisão reduz duplicação de código e segue o princípio DRY (Don't Repeat Yourself).

Gerenciamento do estado da calculadora

A calculadora precisa manter informações entre diferentes interações do usuário.

Para isso, foram utilizados campos privados como:

pascal
FFirstOperand
FOperator
FStartNewInput

Esses valores permitem identificar diferentes estados da aplicação, como:

  • Entrada do primeiro operando;
  • Seleção do operador;
  • Entrada do segundo operando;
  • Exibição do resultado;
  • Início de uma nova operação.

O prefixo F foi utilizado como convenção para representar campos pertencentes à classe do formulário.

Separação entre expressão e resultado

Inicialmente, apenas o valor atual era apresentado no display.

Durante o desenvolvimento foi identificada a necessidade de fornecer ao usuário um retorno visual da operação selecionada.

Para isso, foi adicionado um componente separado para exibir a expressão aritmética.

Por exemplo:

text
1 + 1 =
      2

A expressão e o valor numérico foram mantidos em componentes diferentes.

Essa decisão evita misturar textos como:

text
1 + 1 =

com valores que posteriormente precisam ser convertidos para tipos numéricos através de funções como StrToFloat.

Além disso, melhora a experiência do usuário ao manter visível o contexto da operação realizada.

Após o resultado, a expressão permanece visível até que uma nova entrada seja iniciada ou a calculadora seja reinicializada.

Validações

Foram adicionadas validações para impedir estados inválidos durante a utilização da calculadora.

Divisão por zero

Antes de executar uma divisão, o segundo operando é verificado.

Caso seja igual a zero, o cálculo não é enviado para execução e o display apresenta:

text
Error

A mensagem foi mantida curta devido ao espaço disponível no display da calculadora.

Após o erro, o estado da aplicação é preparado para permitir uma nova entrada de forma segura.

Botão de resultado

O botão = inicia desabilitado.

Ele é habilitado somente após a seleção de uma operação e volta a ser desabilitado depois da execução do cálculo ou de uma operação inválida.

Isso evita a execução do cálculo quando não existe uma operação válida preparada.

Operadores consecutivos

Também foi considerado o cenário em que o usuário seleciona mais de um operador antes de informar o segundo operando.

Por exemplo:

text
10 + *

Nesse caso, o último operador selecionado substitui o anterior.

Assim:

text
10 + * 5 =

é interpretado como:

text
10 * 5 =

Esse comportamento evita gerar um erro desnecessário e permite que o usuário corrija o operador antes de continuar a operação.

Operação de limpeza

O botão C não foi implementado apenas como uma limpeza visual do display.

Ele realiza uma reinicialização completa do estado da calculadora.

São restaurados:

  • Valor apresentado no display;
  • Expressão apresentada ao usuário;
  • Primeiro operando armazenado;
  • Operador selecionado;
  • Estado de entrada numérica;
  • Estado do botão de resultado.

Essa abordagem impede que informações de uma operação anterior permaneçam armazenadas internamente e interfiram em cálculos posteriores.

Processo de desenvolvimento

O projeto foi desenvolvido de forma incremental.

As funcionalidades foram separadas em diferentes etapas:

text
Inicialização do projeto
        ↓
Interface gráfica
        ↓
Entrada numérica
        ↓
Seleção dos operadores
        ↓
Execução dos cálculos
        ↓
Validação das operações
        ↓
Reinicialização da calculadora
        ↓
Refatoração do mecanismo de cálculo
        ↓
Exibição da expressão

Cada alteração relevante foi desenvolvida em uma branch específica e posteriormente integrada à main através de Pull Requests.

Essa estratégia permitiu desenvolver e validar cada comportamento de maneira isolada, além de manter um histórico claro da evolução da aplicação.

Estratégia de versionamento

Foi utilizado Git para controle de versão e GitHub para armazenamento do repositório e integração das alterações.

Os commits seguem uma convenção baseada em Conventional Commits, permitindo identificar facilmente a finalidade de cada alteração.

Alguns dos commits realizados durante o desenvolvimento foram:

text
chore: initialize Lazarus calculator project
feat: create calculator user interface
feat: implement numeric input handling
feat: implement arithmetic operator selection
feat: implement calculation execution
feat: add calculation validation
feat: implement calculator reset
refactor: extract calculation engine
feat: add arithmetic expression display

Os principais prefixos utilizados foram:

  • feat: implementação de uma nova funcionalidade;
  • refactor: alteração estrutural sem modificar o comportamento esperado;
  • chore: configuração ou manutenção do projeto;
  • docs: alterações relacionadas à documentação.

Dificuldades encontradas durante o desenvolvimento

Adaptação ao Lazarus

Uma das principais dificuldades foi a utilização da IDE Lazarus e do Object Pascal durante o desenvolvimento do projeto.

Foi necessário compreender conceitos e recursos como:

  • Forms;
  • Object Inspector;
  • Componentes visuais;
  • Propriedades dos componentes;
  • Eventos;
  • Parâmetro Sender;
  • Hierarquia entre componentes;
  • Units;
  • Arquivos .pas e .lfm.

Durante o desenvolvimento, também foram encontrados comportamentos relacionados às propriedades dos componentes.

Por exemplo, quando um componente pai estava com Enabled = False, seus componentes filhos também deixavam de aceitar interação.

A identificação desse comportamento foi importante para compreender melhor a hierarquia dos componentes da interface.

Gerenciamento de eventos

Durante a implementação da entrada numérica, inicialmente foram criados eventos individuais para alguns botões.

Posteriormente, esses eventos foram substituídos por um único manipulador compartilhado.

Após essa alteração, o Lazarus identificou referências antigas no arquivo visual do formulário para métodos que já não existiam.

Foi necessário remover essas referências e associar os botões ao novo evento compartilhado NumberButtonClick.

Essa situação ajudou a compreender a relação entre o código Pascal e as configurações armazenadas pelo Form Designer.

Palavras reservadas do Object Pascal

Durante a extração do CalculationEngine, inicialmente foi utilizado o identificador:

pascal
Operator

como parâmetro da função responsável pelo cálculo.

Durante a compilação, o Free Pascal apresentou um erro porque operator é uma palavra reservada da linguagem.

O identificador foi substituído por:

pascal
Operation

Esse problema reforçou a necessidade de considerar características específicas da linguagem mesmo quando um nome parece adequado semanticamente.

Gerenciamento de estado

Outra dificuldade importante foi manter o estado interno da calculadora sincronizado com aquilo que era apresentado ao usuário.

Por exemplo, alterar o display para 0 ao pressionar C não seria suficiente caso o primeiro operando e o operador anterior continuassem armazenados.

O mesmo cuidado foi necessário após:

  • Execução de um cálculo;
  • Divisão por zero;
  • Seleção consecutiva de operadores;
  • Exibição de um resultado;
  • Início de uma nova entrada.

Essas situações levaram à implementação explícita do gerenciamento de estado através dos campos privados do formulário.

Utilização de Inteligência Artificial

A Inteligência Artificial foi utilizada como ferramenta de apoio durante o desenvolvimento do projeto.

A IA auxiliou principalmente em:

  • Compreensão inicial da IDE Lazarus;
  • Explicação de conceitos do Object Pascal;
  • Sugestões de organização do projeto;
  • Convenções de nomenclatura;
  • Discussão sobre reutilização de eventos;
  • Separação de responsabilidades;
  • Identificação de cenários para testes;
  • Revisão das decisões de implementação.

As sugestões fornecidas pela IA não foram consideradas automaticamente corretas.

Durante o desenvolvimento, algumas orientações precisaram ser corrigidas ou adaptadas após compilação e testes no ambiente real.

Um exemplo ocorreu durante a criação do CalculationEngine, quando o identificador Operator foi inicialmente utilizado como nome de parâmetro. O compilador identificou que se tratava de uma palavra reservada do Object Pascal, exigindo a alteração para Operation.

Também foram necessárias adaptações relacionadas ao gerenciamento de eventos pelo Form Designer e às propriedades dos componentes da interface.

Por esse motivo, a utilização da IA seguiu um processo de validação:

text
Sugestão da IA
      ↓
Implementação
      ↓
Compilação
      ↓
Teste manual
      ↓
Análise do comportamento
      ↓
Correção ou aprovação

Dessa forma, a IA foi utilizada como ferramenta de suporte ao desenvolvimento, e não como substituta da análise, compreensão e validação do código.

Critérios considerados durante o desenvolvimento

Durante a implementação foram considerados principalmente:

  • Legibilidade do código;
  • Nomes claros para componentes e métodos;
  • Redução de código duplicado;
  • Separação entre interface e lógica de cálculo;
  • Controle explícito do estado da aplicação;
  • Tratamento de operações inválidas;
  • Retorno visual das ações realizadas pelo usuário;
  • Desenvolvimento incremental;
  • Histórico de versionamento compreensível;
  • Facilidade de manutenção;
  • Possibilidade de expansão futura;
  • Complexidade proporcional ao escopo do projeto.

Uma decisão importante foi evitar adicionar funcionalidades apenas para tornar artificialmente o projeto mais complexo.

A estrutura foi preparada para permitir evolução futura, mas o desenvolvimento foi mantido dentro do escopo necessário para atender ao desafio.

Possíveis evoluções

Embora não façam parte do escopo atual, a estrutura permite futuramente adicionar funcionalidades como:

  • Entrada de números decimais;
  • Operações com porcentagem;
  • Potenciação;
  • Raiz quadrada;
  • Entrada através do teclado;
  • Histórico de cálculos;
  • Operações de memória;
  • Novas operações matemáticas;
  • Testes automatizados.

Essas funcionalidades podem ser adicionadas incrementalmente sem necessidade de reconstruir completamente a aplicação.

Testes

Os testes foram realizados manualmente durante cada etapa do desenvolvimento.

Entre os cenários utilizados estão:

text
10 + 5 = 15
10 - 5 = 5
10 * 5 = 50
10 / 5 = 2
10 / 0 = Error

Também foram verificados:

  • Entrada de números com múltiplos dígitos;
  • Tratamento de zeros à esquerda;
  • Substituição de operadores consecutivos;
  • Limpeza durante uma operação incompleta;
  • Limpeza após um cálculo;
  • Limpeza após um erro;
  • Início de uma nova operação após um resultado;
  • Exibição da expressão aritmética;
  • Habilitação e desabilitação do botão =;
  • Preservação do comportamento após a refatoração do CalculationEngine.

Após a separação da lógica matemática para o CalculationEngine, os principais cenários foram executados novamente como testes de regressão para garantir que a refatoração não alterasse o comportamento existente.

Conclusão

O desenvolvimento demonstrou que mesmo uma aplicação de pequeno porte pode se beneficiar da aplicação de boas práticas de Engenharia de Software.

Em vez de concentrar toda a implementação diretamente nos eventos individuais dos botões, o projeto foi desenvolvido incrementalmente, utilizando reutilização de eventos, gerenciamento explícito de estado, validações, separação de responsabilidades, versionamento e documentação das decisões tomadas.

A aplicação final atende aos requisitos propostos para a calculadora e mantém uma estrutura organizada, compreensível e preparada para possíveis evoluções futuras.