7. Programação Estruturada
A programação estruturada é uma técnica de organização e codificação de programas que foi desenvolvida para eliminar os efeitos nocivos da programação linear e seus desvios incondicionais para frente e para trás. Além disso, a programação estruturada facilita os testes do programa e sua manutenção no futuro.
É possível que você ainda encontre alguns programas que foram construídos de acordo com o modelo linear. Mas com toda certeza, você será obrigado a construir seus novos programas seguindo o modelo estruturado.
Essa abordagem afirma que todo programa pode ser construído com apenas três estruturas básicas: sequência, seleção e repetição. Neste capítulo explicaremos cada uma dessas estruturas e aplicaremos seus conceitos em muitos exemplos em COBOL.
Sequência
Sequência é a estrutura mais simples, representada por um conjunto finito de ações ou comandos que são executados um após o outro, e que têm um objetivo funcional claro.
O exemplo abaixo mostra uma sequência que tem por objetivo preencher e gravar um registro no arquivo de saída:
MOVE CRA0205-NR-FATURA TO CRA0206-NR-FATURA MOVE CRA0205-NR-DUPLICATA TO CRA0206-NR-DUPLICATA MOVE CRA0205-CD-CLIENTE TO CRA0206-CD-CLIENTE MOVE CRA0205-DT-EMISSAO TO CRA0206-DT-EMISSAO MOVE CRA0205-DT-VENCIMENTO TO CRA0206-DT-VENCIMENTO MOVE CRA0205-VL-FATURA TO CRA0206-VL-FATURA MOVE CRA0205-CD-CATEGORIA TO CRA0206-CD-CATEGORIA MOVE CRA0205-ST-DUPLICATA TO CRA0206-ST-DUPLICATA WRITE CRA0206-REGISTRO
Muitas vezes é interessante isolar uma sequência num parágrafo. Isso permite que um algoritmo complexo seja quebrado em pequenas “funções” que serão “orquestradas” por um parágrafo principal. Isolar uma sequência num parágrafo também permite que ela seja executada de diversos pontos do programa.
No COBOL, quando queremos isolar uma sequência num parágrafo usamos o comando PERFORM para executá-lo. Este comando transfere o fluxo de execução para um parágrafo. Quando o último comando do parágrafo é executado, o fluxo de controle retorna para o próximo comando depois do PERFORM.
PERFORM Paragrafo1 Comando1 . . . Paragrafo1. Comando2 Comando3 Comando4.
No exemplo acima, Comando2, Comando3 e Comando4 formam uma sequência que foi isolada num parágrafo chamado Parágrafo1, que é chamado pelo comando PERFORM. O Comando1 só será executado depois do Comando4.
Seleção
A estrutura de seleção permite a escolha entre caminhos alternativos. Esta escolha é baseada no teste de uma ou mais condições que definem o caminho que será seguido pelo fluxo de execução do programa.
As seleções podem ter uma, duas ou mais alternativas.
Exemplo de seleção com um único caminho alternativo:
IF CRA0205-ST-DUPLICATA NOT = “CNC” MOVE CRA0205-NR-FATURA TO CRA0206-NR-FATURA MOVE CRA0205-NR-DUPLICATA TO CRA0206-NR-DUPLICATA MOVE CRA0205-CD-CLIENTE TO CRA0206-CD-CLIENTE MOVE CRA0205-DT-EMISSAO TO CRA0206-DT-EMISSAO MOVE CRA0205-DT-VENCIMENTO TO CRA0206-DT-VENCIMENTO MOVE CRA0205-VL-FATURA TO CRA0206-VL-FATURA MOVE CRA0205-CD-CATEGORIA TO CRA0206-CD-CATEGORIA MOVE CRA0205-ST-DUPLICATA TO CRA0206-ST-DUPLICATA WRITE CRA0206-REGISTRO ADD 1 TO WT-CT-GRAVADOS END-IF
Exemplo de seleção com dois caminhos alternativos:
IF CRA0205-ST-DUPLICATA NOT = “CNC” MOVE CRA0205-NR-FATURA TO CRA0206-NR-FATURA MOVE CRA0205-NR-DUPLICATA TO CRA0206-NR-DUPLICATA MOVE CRA0205-CD-CLIENTE TO CRA0206-CD-CLIENTE MOVE CRA0205-DT-EMISSAO TO CRA0206-DT-EMISSAO MOVE CRA0205-DT-VENCIMENTO TO CRA0206-DT-VENCIMENTO MOVE CRA0205-VL-FATURA TO CRA0206-VL-FATURA MOVE CRA0205-CD-CATEGORIA TO CRA0206-CD-CATEGORIA MOVE CRA0205-ST-DUPLICATA TO CRA0206-ST-DUPLICATA WRITE CRA0206-REGISTRO ADD 1 TO WT-CT-GRAVADOS ELSE ADD 1 TO WT-CT-REJEITADOS END-IF
A estrutura de seleção com múltiplos caminhos também é conhecida como “estrutura case”. No COBOL, ela é implementada pelo comando EVALUATE. Um de seus formatos possíveis é:
EVALUATE Variável WHEN valor2 Comando-11 Comando-12 Comando-1n WHEN Valor2 Comando-21 Comando-22 Comando-2n WHEN ValorN Comando-n1 Comando-n2 Comando-nn WHEN OTHER Outros comandos END-EVALUATE
A opção WHEN OTHER é selecionada se a variável não possui nenhum dos valores informados nas cláusulas WHEN anteriores.
Repetição
As estruturas de repetição são formadas por um procedimento e uma condição de início ou término. Em outras palavras, um procedimento é executado enquanto (ou até que) uma condição é (ou seja) verdadeira. Um procedimento pode ser formado por sequências, seleções e até mesmo outras repetições. Estruturas de repetição são mais conhecidas como loops controlados, ou simplesmente loops.
A condição que define a estrutura de repetição pode ser testada antes do procedimento:
Em COBOL, esse tipo de estrutura também é implementado pelo comando PERFORM. Para declarar a condição de execução usamos a cláusula UNTIL.
PERFORM Paragrafo1 UNTIL Condicao1
Comando1
.
.
.
Paragrafo1.
Comando2
Comando3
Comando4.
Neste exemplo, o programa executará do Comando2 ao Comando4 até que a Condicao1 seja verdadeira. Quando essa condição for confirmada, o fluxo de execução retorna para o comando posterior ao PERFORM, Comando1.
Podemos usar o PERFORM UNTIL para executar os comandos de um parágrafo (como vimos no exemplo anterior) ou codificá-lo num formato conhecido como modo in-line, onde os comandos ficam num bloco delimitado pelas palavras PERFORM e END-PERFORM.
O exemplo abaixo teria o mesmo efeito do exemplo anterior:
PERFORM UNTIL Condicao1
Comando2
Comando3
Comando4
END-PERFORM
Comando1
Com a opção UNTIL, a condição é testada antes que o parágrafo seja executado. Em outras palavras, o parágrafo será executado em loop enquanto a condição for falsa. Mas é possível também (apesar de não ser comum) codificar o PERFORM para que ele só condição depois de executar o procedimento pelo menos uma vez:
Para isso usamos a opção WITH TEST AFTER:
PERFORM Paragrafo1 WITH TEST AFTER UNTIL Condicao1
Comando1
.
.
.
Paragrafo1.
Comando2
Comando3
Comando4.
No exemplo acima, os comandos 2, 3 e 4 serão executados pelo menos uma vez. Quando o programa executar o último comando do parágrafo, a Condição1 será testada. Se ela for verdadeira o fluxo de controle retorna para o comando posterior ao PERFORM (Comando1). Caso contrário, o loop continua.
A opção WITH TEST AFTER também pode ser usada no modo in-line:
PERFORM WITH TEST AFTER UNTIL Condicao1 Comando2 Comando3 Comando4 END-PERFORM Comando1
O diagrama estruturado
Desenhar um diagrama antes de começar a codificar ajuda muito no planejamento, na construção e até nos testes de programa estruturado. Nesses diagramas as sequências, seleções e repetições são organizadas de maneira hierárquica, formando um desenho semelhante a um organograma.
Cada “caixa” do diagrama dará origem a um parágrafo no programa COBOL. E, para isso, é importante que cada “caixa” tenha uma função clara e única dentro do programa.
Na verdade, o próprio programa deve ter uma função clara e única, e essa função será decomposta em parágrafos que serão orquestrados através de IFs e PERFORMs.
Normalmente a especificação do programa é composta pelo diagrama estruturado e por um pseudocódigo para cada parágrafo.
Também é comum adotarmos algumas convenções quando elaboramos um diagrama estruturado, a começar pelos nomes. Como cada caixa será um parágrafo com uma função única é interessante nomeá-las usando um verbo e um objeto, como em “Imprime Total” e “Imprime Cabeçalho”.
É importante também numerar os parágrafos, mantendo alguma ordem hierárquica. Repare que a caixa que representa o programa ganhou o número zero, e sua funcionalidade foi dividida em três parágrafos principais, que numeramos com 1, 2 e 3. O parágrafo 2 chama dois outros parágrafos, que chamamos de 21 e 22, e o parágrafo 21 chama um outro parágrafo a quem demos o número 211. Esse tipo de numeração ajuda bastante em manutenções futuras: se você quiser saber “quem chama” o parágrafo 211-CALCULA-COMISSAO basta procurar por um parágrafo cujo nome comece com 21-… Se quiser saber quem chama o parágrafo 21-IMPRIME-DETALHE, basta procurar por um parágrafo cujo nome comece com 2-…
Ainda nesse diagrama de exemplo, o parágrafo IMPRIME-CABECALHO pode ser chamado em dois lugares diferentes: 21-IMPRIME-DETALHE e 22-IMPRIME-TOTAL. Para caracterizar essa situação usamos uma numeração diferente (X1). Se tivéssemos mais parágrafos nessa condição eles seriam chamados de X2, X3, X4 e assim por diante.
Outra convenção que usamos no diagrama estruturado é a indicação de loops e condições. O parágrafo 2-PROCESSA será executado em loop, até que não existam mais registros a ler no arquivo de entrada. Nós marcamos essa situação com um asterisco ao lado de seu número. O parágrafo X1-IMPRIME-CABECALHO só será executado quando a página do relatório estiver completa. Sinalizamos essa condição com um ponto de interrogação.
O diagrama estruturado considera que as ações são executadas de cima para baixo, e da esquerda para a direita. Assim, na figura anterior, as ações seriam executadas na seguinte ordem: 0, 1, 2 (em loop), 21, X1 (talvez), 211, 22, X1 (talvez) e 3.
Na hora de transformar essa representação num programa COBOL, porém, é importante que todos os parágrafos sejam posicionados de maneira que:
- Possamos ler e entender o programa de cima para baixo (top-down)
- Nunca precisemos executar um parágrafo que está “atrás” do PERFORM
E existe um jeito fácil de conseguir isso: basta manter juntos os parágrafos de mesmo nível, posicioná-los em ordem crescente, deixando os parágrafos do tipo “X” para o final. Traduzindo para o COBOL, nosso programa hipotético ficaria mais ou menos assim:
PROCEDURE DIVISION. 0-PROGRAMA. PERFORM 1-INICIA PERFORM 2-PROCESSA UNTIL condicao PERFORM 3-TERMINA STOP RUN. 1-INICIA. (comandos do parágrafo INICIA) 2-PROCESSA. (outros comandos, se necessário) PERFORM 21-IMPRIME-DETALHE (outros comandos, se necessário) PERFORM 22-IMPRIME-TOTAL (outros comandos, se necessário) 3-TERMINA. (comandos do parágrafo TERMINA) 21-IMPRIME-DETALHE. IF condição PERFORM X1-IMPRIME-CABECALHO END-IF (comandos para imprimir as linhas de detalhe) 22-IMPRIME-TOTAL. IF condição PERFORM X1-IMPRIME-CABECALHO END-IF (comandos para imprimir as linhas de total) X1-IMPRIME-CABECALHO. (comandos para imprimir o cabeçalho do relatório)
Dividir a funcionalidade de um programa em parágrafos estruturados busca oferecer níveis de abstração diferentes, dependendo do nível em que o parágrafo está. No nosso exemplo, o parágrafo zero contém toda a funcionalidade do programa, mas os detalhes de implementação estão “escondidos” nos parágrafos 1, 2 e 3.
Estruturando o primeiro programa
Neste ponto já conseguimos perceber algumas características de um programa estruturado:
- Todo comando PERFORM faz referência a um parágrafo que será declarado depois dele, nunca antes. Ao analisar um programa grande você saberá que os detalhes de implementação de um parágrafo estarão sempre depois do ponto em que você encontrar o PERFORM;
- Ao imaginar a estrutura de um programa, o programador tentará sempre pensar em blocos de funções que possam ser montadas em estruturas superiores que tenham algum nível de abstração
- O padrão de nomenclatura adotado para os parágrafos é essencial para que se consiga identificar com facilidade seu objetivo e sua ordem na hierarquia de parágrafos.
Esses conceitos ficarão mais claros nesta seção, onde reconstruiremos de forma estruturada o primeiro programa que codificamos neste livro. Se analisarmos seu fluxograma, veremos que existem sequências, seleções e repetições:
Nosso algoritmo se divide em três funcionalidades principais:
- Procedimentos iniciais do programa, executado uma única vez, responsável pela abertura dos arquivos e pela primeira leitura do arquivo de entrada
- Loop, que se repete para todos os registros lidos no arquivo de entrada; será executado até que se detecte a condição de fim de arquivo;
- Procedimentos finais do programa, também executado uma única vez, responsável pelo fechamento dos arquivos e pela exibição dos contadores de lidos e gravados.
Representando isso num diagrama estruturado ficaria assim:
Vamos começar criando um parágrafo que chamaremos de 0-PRINCIPAL. Esse será o parágrafo que executará os procedimentos iniciais, executará o loop até o fim de arquivo, executará os procedimentos finais e encerrará o programa:
*================================================================* PROCEDURE DIVISION. *----------------------------------------------------------------* 0-PRINCIPAL. PERFORM 1-INICIO PERFORM 2-PROCESSO UNTIL WT-ST-CRA0205 NOT = “00” PERFORM 3-TERMINO STOP RUN.
Em seguida codificaremos o parágrafo dos procedimentos iniciais.
*----------------------------------------------------------------* * ABERTURA DE ARQUIVOS E PRIMEIRA LEITURA DO ARQUIVO DE ENTRADA *----------------------------------------------------------------* 1-INICIO. OPEN INPUT CRA0205 OUTPUT CRA0206 READ CRA0205.
O parágrafo 2-PROCESSA vem na sequência. Nele ficarão o incremento do contador de registros lidos, o teste para verificar se a duplicata é ativa e leitura do próximo registro a ser processado:
*----------------------------------------------------------------* * VERIFICA SE A DUPLICATA E’ ATIVA. SE FOR, GRAVA O REGISTRO NO * ARQUIVO DE SAIDA E LE O PROXIMO REGISTRO DO ARQUIVO DE ENTRADA *----------------------------------------------------------------* 2-PROCESSO. ADD 1 TO WT-CT-LIDOS IF CRA0205-ST-DUPLICATA NOT = “CNC” PERFORM 21-GRAVA-SAIDA THRU 21-FIM END-IF READ CRA0205.
Mantendo a ordem dos parágrafos, o próximo a ser codificado é aquele onde executaremos os procedimentos finais:
*----------------------------------------------------------------* * FECHA ARQUIVOS E EXIBE CONTADORES *----------------------------------------------------------------* 3-TERMINO. CLOSE CRA0205 CRA0206 DISPLAY “LIDOS=” WT-CT-LIDOS “ GRAVADOS=” WT-CT-GRAVADOS.
E por último, o parágrafo onde acontece a gravação do registro no arquivo de saída:
*----------------------------------------------------------------* * GRAVA REGISTRO NO ARQUIVO DE SAIDA *----------------------------------------------------------------* 21-GRAVA-SAIDA. MOVE CRA0205-NR-FATURA TO CRA0206-NR-FATURA MOVE CRA0205-NR-DUPLICATA TO CRA0206-NR-DUPLICATA MOVE CRA0205-CD-CLIENTE TO CRA0206-CD-CLIENTE MOVE CRA0205-DT-EMISSAO TO CRA0206-DT-EMISSAO MOVE CRA0205-DT-VENCIMENTO TO CRA0206-DT-VENCIMENTO MOVE CRA0205-VL-FATURA TO CRA0206-VL-FATURA MOVE CRA0205-CD-CATEGORIA TO CRA0206-CD-CATEGORIA MOVE CRA0205-ST-DUPLICATA TO CRA0206-ST-DUPLICATA WRITE CRA0206-REGISTRO ADD 1 TO WT-CT-GRAVADOS.
Basicamente o que fizemos foi reorganizar os mesmos comandos do programa anterior em parágrafos que têm finalidades específicas e distintas.
Talvez a única mudança significativa tenha sido a forma como codificamos o comando READ. Antes tínhamos a cláusula AT END para capturar o estado de fim de arquivo; agora testamos o fim de arquivo usando o file status retornado pelo comando de leitura; o programa executa o loop 2-PROCESSO até que a operação de leitura resulte num file status diferente de “00”.
Boas práticas na programação estruturada
Qualquer que seja a técnica de programação, em qualquer linguagem, a diferença entre o veneno e o remédio está na dose. Um programa linear cheio de GO TOs para frente e para trás dificulta o entendimento e prejudica manutenções futuras. Mas um programa estruturado com muitos parágrafos curtos, ou poucos parágrafos muito longos, também será difícil de manter.
A dose certa depende sempre de experiência e bom senso, mas algumas regras podem ajudar nas decisões iniciais. Vamos resumir algumas recomendações que fizemos nesse capítulo:
- Deve haver apenas um comando STOP RUN no programa, posicionado no final do parágrafo principal.
- Nunca codificar mais de um comando em uma única linha
- Cada parágrafo deve ter uma finalidade que você consiga descrever numa frase com verbo e predicado. Se a frase ficar longa e confusa talvez você esteja misturando funções diferentes, que deveriam estar separadas. Por exemplo: “Calcular Imposto de Renda” parece bom; mas “Calcular Imposto de Renda e Gravar Registro de Declaração” pode não ser tão bom, pois parece estar juntando duas funções que não necessariamente precisam acontecer juntas;
- Os parágrafos devem aparecer na mesma ordem em que foram mencionados dentro do programa. Repare no nosso exemplo que 1-INICIO, 2-PROCESSO e 3-TERMINO aparecem antes de 21-GRAVA-SAIDA. Isso está assim porque PERFORM 3-TERMINO veio antes de PERFORM 21-GRAVA-SAIDA.
- Nunca dar um PERFORM “para trás”, ou seja, para um parágrafo que foi codificado antes do comando PERFORM que o chamou,
- Os nomes após os prefixos também deve ser claros sobre a ação que se pretende executar. 231-ALTFATMES não é um nome muito bom; 231-ALTERA-FATURA-MENSAL é um nome melhor.
- Os comentários são essenciais para orientar o programador em futuras manutenções. Uma prática saudável é incluir pelo menos uma linha de comentário, destacada por linhas tracejadas, antes de cada parágrafo. Os comentários devem informar o objetivo e, se possível, explicar seu funcionamento e o motivo das decisões que toma.
Anterior | Conteúdo | Próxima |