Princípios da programação estruturada

Photo by Alain Pham on Unsplash

A programação estruturada é uma técnica de programação, desenvolvida a partir dos anos 1960, e que tem por objetivo aumentar a qualidade e a clareza de programas, além de acelerar o tempo necessário para codificação e teste de um programa.

Esse paradigma ganhou força e foi amplamente aceito tanto pelo meio acadêmico quanto pela indústria a partir, principalmente, da publicação em 1968 do artigo Go To Statement Considered Harmful, de Edsger Dijkstra, cientista da computação holandês que é considerado o pai da programação estruturada.

Por causa desse artigo, a oposição entre programação estruturada e o uso do comando GO TO virou quase um dogma: “se tem GO TO não é estruturado”, o que obviamente é um exagero. Ou uma visão muito simplista. Algum tempo atrás publiquei um artigo falando sobre o meu ponto de vista em relação a essa controvérsia.

De acordo com o teorema da programação estruturada, todo programa pode ser construído a partir da combinação de três estruturas básicas: sequência, seleção e repetição. Neste artigo vamos revisar quais recursos estão disponíveis no COBOL para implementar cada uma delas. Muitos desses recursos só se tornaram disponíveis após a revisão COBOL-85.

Sequência

A sequência ou bloco de comandos é a estrutura mais simples das três. Trata-se, como o nome diz, apenas de uma sequência de comandos sem desvios dentro do programa.

Cobol: Estrutura de sequência
Figura 1. Bloco de comados.

No COBOL, qualquer sequência de comandos de atribuição, entrada/saída e operação arimética se encaixa nessa definição. Nossa única preocupação deve ser agrupá-los de acordo com sua finalidade funcional, e talvez separá-los em parágrafos específicos, quando aplicável.

Veja o exemplo abaixo:

initialize sh05v-cd-uf
exec sql 
    select cd_uf 
    into :sh05v-cd-uf 
    from sh05v 
    where mt_part = :sh02v-mt-part 
      and nr_seq_end = (select max(nr_seq_end) 
                        from sh05v 
                        where mt_part = :sh02v-mt-part) 
end-exec.

move xx00v-da-arg to r2000-da-ref
move sh02v-mt-part to r2000-mt-part
move sh01v-nm-part to r2000-nm-part
move sh01v-tp-sexo to r2000-tp-sexo
move sh01v-tp-civil to r2000-tp-civil
move sh01v-tp-ativo to r2000-tp-ativo
move sh01v-tp-apos to r2000-tp-apos
move sh01v-cd-patr to r2000-cd-patr
move sh05v-cd-uf to r2000-cd-uf
move sc03v-vr-sal to r2000-vr-sal
move sh02v-tx-part-ant to r2000-tx-part-ant
move sh02v-tx-part-atu to r2000-tx-part-atu
move sh01v-dt-nasc to wt-dt-aux
compute r2000-nr-idade = wt-da-aa-arg - wt-aa-aux
move sh01v-dt-inscr to wt-dt-aux
compute r2000-nr-tmp-aerus = wt-da-aa-arg - wt-aa-aux
write r2000-relatorio

move sh02v-mt-part to r3000-mt-part
move sh01v-nm-part to r3000-nm-part
write r3000-relatorio

Neste exemplo, o conjunto de todos os comandos formam uma sequência, inclusive a instrução SQL que aparece no começo. O que podemos reparar é que essa sequência parece juntar blocos com finalidades diferentes. As primeiras buscam um registro na base de dados, em seguida vemos linhas que parecem montar uma linha de impressão para o relatório r2000 e finalmente alguns outros comandos para montar uma linha de impressão para um relatório chamado r3000.

Uma opção a considerar seria separar os três conjuntos em três parágrafos distintos, cada um deles com uma finalidade específica. Algo como LE-REGISTRO-SH05V, IMPRIME-RELATORIO-R2000 e IMPRIME-RELATORIO-R3000.

