VBA - Artigo 034 - Deixando o código autodocumentado


Deixando o código autodocumentado

Muita gente tem problemas quando precisa pegar um código feito por outra pessoa, gastando um tempo considerável para entender o que foi feito. Normalmente o código legado não tem nenhuma documentação além de alguns comentários aqui ou ali. Há também pessoas que têm problema para entender o próprio código ao olhá-lo depois de alguns meses ou mesmo algumas poucas semanas. Muito desse problema se dá por causa do não uso de boas práticas de programação. Veremos as principais aqui neste artigo. Alguns dos meus próprios códigos em artigos anteriores não seguem algumas dessas recomendações, pois o foco sempre foi deixar o mais didático possível, abstraindo os leitores de detalhes que não sejam relevantes no momento. Algumas dicas já foram mencionadas em alguns dos artigos anteriores, encare-as como uma revisão ou lembrete.

O primeiro ponto é escolher nomes adequados às variáveis que serão utilizadas. Como os leitores devem ter percebido, nos meus artigos sempre procuro colocar nomes que definam bem o propósito da variável. Recomenda-se que o tamanho deve ter no mínimo cinco caracteres se for composto de apenas uma palavra. Se houver mais de duas palavras, pode-se utilizar abreviação, mas é preciso que esta seja consistente. Por exemplo, não adianta abreviar produto para Prod em algumas variáveis, Pro em outras e ainda Prd em outras. É preciso estabelecer um padrão e se ater a ele. Assim, quem ler o programa conhecerá o padrão das abreviações em pouco tempo.

Mesmo em iterações simples, onde muitos programadores, mesmo experientes, usam variáveis com apenas um caractere, sempre usando a ordem i, j e k. Essa prática atrapalha muito quem estiver lendo o código, pois fica difícil saber qual o propósito do i, do j e do k. É preciso gastar um tempo considerável para entender para que serve cada uma e qual o seu uso. Mesmo quando tem apenas uma estrutura de repetição, é uma boa prática especificar a função da iteração. For Linha, For Cliente e For Produto são mais fáceis de entender do que For i, já fica bem evidente o propósito do loop. Caso não saiba, programadores gastam mais tempo lendo código do que programando. Portanto, gastar alguns caracteres a mais no nome das variáveis facilita muito no entendimento dos leitores. Lembre-se que você mesmo pode não entender o que você escreveu há alguns dias atrás por causa da economia de digitação.

Outro ponto em variáveis é a forma de escrever as palavras individuais do nome da variável. Há duas formas bastante populares e utilizadas: PascalCase e CamelCase. A primeira diz para colocar a inicial de cada palavra em maiúscula e o restante em minúsculas, a segunda diz a mesma coisa, exceto para a primeira palavra, que deve iniciar em minúscula. Exemplo: CodProduto é a forma em PascalCase e codProduto em CamelCase. Não há uma forma certa ou errada, mas o ideal é decidir por uma e usá-la consistentemente no código. O formato CamelCase é mais utilizada quando o programador coloca o tipo da variável ou objeto no nome, como int (inteiro), str (string), frm (formulário), btn (botão), cls (classe) etc. Com relação a constantes, o consenso entre a maioria dos programadores é utilizá-las em maiúsculas, como QTD_MAX, NUM_LINHAS etc. Perceba que neste caso é melhor colocar uma sublinha (underline) para separar as palavras e facilitar o entendimento.

Uma outra dica interessante é evitar o uso de chamados "números mágicos" ou "textos mágicos", que seriam números e textos colocados de forma fixa no código. O ideal é substituí-los por constantes. Por exemplo, você tem uma planilha com orçamento que será impressa, há um limite de 20 produtos por questões de layout e no seu código há algumas validações que usam esse valor. Mas suponha que um dia você refaça o layout do orçamento, alguns ajustes aqui ou ali e agora tem 25 linhas à disposição para colocar os produtos. O que será preciso fazer no código? Procurar cada "20" e trocar por "25", checando antes se esse número realmente tem relação com o layout do orçamento. Ou seja, será trabalhoso demais, sujeitando-se a erros. É melhor colocar uma constante como NUM_LINHAS com o valor da quantidade de linhas. Assim, se houver alguma alteração, basta alterar a linha com o valor dessa constante. Reduz muito o tempo da manutenção do código e mitiga as chances de esquecer de alterar algum ou alterar algum errado.

