O controverso comando GO TO
Combatido pelas patrulhas das melhores práticas e banido de linguagens mais novas como Java e Phyton, mesmo profissionais de TI com pouca experiência em programação COBOL acreditam piamente que o GO TO é um mal que precisa ser combatido. Mas existe alguma situação em que seu uso possa ser justificado?
O início da controvérsia
Já nas primeiras discussões do comitê encarregado de desenvolver o ALGOL, no final dos anos 1950, havia discussões sobre a necessidade ou não de se implementar nesta linguagem um comando que permitisse um desvio de controle incondicional.
Essa polêmica ganhou visibilidade no meio acadêmico em 1968, quando o cientista holandês Edsger Dijkstra publicou um artigo intitulado “A Case Against GO TO Statement”, que depois Niklaus Wirth – editor da revista Communications of Association for Computing Machinery – rebatizou de “GO TO statement considered harmful”.
Neste artigo, Dijkstra afirma que esse comando deveria ser abolido de todas as linguagens de programação de alto nível, uma vez que prejudicava a análise e a compreensão do código fonte e poderia ser evitado em todas as situações.
A transcrição do artigo original pode ser lida aqui.
A afirmação de Dijkstra, claro, causou reações de apoio e críticas furiosas, com muitos defendendo que o problema era o uso indiscriminado do comando, mas que a própria legibilidade do código dependia do uso correto do GO TO, em determinadas situações.
Só o GO TO?
Algumas vezes tenho a impressão de que determinadas palavras ganham mais importância do que os conceitos que tentam expressar.
Por exemplo, houve um tempo em que eu fazia propostas de projetos para manutenção de sistemas. Havia um dogma na empresa onde eu trabalhava que não podia ser questionado, sob pena de excomunhão: nenhuma frase da proposta poderia conter a expressão “et cetera” ou “etc.”.
A alegação era de que o cliente poderia interpretar este “etc.” como qualquer coisa, o que, na prática, deixaria o projeto com o escopo aberto.
Eu sempre tentei argumentar que não eram os caracteres “e”, “t”, “c”, “.” que ameaçavam o projeto, e sim o uso irresponsável dessa expressão sem considerar o contexto. Uma frase do tipo “o sistema permitirá que faturas sejam incluídas, alteradas, excluídas, consultadas etc.” certamente levaria a enormes discussões com o cliente no futuro. Mas algo como “o usuário informará o código internacional da moeda, previamente cadastrado (USD para Dólar, GBP para Libra etc.)” não teria nenhum efeito devastador.
Com o comando GO TO acredito que aconteça uma coisa parecida. É a expressão “vá para” que deve ser banida dos programas? O comando break e continue dentro de um loop não são também uma forma de instruir o programa para “ir para” algum lugar? A famosa combinação “try / catch / finally” do Java não têm também um GO TO implícito em todas as instruções?
Um loop gigante escrito em C cheio de breaks e continues ou um try a 480 quilômetros do seu catch em Java, comprometem a legibilidade e as futuras manutenções de um programa. E o mesmo acontece com um GO TO utilizado indiscriminadamente.
O GO TO no COBOL
O COBOL possui comandos para loops e cases, como diversas outras linguagens. O uso do GO TO, por esse motivo, seria tecnicamente dispensável. Mas existem situações em que seu uso facilita a leitura do programa e sua manutenção no futuro.
Vamos analisar um caso prático. O trecho abaixo é um parágrafo de um programa real, que tem por objetivo validar campos preenchidos pelo usuário:
/----------------------------------------------------------------* * OBJETIVO : VALIDAR INFORMACOES FORNECIDAS PELO USUARIO *----------------------------------------------------------------* R12-VALIDA-TELA. MOVE "SIM" TO WT-FL-ERRO IF AIS0108A-DE-REFERENCIA = SPACES MOVE "Nome Nao Preenchido." TO AIS0108A-TX-MSG MOVE 04 TO WT-NR-CAMPO GO TO SR12 END-IF IF AIS0108A-NM-LOGRADOURO = SPACES MOVE "Endereco Nao Preenchido." TO AIS0108A-TX-MSG MOVE 05 TO WT-NR-CAMPO GO TO SR12 END-IF IF AIS0108A-NM-BAIRRO = SPACES MOVE "Bairro Nao Preenchido." TO AIS0108A-TX-MSG MOVE 06 TO WT-NR-CAMPO GO TO SR12 END-IF IF AIS0108A-NM-CIDADE = SPACES MOVE "Cidade Nao Preenchida." TO AIS0108A-TX-MSG MOVE 07 TO WT-NR-CAMPO GO TO SR12 END-IF IF AIS0108A-CD-UF = SPACES MOVE "Estado Nao Preenchido." TO AIS0108A-TX-MSG MOVE 09 TO WT-NR-CAMPO GO TO SR12 END-IF IF AIS0108A-CD-UF NOT = SPACES MOVE AIS0108A-CD-UF TO XXVEST-CD-ESTADO CALL "XXVEST" USING XXCOMUM-PARAMS, XXVEST-PARAMS CANCEL "XXVEST" IF XXVEST-RC-CODE NOT = ZEROS MOVE "Estado Invalido." TO AIS0108A-TX-MSG MOVE 09 TO WT-NR-CAMPO GO TO SR12 END-IF END-IF IF AIS0108A-CD-CEP1 = ZEROS AND AIS0108A-CD-CEP2 NOT = ZEROS MOVE "CEP Invalido." TO AIS0108A-TX-MSG MOVE 10 TO WT-NR-CAMPO GO TO SR12 END-IF IF AIS0108A-NM-CONTATO = SPACES MOVE " Contato Não Informado." TO AIS0108A-TX-MSG MOVE 11 TO WT-NR-CAMPO GO TO SR12 END-IF IF AIS0108A-TX-EMAIL = SPACES MOVE "E-Mail Não Informado." TO AIS0108A-TX-MSG MOVE 12 TO WT-NR-CAMPO GO TO SR12 END-IF IF AIS0108A-TX-RAMO = SPACES MOVE "Ramo Principal Não Informado." TO AIS0108A-TX-MSG MOVE 13 TO WT-NR-CAMPO GO TO SR12 END-IF MOVE "NAO" TO WT-FL-ERRO. SR12. EXIT.
Neste trecho temos dez comandos GO TO que desviam o fluxo para o final do parágrafo (SR12[1]) quando algum erro é identificado.
Agora vejamos como ficaria esse parágrafo numa instalação que simplesmente proibisse a utilização do GO TO, sem exceções.
/----------------------------------------------------------------* * OBJETIVO : VALIDAR INFORMACOES FORNECIDAS PELO USUARIO *----------------------------------------------------------------* R12-VALIDA-TELA. MOVE "SIM" TO WT-FL-ERRO IF AIS0108A-NM-CLIENTE = SPACES MOVE "Nome Nao Preenchido." TO AIS0108A-TX-MSG MOVE 04 TO WT-NR-CAMPO ELSE IF AIS0108A-NM-LOGRADOURO = SPACES MOVE "Endereco Nao Preenchido." TO AIS0108A-TX-MSG MOVE 05 TO WT-NR-CAMPO ELSE IF AIS0108A-NM-BAIRRO = SPACES MOVE "Bairro Nao Preenchido." TO AIS0108A-TX-MSG MOVE 06 TO WT-NR-CAMPO ELSE IF AIS0108A-NM-CIDADE = SPACES MOVE "Cidade Nao Preenchida." TO AIS0108A-TX-MSG MOVE 07 TO WT-NR-CAMPO ELSE IF AIS0108A-CD-UF = SPACES MOVE "Estado Nao Preenchido." TO AIS0108A-TX-MSG MOVE 09 TO WT-NR-CAMPO ELSE IF AIS0108A-CD-UF NOT = SPACES MOVE AIS0108A-CD-UF TO XXVEST-CD-ESTADO CALL "XXVEST" USING XXCOMUM-PARAMS XXVEST-PARAMS CANCEL "XXVEST" IF XXVEST-RC-CODE NOT = ZEROS MOVE "Estado Invalido." TO AIS0108A-TX-MSG MOVE 09 TO WT-NR-CAMPO ELSE IF AIS0108A-CD-CEP1 = ZEROS AND AIS0108A-CD-CEP2 NOT = ZEROS MOVE "CEP Invalido." TO AIS0108A-TX-MSG MOVE 10 TO WT-NR-CAMPO ELSE IF AIS0108A-NM-CONTATO = SPACES MOVE "Contato Não Informado." TO AIS0108A-TX-MSG MOVE 11 TO WT-NR-CAMPO ELSE IF AIS0108A-TX-EMAIL = SPACES MOVE "E-Mail Não Informado." TO AIS0108A-TX-MSG MOVE 12 TO WT-NR-CAMPO ELSE IF AIS0108A-TX-ATIVIDADE = SPACES MOVE "Ramo Principal Não Informado." TO AIS0108A-TX-MSG MOVE 13 TO WT-NR-CAMPO ELSE MOVE “NAO” TO WT-FL-ERRO END-IF END-IF END-IF END-IF END-IF END-IF END-IF END-IF END-IF END-IF END-IF. SR12. EXIT.
Há quem prefira esse formato por ser mais “estruturado”. Eu, particularmente, acho que dificulta a manutenção do programa sem agregar nenhum outro valor.
Repare que tivemos que alterar a edentação de algumas linhas para não ultrapassar o limite da coluna 72. Poderíamos ter reduzido os nomes das variáveis ou as mensagens exibidas para o usuário mas, convenhamos, essa teria sido uma solução pobre.
E esse nem é o maior problema. Imagine, por exemplo, que no futuro precisemos inserir três novos campos nessa tela, entre os NM-CLIENTE e NM-LOGRADOURO. Além de ter que refazer toda a edentação das linhas subsequentes, fatalmente ficaríamos ainda mais “imprensados” na coluna 72, tendo que desalinhar os comandos ou quebrá-los em várias linhas. E tudo isso para que? Para evitar o uso de um comando rápido (um GO TO gera apenas uma instrução jump em código de máquina) que não afetaria em nada a legibilidade do programa.
Conclusão
Todo padrão de codificação deve se preocupar com:
- Performance
- Segurança
- Clareza
- Facilidade em manutenções futuras
Qualquer regra que não tenha uma das quatro justificativas acima deveria ser descartada. Infelizmente, não é incomum encontrar instalações que estabelecem restrições irracionais e injustificáveis.
Qualquer um que já tenha participado de reuniões de trabalho para definição de padrões de codificação sabe que normalmente elas terminam com amizades desfeitas, ódio, rancor e ranger de dentes.
Regras de programação estão mais associadas a preferências pessoais do que a argumentos técnicos. Programar, como vivo repetindo, é mais parecido com artesanato do que com engenharia.
E é bom justamente por isso.
[1] Para ser tecnicamente correto, SR12 não é o “final” do parágrafo R12-VALIDA-TELA; ele apenas marca até onde o comando PERFORM deve ir.
Muti bom …. ótimo …. ” Meus Parabéns ” !!!!
Muito bom …. ótimo …. ” Meus Parabéns ” !!!!
Primeiramente, excelente texto!
Mas sigo com uma dúvida que creio que seja comum entre os iniciantes:
Nessa situação utilizando o GO TO, se o perform “R12-VALIDA-TELA” estiver em uma condição de repetição (“faça até” ou “faça enquanto”), exemplo: UNTIL, o programa continuaria dentro do loop UNTIL?
Para ficar mais claro:
ROTINA-PRINCIPAL.
PERFORM R12-VALIDA-ARQUIVO THRU SR12
UNTIL WS-FIM-ARQUIVO.
Obrigado!