Naturalmente isso não é uma regra; é só um ponto a considerar. A questão que deve ser respondida para tomar esse tipo de decisão é: separar esses comandos em parágrafos distintos vai melhorar ou piorar a clareza do programa para um programador que tenha que modificá-lo daqui a 10 anos?

Seleção

Comandos de seleção são aqueles que permitem desviar o fluxo de execução de um programa para processar alguns comandos e outros não, como mostram as figuras abaixo:

Cobol: Estrutura de seleção com um caminho alternativo
Figura 2. IF.
Cobol: Estrutura de seleção com dois caminhos alternativos
Figura 3. IF/THEN.

Normalmente essa estrutura é formada por comandos IF/ELSE, e a partir do COBOL-85 delimitadores devem ser usados para aumentar a clareza do programa, como mostrados em amarelo no exemplo abaixo:

if sh02v-tx-part-atu not = sh02v-tx-part-ant 
    if sh01v-dt-transf not = "01.01.0001" 
        move sh01v-dt-transf(7:4) to wt-da-aa-transf 
        move sh01v-dt-transf(4:2) to wt-da-mm-transf 
        if wt-da-transf > sh02v-da-ref-ant 
            move "S" to r2000-fl-transf 
            perform s05-select-sh12v thru ss05 
            move sh12v-cd-repr to r2000-cd-repr
        end-if
    end-if
end-if

Outro comando do COBOL-85 que pode ser usado para seleção é o EVALUATE, representado graficamente pela figura 4.

Este comando faz a função “case”, permitindo a execução de mais de dois blocos de comandos em função dos valores de determinadas variáveis.

Cobol: Estrutura de seleção com múltiplos caminhos
Figura 4. EVALUATE.

O comando EVALUATE permite diversas variações para uso de múlplicas variáveis e múltiplos valores para comparação. Seu formato mais simples é o que executa comandos em função dos valores possíveis de uma única variável, como no exemplo abaixo:

    evaluate adirf-id-registro
       when "00"
            perform c211-id-registro-00 thru sc211
       when "01"
            perform c212-id-registro-01 thru sc212
       when "3 "
            perform c213-id-registro-3  thru sc213
       when "20"
            perform c214-id-registro-20 thru sc214
       when "21"
            perform c215-id-registro-21 thru sc215
       when "02"
            perform c216-id-registro-02 thru sc216
       when "23"
            perform c217-id-registro-23 thru sc217
       when "44"
            perform c218-id-registro-44 thru sc218
    end-evaluate

Naturalmente cada opção “when” poderia ter mais de um comando, ao invés do perform que se vê no exemplo acima.

Existem outros comandos do COBOL-85 em diante igualmente válidos para seleção. Por exemplo, um comando READ com as opções AT END e NOT AT END funciona, na prática, como se houvesse um IF/THEN associado ao resultado da leitura:

READ ARQUIVO
    AT END MOVE CURRENT-DATE TO WT-DT-FIM-EXECUCAO
           ADD WT-CT-REGISTROS TO WT-TT-PROCESSADO
    NOT AT END
           ADD 1 TO WT-CT-REGISTROS
END-READ

Repetição

Estruturas de repetição são usadas para executar um determinado bloco de comandos (1) enquanto uma ou mais condições são satisfeitas (WHILE), (2) até que uma ou mais condições sejam satisfeitas (UNTIL), (3) uma determinada quantidade de vezes (TIMES) (4) ou variando uma variável até que ela atinja determinado valor (FOR).

Cobol: Estrutura de repetição com teste prévio
Figura 5. WHILE.
Cobol: Estrutura de repetição com teste posterior
Figura 6. UNTIL.

Todas essas opções podem ser reproduzidas por variações do comando PERFORM, algumas delas alguns existentes no COBOL desde a especificação de 1960.

A semântica do COBOL contribui para algumas confusões, principalmente quando o programador está começando a trabalhar com a linguagem. Por exemplo, para construir em COBOL a estrutura while que se vê abaixo…

while A < B
   C = B ** 2
   A = A + 1
end-while

…teríamos que escrever…