Da mesma forma que os números, há textos que podem ser substituídos por constantes, de forma a deixar mais explícito o que significa o valor. Um "F" no código pode indicar uma validação de pessoa física ou sexo feminino, por exemplo. Criar as constantes e validar PES_FISICA ou FEMININO deixa mais explícito o que está sendo validado no momento, além de evitar erros de colocar minúsculas quando deveriam ser maiúsculas e vice-versa.

Passando para os procedimentos, é fundamental que sejam curtos e tenham um único propósito. É comum ouvir dizer que se deve dividir rotinas para evitar repetição de código. Isso é verdade, mas também deve-se dividir para reduzir a complexidade do código. As boas práticas recomendam que uma sub-rotina ou função não deve ter mais do que uma tela de código de altura, pois mais do que isto indica que há muita coisa sendo feita e aumenta a complexidade do procedimento. Além disso, analisar um procedimento sem precisar usar a barra de rolagem ajuda a manter o foco – só o fato de subir ou descer o código é uma distração.

Deve-se considerar a possibilidade de extrair alguns trechos em novas sub-rotinas de forma a isolar alguns procedimentos. Isto é particularmente útil em instruções If e Select Case quando o código em cada opção tem muitas linhas. É melhor criar uma sub-rotina e colocar uma chamada no lugar de várias linhas. Veja o exemplo abaixo como fica mais fácil entender o que é feito:

Select Case TipoPessoa
    Case PES_FISICA
        ProcessarPesFisica
    Case PES_JURIDICA
        ProcessarPesJuridica
    Case Other
        GerarErro "Tipo de pessoa inválida"
End Select

Fica mais fácil entender o código desta forma, pois as linhas referentes ao processamento de cada tipo de pessoa foram isoladas em procedimentos separados, o leitor saberá que algumas partes foram separadas. Se houver necessidade de investigar alguma dessas partes, basta ir até aquela parte do programa. Imagine como ficaria o código se cada When viesse acompanhado de pelo menos 10 linhas; o procedimento teria muito mais linhas e certamente ocuparia mais de uma tela.

Da mesma forma que em variáveis, a nomenclatura dos procedimentos é importante. É essencial que ele descreva de forma sucinta tudo que o procedimento faz. Assim, se uma sub-rotina consiste e processa um registro, o nome deverá ser ConsistirEProcessarRegistro, o que indica que está fazendo muita coisa. O ideal é separar em duas separadas, uma que consiste e outra que processe o registro. Neste caso, a consistência pode ser substituída por uma função que retorna Verdadeiro ou Falso, indicando se a consistência foi bem-sucedida ou não:

If RegistroConsistente(Registro) Then
    ProcessarRegistro Registro
Else
    GerarErro "Registro inconsistente"
End If

Perceba que neste exemplo tanto a função quanto a sub-rotina usa um parâmetro Registro. Habitue-se a usar parâmetros, pois evita ter de declarar variáveis como públicas para que possam ser acessadas em vários procedimentos. No VBA há como passar valores que podem ser alterados ou não, veja este artigo para mais detalhes.

Outro fator importante na nomenclatura dos procedimentos é a forma, normalmente composta de um verbo e um objeto. Eu costumo usar os verbos no infinitivo, há quem prefira usar na terceira pessoa, vai da preferência de cada um. Com relação a funções, a recomendação é usar o resultado dela como nome. Por exemplo, Sin e Cos são funções que retornam o seno e o cosseno. No caso de valores booleanos, o ideal é colocar o nome do caso Verdadeiro, como fiz no exemplo acima: RegistroConsistente. Também pode-se colocar ÉConsistente (da mesma forma que algumas funções do Excel começam com É), mas em certos casos como validações ou consistências é melhor especificar o que está sendo validado ou consistido para que a função não fique ambígua.

Lembre-se que um código legível permite que você leia de cima para baixo e entenda tudo, sem interrupções. As sub-rotinas e funções com nome de entendimento fácil permitem que você saiba o que é feito naquele momento do programa. Desta forma, a instrução GoTo deve ser evitada ao máximo. A única condição que utilizo é em tratamento de erros. Se seu programa tem GoTo para determinados pontos e não é para tratamento de erro, é bem provável que você precise reescrever o código e criar alguns procedimentos para melhorar o fluxo do programa e evitar esses GoTo.

Estrutura de decisão é outro ponto que merece atenção. Sempre coloque o caso normal após o If, nunca após o Else. Isto melhora tanto a legibilidade da instrução quanto o desempenho em si, pois os casos mais comuns devem ser tratados antes dos demais. No código acima, por exemplo, o caso normal é o registro ser consistente e o anormal é o erro, por isso valida-se o caso positivo. Em caso de instruções If aninhadas, procure sempre colocar o mais provável na frente e seguindo a sequência de importância.

No caso de instruções Select Case é tentador manter os itens em ordem alfabética ou numérica. Porém, nem sempre o caso mais comum está na frente. Por exemplo, muitas instituições financeiras trabalham com arquivos gigantescos e trazem como primeiro caractere o tipo do registro. Normalmente 1 indica cabeçalho (ou header, termo mais usado), 9 é o rodapé (ou trailer) e outros números são detalhes. Há arquivos com mais de um tipo de registro detalhe, sendo que cada um tem um número diferente: 2, 3 etc. O caso mais comum certamente é o detalhe e, se tiver mais de um, é preciso verificar o mais comum. Assim, neste caso você terá seu código validando primeiro o 2, depois o 3, depois o 1 e por fim o 9, ordenando pelo tipo mais provável na frente e o menos provável no fim para manter um melhor desempenho. Se você deixar a ordem como está, é capaz de alguém que for fazer alguma manutenção resolver deixar em ordem numérica, prejudicando assim o desempenho. Há dois caminhos: validar com constantes ou com enumerações. Lembre-se que as enumerações só aceitam números inteiros, o que é o caso aqui. Vejamos como fica a declaração usando nomes que indiquem o significado dos números:

Enum enumRegistro
    Reg_Cabecalho = 1
    Reg_Cliente = 2
    Reg_Parcela = 3
    Reg_Rodape = 9
End Enum

Lembre-se que a enumeração não precisa ter todos os números em sequência, pode-se escolher os valores de acordo com a necessidade. Agora vejamos a estrutura de decisão:

Select Case Registro.Tipo
    Case Reg_Cliente
        ProcessarCliente Registro
    Case Reg_Parcela
        ProcessarParcela Registro
    Case Reg_Cabecalho
        ProcessarCabecalho Registro
    Case Reg_Rodape
        ProcessarRodape Registro
    Case other
        GerarErro "Tipo de registro inválido"
End Select

Com a validação acima, fica mais fácil perceber qual tipo de registro está sendo validado. Se você achar que alguém ainda pode ficar tentado a colocar em ordem alfabética, talvez seja melhor colocar um comentário dizendo que a ordem atual é importante e que deve ser mantida. De qualquer forma, Case Reg_Cliente é muito mais fácil de entender que Case 2.

Estruturas de repetição também merecem alguns pontos de atenção. O primeiro é decidir qual a melhor forma: usar For, While, Until, validação no início ou no fim? O For deve ser usado quando se sabe a quantidade de repetições que o laço terá. E, como citado anteriormente, se possível substituir os "números mágicos" por constantes ou variáveis/propriedades que contenham a quantidade de iterações, como Sheets.Count, por exemplo.

Quando a quantidade de iterações não é conhecida, é preciso decidir por um entre While e Until. A escolha depende do tipo da condição que será usada: While significa enquanto e Until significa até. Se a repetição deve acontecer enquanto uma condição for verdadeira, use While, se a repetição for até que uma condição seja verdadeira, use Until. O VBA permite as duas formas justamente para não precisar usar a chamada lógica negativa, ou seja, ter algo na validação que indique o oposto do que quer que valide. Lógica negativa é um grande problema em qualquer linguagem, pois exigem que quem esteja lendo o código faça alguns exercícios mentais até compreender o que realmente está sendo validado. Por fim, para decidir entre fazer a validação no começo e no fim, basta verificar se o conteúdo da estrutura de repetição precisa executar uma vez antes de verificar a condição. Se for o caso, valide no fim, senão valide no início.

Uma outra coisa bem legal que o VBA nos fornece é a possibilidade de agrupar dados em uma estrutura de dados. Uma variável chamada Nome fica muito vaga no programa, mas Produto.Nome, Cliente.Nome e Fornecedor.Nome deixam mais claro o propósito de cada uma. Se você possui variáveis relacionadas, considere criar um objeto Type, é possível até criar um array deles, permitindo que você trabalhe com um bom volume de dados em memória ao invés de precisar guardá-las na planilha para processar depois. Caso não conheça essas estruturas de dados, recomendo que leia os dois artigos, vale muito a pena usar o Type.

Por fim, tem as classes. Quando bem projetadas ajudam muito a reduzir a complexidade do código. O melhor é que uma classe pode ser facilmente exportada e reutilizada em outros programas. Por exemplo, eu tenho uma classe para um arquivo de log. Desta forma acrescento o arquivo da classe na minha planilha e só preciso instanciar um objeto para usar seus métodos. Toda a complexidade de verificar se o arquivo existe, saber se precisa criar um arquivo ou não, a parte de abrir e fechar o arquivo, a forma de gravar o texto no arquivo, tudo está encapsulado nessa classe. Não preciso nem saber mais como ela funciona, só preciso conhecer os métodos que preciso usar e pronto, funciona como se fosse um objeto nativo do Excel. Tem uma série de três artigos onde expliquei como usar classe com formulários. Lembre-se que a classe deve ter um propósito bem definido e ser especialista naquilo, como está bem demonstrado nos artigos. Uma classe que faz mais de uma coisa não é uma classe bem projetada, é melhor dividir em classes menores. Pode parecer mais trabalhoso, mas torna o entendimento e as manutenções mais fáceis.

Se você seguir todas as dicas citadas aqui, a necessidade de escrever comentários para explicar determinadas partes do programa será bem reduzida. Em alguns procedimentos talvez não precise ter nenhum, nem para descrever o que a função ou sub-rotina faz caso o nome seja bem autoexplicativo. Um código autodocumentado é excelente tanto para quem vai fazer a manutenção quanto para a reputação de quem o fez.

Muitas dessas dicas vieram com a prática e troca de conhecimento com outros programadores. Elas valem para praticamente qualquer linguagem de programação, salvo limitações de algumas. Outras dicas vieram de leitura de livros. Recomendo fortemente Code Complete, de Steve McConnell, que é popularmente conhecido como "a bíblia do programador". A segunda edição é de 2005, mas todo o conteúdo ainda é válido para qualquer linguagem, mesmo as mais modernas.

Para dúvidas sobre o artigo, comentários ou sugestões, utilize os comentários abaixo. Até o próximo artigo!

Pedro Martins

Pós-graduando em Business Intelligence e Big Data pela Faculdade Impacta de Tecnologia. Formado em Tecnologia em Eletrônica Digital com Ênfase em Microprocessadores


Catálogo de aulas (NOVIDADE)

Criei um catálogo de aulas para ajudar você em seus estudos. Acesse clicando na imagem abaixo ou clique aqui.


Postagem Anterior Próxima Postagem