PERFORM UNTIL A >= B
   COMPUTE C = B ** 2
   ADD 1 TO A
END-PERFORM

Note que utilizamos a opção UNTIL do comando PERFORM para executar um while e que o operador lógico está invertido: A < B no primeiro código, A >= B no segundo. Isso acontece porque o default do PERFORM é testar a condição que está no UNTIL antes de executar o bloco de comandos, assim como o while na maioria das linguagens.

Já numa estrutura until, o teste das condições acontece depois da primeira execução do bloco de comandos. Algo mais ou menos assim:

do
   C = B ** 2
   A = A + 1
until A >= B

Em COBOL, continuaríamos usando o comando PERFORM com opção UNTIL, mas com a cláusula WITH TEST AFTER:

PERFORM UNTIL A >= B WITH TEST AFTER
   COMPUTE C = B ** 2
   ADD 1 TO A
END-PERFORM

Outra variação muito comum do comando PERFORM é a opção VARYING, que permite incrementar uma variável até que determinada condição seja atigida.

Em Java, seria algo semelhante ao código abaixo:

Outras práticas que favorecem a estruturação de programas em COBOL

(1) Organizar o programa em parágrafos que tenham uma única finalidade, (2) usar as diversas opções do comando PERFORM para organizar a execução do programa “de cima para baixo”, (3) usar delimitadores de escopo como END-IF, END-PERFORM, END-READ etc sempre que possível.

Essas seriam as três regras essenciais da programação estruturada em COBOL. Algumas outras práticas, porém, vão contribuir significativamente para a clareza e a organização do programa.

O uso do comando SET para ativar e desativar condições definidas com nível 88 é uma delas. Além de serem mais simples, essas instruções deixam mais evidentes as intenções do programar.

Veja no exemplo abaixo:

WORKING-STORAGE SECTION.
...
01 TIPO-MOVIMENTO      PIC 9(001) VALUE ZEROS.
    88 CREDITO         VALUE 1 THRU 3.
    88 DEBITO          VALUE 4 THRU 6.
    88 ESTORNO         VALUE 9.
...
PROCEDURE DIVISION.
...
    SET DEBITO TO TRUE

Quando um nome condicional possui mais de um valor possível, como é o caso de CREDITO e DEBITO, o comando SET TO TRUE vai fazer com que a variável (TIPO-MOVIMENTO) receba o primeiro valor definido na cláusula VALUE.do nome condicional. No exemplo acima, SET DEBITO TO TRUE corresponde a um MOVE 4 TO TIPO-MOVIMENTO.

Se não usássemos o comando SET teríamos que usar o MOVE. O uso do SET deixou a instrução mais evidente, e produziu o mesmo efeito.

Da mesma forma, o uso de cláusulas NOT ajuda muito a manter o código estruturado e legível. O NOT está disponível para sentenças a partir do COBOL-85, tais como AT END, ON SIZE ERROR e INVALID KEY.

O exemplo abaixo mostra como ficaria um código hipotético com e sem a cláusula NOT:

Com NOT AT END:

READ ARQUIVO
   AT END
        SET ARQEOF TO TRUE
   NOT AT END
        ADD 1 TO CONTADOR
END-READ
   

Sem NOT AT END:

READ ARQUIVO
    AT END
        SET ARQEOF TO TRUE
END-READ

IF NOT ARQEOF
    ADD 1 TO CONTADOR
END-IF

Conclusão

Algumas linguagens de programação, principalmente aquelas que são estritamente orientadas a objeto, exigem uma estrutura mais rígida de codificação que minimiza a desorganização do programa ao longo do tempo.

O COBOL, no entanto, foi projetado para que o programa fosse um “texto” escrito em inglês, com seções, parágrafos e sentenças. E textos podem ser claros e concisos ou verborrágicos e desorganizados.

A prática da programação estruturada não só reduz o esforço necessário para manutenções futuras, mas também aumenta a longevidade do programa na medida em que reduz aquele nosso velho impulso de “jogar fora e fazer de novo”.

E quem nunca…


Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *