8. Reuso de Código

8. Reuso de Código

O reuso de código é um recurso valioso que economiza tempo de programação, além de aumentar a uniformidade de sistemas e reduzir redundâncias. A ideia é aproveitar ao máximo qualquer ativo (ou asset)  que já tenha sido desenvolvido para outros sistemas ou aplicações.

O reuso de software não se limita apenas a código fonte. Qualquer ativo produzido durante o ciclo de desenvolvimento de uma aplicação pode ser projetada para reuso, incluindo componentes executáveis, cenários de teste, templates, documentos etc.

No entanto, como este livro trata especificamente sobre programação, neste capítulo veremos alguns recursos disponíveis no COBOL para reuso de código tanto em tempo de compilação quanto em tempo de execução.

Reuso de código em tempo de compilação

Em todo grande sistema é muito comum que algumas estruturas se repitam em vários programas. Vejamos, por exemplo, o detalhamento do arquivo CRA0205 no programa que construímos nos capítulos anteriores:

      *================================================================*
       DATA DIVISION.
      *----------------------------------------------------------------*
       FILE SECTION.

      *----------------------------------------------------------------*
      * UNLOAD DA TABELA DE DUPLICATAS
      *----------------------------------------------------------------*
       FD CRA0205.
       01 CRA0205-REGISTRO.
           03 CRA0205-NR-FATURA        PIC  9(006).
           03 CRA0205-NR-DUPLICATA     PIC  9(002).
           03 CRA0205-CD-CLIENTE       PIC  X(006).
           03 CRA0205-DT-EMISSAO       PIC  9(008).
           03 CRA0205-DT-VENCIMENTO    PIC  9(008).
           03 CRA0205-VL-FATURA        PIC S9(013)V9(002).
           03 CRA0205-CD-CATEGORIA     PIC  X(003).
           03 CRA0205-ST-DUPLICATA     PIC  X(003).

Este layout, teria que ser reescrito em qualquer programa de nosso sistema que quisesse acessar o arquivo ../dat/cra0205.dat.

Para permitir que esse trecho de código possa ser reaproveitado por outros programas, normalmente o colocamos num arquivo separado, conhecido como copybook ou simplesmente book. Um book nada mais é que um pedaço de código fonte que pretendemos reaproveitar em diversos programas.

Ao construir nosso programa, ao invés de redigitarmos todas as linhas, simplesmente fazemos referencia ao book usando um comando do COBOL chamado COPY.

      *================================================================*
       DATA DIVISION.
      *----------------------------------------------------------------*
       FILE SECTION.

       COPY CRF0205.

Ao encontrar um comando COPY, o compilador identifica o nome do book (no exemplo acima, CRF0205), procura um arquivo com esse nome na biblioteca de books (previamente criada pelo administrador do sistema) e insere todas as suas linhas no programa fonte, antes de iniciar a compilação.

A princípio qualquer trecho de código fonte pode ser criado num book para ser reaproveitado a posteriori. Mas o mais comum é usarmos esse recurso para:

  • Declaração de arquivos na CONFIGURATION SECTION da ENVIRONMENT DIVISION (SELECT arquivo ASSIGN TO…)
  • Detalhamento de arquivos na FILE SECTION da DATA DIVISION (FD arquivo…)
  • Variáveis de trabalho que devem existir em todos os programas (normalmente definidas pelos arquitetos do sistema)
  • Cabeçalhos padronizados para telas e relatórios

O comando COPY deve ser inserido exatamente no mesmo lugar onde estariam as linhas que ele contém. Se queremos inserir um book de declaração de arquivos, ele deve ser inserido na CONFIGURATION SECTION; se é um detalhamento de arquivos, ele deve aparecer na FILE SECTION.

Reuso de código em tempo de execução

Além da biblioteca de books, todas as empresas possuem um conjunto de subrotinas (ou subprogramas) que seu programa pode chamar em tempo de execução. O uso de subrotinas diminui a necessidade de codificação, simplifica manutenções e testes e padroniza o comportamento dos sistemas.

Subrotinas são programas executáveis que ficam disponíveis numa biblioteca criada e mantida pelos administradores ou arquitetos do sistema.

Qualquer funcionalidade que possa ser reaproveitada em vários programas e em vários sistemas é candidata a virar uma subrotina. Mas o mais comum é encontrar subrotinas para:

  • Validação, formatação e cálculo envolvendo datas e horas
  • Validação e formatação de CPF, CNPJ, CEP, telefones e e-mails
  • Geração de número por extenso
  • Exibição e registros de execução e mensagens de erro
  • Controle de níveis de acesso necessários para execução do programa
  • Recuperação de informações obrigatórias sobre a empresa, seus departamento e sistemas

Na grande maioria das vezes as instalações produzem seu próprio conjunto de subrotinas, que devem ser usadas por todos os programadores. Consequentemente nomes, argumentos e resultados variam muito de empresa para empresa.

Quando precisamos executar uma subrotina em nossos programas usamos o comando CALL, cuja sintaxe mais simples é mostrada abaixo. O programa que chama a subrotina normalmente é chamado de programa chamador ou programa principal:

CALL nome-da-subrotina

O nome da subrotina pode ser informado como um literal…

CALL “SLXFDATA”

…ou como uma variável que contenha o nome do subrotina…

CALL WT-NM-SUBROTINA

No primeiro caso, conhecido como modo estático, a subrotina será inserida no programa chamador em tempo de linkedição. Isso significa que se a subrotina for atualizada, todos os programas que chamam essa subrotina terão que ser linkeditados novamente, provavelmente passando novamente pelo processo de compilação.

No segundo caso, conhecido como modo dinâmico, o sistema operacional procura pela subrotina em tempo de execução, no momento em que o comando CALL é executado no programa chamador. Esse modo não tem uma performance tão boa quanto a anterior, mas permite que subrotinas sejam atualizadas sem que os programas principais precisem ser recompilados.

Depois que uma subrotina é executada pelo comando CALL, o fluxo de execução volta para o comando seguinte, no programa chamador.

Você pode passar parâmetros ou argumentos para a subrotina usando a cláusula USING:

CALL nome-da-subrotina USING argumento1 argumento2 ... argumentoN

Na prática, esses argumentos são variáveis que a subrotina espera receber para cumprir sua função. Elas devem aparecer na cláusula USING na ordem que foi definida quando a subrotina foi construída. Além disso, as pictures de cada argumento precisa ser igual às pictures esperadas pelo subprograma. Normalmente as instalações possuem algum tipo de documentação que mostra quais são as subrotinas disponíveis na instalação, como elas funcionam e argumentos precisam receber e que retorno elas geram.

Por default, os argumentos são passados para a subrotina by reference. Isso significa que o programa principal passa para a subrotina apenas os endereços de memória de cada argumento, e qualquer alteração que a subrotina faça nos valores desses argumentos será refletida no programa principal.

A outra opção é passar os argumentos by content[1], como no exemplo abaixo:

CALL nome-da-subrotina USING BY CONTENT argumento1 ... argumento

Nesse caso, o programa principal passa os endereços de memória de cópias dos argumentos. Logo, mesmo que a subrotina faça qualquer alteração nos valores desses argumentos o programa principal continuará com seus valores originais.

O comando CALL também permite que se insira a cláusula RETURNING, que indica em que variável (ou variáveis) a subrotina vai salvar seus resultados. Esse recurso muitas vezes é usado para receber um código de retorno que indique se a subrotina foi executada com sucesso ou não:

CALL nome-da-subrotina
     USING argumento1 argumento2 ... argumento
     RETURNING retorno

No exemplo acima, “retorno” seria substituído pelo nome de uma variável definida no programa principal.

Chamando subrotinas

Para ilustrar o uso de subrotinas em COBOL vamos construir um novo programa, chamado CRP0207.

Este programa vai ler todos os registros do arquivo CRA0206 (que geramos no programa anterior) e validar a data de vencimento através de uma subrotina chamada XXRVDAT.

Se a data de vencimento for válida, o programa gravará a duplicata num arquivo de duplicatas válidas (CRA0207V). Caso contrário, a duplicata será gravada no arquivo de duplicatas inválidas (CRA0207I).

O layout do arquivo de entrada já é conhecido, e seus books já estão prontos: CRS0206 para declaração do arquivo ENVIRONMENT DIVISION e CRF0206 para detalhamento do arquivo na DATA DIVISION.

O layout do arquivo de duplicatas válidas é igual ao layout do arquivo de entrada. Sendo assim, seu book de declaração (CRS0207V) terá as linhas abaixo:

      *----------------------------------------------------------------*
      * DUPLICATAS VALIDAS
      *----------------------------------------------------------------*
           SELECT CRA0207V ASSIGN TO "$DAT/CRA0207V.DAT"
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WT-ST-CRA0207V.

E seu book de detalhamento (CRF0207V) será praticamente igual ao CRF0206, só mudando a linha de comentários, o nome do arquivo e os prefixos das variáveis:

      *----------------------------------------------------------------*
      * DUPLICATAS VALIDAS
      *----------------------------------------------------------------*
       FD CRA0207V.
       01 CRA0207V-REGISTRO.
           03 CRA0207V-NR-FATURA        PIC  9(006).
           03 CRA0207V-NR-DUPLICATA     PIC  9(002).
           03 CRA0207V-CD-CLIENTE       PIC  X(006).
           03 CRA0207V-DT-EMISSAO       PIC  9(008).
           03 CRA0207V-DT-VENCIMENTO    PIC  9(008).
           03 CRA0207V-VL-FATURA        PIC S9(013)V9(002).
           03 CRA0207V-CD-CATEGORIA     PIC  X(003).
           03 CRA0207V-ST-DUPLICATA     PIC  X(003).

O book de declaração do arquivo de duplicatas inválidas (CRS0207I) também é praticamente igual aos anteriores, só mudando os comentários e os nomes internos e externos do arquivo:

      *----------------------------------------------------------------*
      * DUPLICATAS INVALIDAS
      *----------------------------------------------------------------*
           SELECT CRA0207I ASSIGN TO "$DAT/CRA0207I.DAT"
               ORGANIZATION IS LINE SEQUENTIAL
               FILE STATUS IS WT-ST-CRA0207I.

Já seu book de detalhamento (CRF0207I) terá um campo a mais, justamente para armazenar o código de erro retornado pela subrotina da validação de datas:

      *----------------------------------------------------------------*
      * DUPLICATAS INVALIDAS
      *----------------------------------------------------------------*
       FD CRA0207I.
       01 CRA0207I-REGISTRO.
           03 CRA0207I-NR-FATURA        PIC  9(006).
           03 CRA0207I-NR-DUPLICATA     PIC  9(002).
           03 CRA0207I-CD-CLIENTE       PIC  X(006).
           03 CRA0207I-DT-EMISSAO       PIC  9(008).
           03 CRA0207I-DT-VENCIMENTO    PIC  9(008).
           03 CRA0207I-VL-FATURA        PIC S9(013)V9(002).
           03 CRA0207I-CD-CATEGORIA     PIC  X(003).
           03 CRA0207I-ST-DUPLICATA     PIC  X(003).
           03 CRA0207I-CD-ERRO          PIC  9(003).

A IDENTIFICATION DIVISION segue o mesmo padrão que usamos no programa anterior, com adaptação das cláusulas PROGRAM-ID, DATE-WRITTEN e REMARKS:

      *================================================================*
       IDENTIFICATION DIVISION.
      *----------------------------------------------------------------*
       PROGRAM-ID.    CRP0207.
       AUTHOR.        PAULO ANDRE DIAS.
       DATE-WRITTEN.  04/01/2017.
       REMARKS.
      *----------------------------------------------------------------*
      * SISTEMA:      CR - CONTAS A RECEBER
      * JOB:          02 - GERACAO DE FLUXO DE CAIXA
      * PROGRAMA:     07 - VALIDA DUPLICATAS
      *
      * OBJETIVO:     LER O ARQUIVO DE DUPLICATAS ATIVAS E VALIDAR A
      *               DATA DE VENCIMENTO, SEPARANDO AS DUPLICADAS VA-
      *               LIDAS E INVALIDAS
      *                    
      * VERSOES:      DATA    DESCRICAO
      *               ------  ---------------------------------------
      *               XXXXXX  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      *
      *----------------------------------------------------------------*

Na ENVIRONMENT DIVISION colocamos os books de declaração dos três arquivos que usaremos neste programa:

      *================================================================*
       ENVIRONMENT DIVISION.
      *----------------------------------------------------------------*
       CONFIGURATION SECTION.
       SPECIAL-NAMES.
           DECIMAL-POINT IS COMMA.

       INPUT-OUTPUT SECTION.
       FILE-CONTROL.

      * DUPLICATAS ATIVAS
           COPY CRS0206.

      * DUPLICATAS VALIDAS
           COPY CRS0207V.

      * DUPLICATAS INVALIDAS
           COPY CRS0207I.

E para o detalhamento desses três arquivos inserimos os books de detalhamento, na FILE SECTION da DATA DIVISION:

      *================================================================*
       DATA DIVISION.
      *----------------------------------------------------------------*
       FILE SECTION.

      * DUPLICATAS ATIVAS
           COPY CRF0206.

      * DUPLICATAS VALIDAS
           COPY CRF0207V.

      * DUPLICATAS INVALIDAS
           COPY CRF0207I.

Nossa WORKING-STORAGE começa com a declaração de três campos para file status, um para cada arquivo. Os nomes desses campos, você se lembra, são definidos nos books de declaração que inserimos na ENVIRONMENT:

      *================================================================*
       WORKING-STORAGE SECTION.
      *----------------------------------------------------------------*
       01 WT-FILE-STATUS.
           03 WT-ST-CRA0206            PIC  X(002) VALUE SPACES.
           03 WT-ST-CRA0207V           PIC  X(002) VALUE SPACES.
           03 WT-ST-CRA0207I           PIC  X(002) VALUE SPACES.

Nosso programa exibirá a quantidade de registros lidos, a quantidade de registros válidos (gravados no arquivo CRA0207V) e a quantidade de registros inválidos (gravados no arquivo CRA0207I). Precisamos, portanto, criar esses contadores também na WORKING:

      *================================================================*
       WORKING-STORAGE SECTION.
      *----------------------------------------------------------------*
       01 WT-FILE-STATUS.
           03 WT-ST-CRA0206            PIC  X(002) VALUE SPACES.
           03 WT-ST-CRA0207V           PIC  X(002) VALUE SPACES.
           03 WT-ST-CRA0207I           PIC  X(002) VALUE SPACES.

       01 WT-CONTADORES.
           03 WT-CT-LIDOS              PIC  9(006) VALUE ZEROS.
           03 WT-CT-VALIDOS            PIC  9(006) VALUE ZEROS.
           03 WT-CT-INVALIDOS          PIC  9(006) VALUE ZEROS.

A subrotina XXRVDAT, que usaremos para validar a data de vencimento, recebe dois parâmetros de entrada e gera um código de retorno na saída:

  • Data a ser validada: campo numérico de oito dígitos preenchido com a data que pretendemos validar
  • Formato da data informada: campo alfanumérico de oito dígitos que pode ser preenchido com os seguintes valores: AAAAMMDD, DDMMAAAA, AAMMDD, DDMMAA, AAAAMM ou MMAAAA.
  • Código de retorno da validação: campo numérico de dois dígitos que será preenchido pela subrotina com o resultado da validação da data informada.

O código de retorno poderá assumir os seguintes valores:

  • 0 (zero): Data válida
  • 1: O campo de formato de data (segundo argumento) não foi preenchido com um dos valores válidos (AAAAMMDD, DDMMAAAA etc)
  • 2: Os primeiros dois dígitos do ano não são válidos
  • 3: Os dois últimos dígitos do ano não são válidos
  • 4: O mês é inválido
  • 5: O dia é inválido

Para saber se a data é válida ou inválida, portanto, só precisamos testar se o código de retorno é igual a zero (válido) ou diferente de zero (inválido).

Nós pretendemos chamar a subrotina XXRVDAT de forma dinâmica, ou seja, queremos que nosso programa localize e carregue a subrotina em memória apenas no momento em que ela for chamada. Para isso, precisamos criar uma variável na WORKING para armazenar o nome da subrotina. Vamos aproveitar e criar também uma variável auxiliar para guardar o código de retorno que será gerado pela subrotina:

      *================================================================*
       WORKING-STORAGE SECTION.
      *----------------------------------------------------------------*
       01 WT-FILE-STATUS.
           03 WT-ST-CRA0206            PIC  X(002) VALUE SPACES.
           03 WT-ST-CRA0207V           PIC  X(002) VALUE SPACES.
           03 WT-ST-CRA0207I           PIC  X(002) VALUE SPACES.

       01 WT-CONTADORES.
           03 WT-CT-LIDOS              PIC  9(006) VALUE ZEROS.
           03 WT-CT-VALIDOS            PIC  9(006) VALUE ZEROS.
           03 WT-CT-INVALIDOS          PIC  9(006) VALUE ZEROS.

       01 WT-AUXILIARES.
           03 WT-NM-VDAT               PIC X(008) VALUE "XXRVDAT".
           03 WT-RC-VDAT               PIC 9(002) VALUE ZEROS.

A PROCEDURE DIVISION terá apenas três parágrafos, como mostra seu diagrama estruturado:

COBOL: Diagrama estruturado
Figura 26. Diagrama estruturado do programa CRP0207

O parágrafo principal reproduz a estrutura que mostramos no diagrama. O parágrafo PROCESSA será executado até que o file status seja diferente de zeros, ou seja, que não haja mais registros a serem lidos:

      *================================================================*
       PROCEDURE DIVISION.
      *----------------------------------------------------------------*
       0-PRINCIPAL.

           PERFORM 1-INICIA

           PERFORM 2-PROCESSA UNTIL WT-ST-CRA0206 NOT = "00"

           PERFORM 3-TERMINA

           STOP RUN.

O parágrafo INICIA terá os comandos para abertura dos arquivos e a primeira leitura do arquivo de entrada.

      *----------------------------------------------------------------*
      * ABRE ARQUIVOS E LE PRIMEIRO REGISTRO DO ARQUIVO DE ENTRADA
      *----------------------------------------------------------------*
       1-INICIA.

           OPEN INPUT CRA0206

           OPEN OUTPUT CRA0207V CRA0207I

           READ CRA0206.

No parágrafo PROCESSA ficará a chamada à subrotina para validação da data e a gravação de registros nos arquivos de duplicatas válidas e inválidas.

      *----------------------------------------------------------------*
      * LE TODOS OS REGISTROS DO ARQUIVO DE ENTRADA E VALIDA A DATA DE
      * VENCIMENTO. SE A DATA FOR VALIDA, GRAVA O REGISTRO NO ARQUIVOS
      * DE DUPLICATAS VALIDAS. CASO CONTRARIO, GRAVA O REGISTRO NO AR-
      * QUIVO DE DUPLICATAS INVALIDAS.
      *----------------------------------------------------------------*
       2-PROCESSA.

           ADD 1 TO WT-CT-LIDOS

           CALL WT-NM-VDAT USING CRA0206-DT-VENCIMENTO
                                "AAAAMMDD" 
                                WT-RC-VDAT

           IF  WT-RC-VDAT = ZEROS
               MOVE CRA0206-REGISTRO TO CRA0207V-REGISTRO
               WRITE CRA0207V-REGISTRO
               ADD 1 TO WT-CT-VALIDOS
           ELSE
               MOVE CRA0206-REGISTRO TO CRA0207I-REGISTRO
               MOVE WT-RC-VDAT TO CRA0207I-CD-ERRO
               WRITE CRA0207I-REGISTRO
               ADD 1 TO WT-CT-INVALIDOS
           END-IF

           READ CRA0206.

Vale a pena prestar atenção em alguns detalhes:

  1. O comando CALL chama a subrotina XXRVDAT. O nome da subrotina está na variável WT-NM-VDAT que criamos na WORKING. Esse recurso garante que a subrotina será carregada dinamicamente em tempo de execução.
  2. Estamos passando o próprio campo do arquivo para ser validado pela subrotina (CRA0206-DT-VENCIMENTO). Como não informamos nada em contrário, o COBOL passará apenas o endereço dessa variável para a subrotina (BY REFERENCE).
  3. O segundo argumento que passamos para a subrotina é o formato da data. Sabemos que as datas do nosso arquivo de duplicatas estão no formato AAAAMMDD (quatro dígitos para o ano, dois dígitos para o mês e dois dígitos para o dia). Para informar que o formato é esse usamos um literal alfanumérico simples.
  4. O código de retorno da subrotina será armazenado na variável WT-RC-VDAT, que também criamos na WORKING e informamos como terceiro argumento do comando CALL. Como todos os argumentos estão sendo passados by reference (esse é o default) a subrotina receberá o endereço dessa variável e vai atualizá-la com o código de retorno diretamente em memória.
  5. Para decidir se o registro será gravado no arquivo de válidos ou no arquivo de inválidos, testamos se o código de retorno é zero. Qualquer outro valor significa que a data de vencimento da duplicata é inválida.
  6. Desta vez não fizemos a movimentação campo a campo do registro de entrada para o registro de saída. Como os registros são do mesmo tamanho, movimentamos o registro diretamente (nível 01 do arquivo de entrada para nível 01 do arquivo de saída)
  7. O registro de duplicatas inválidas (arquivo CRA0207I) é três bytes maior que o registro de entrada (os dois bytes onde salvaremos o código de retorno da subrotina). Apesar disso, podemos fazer a movimentação de registro a registro sem problemas: os dois últimos bytes ficarão com “brancos” mas logo em seguida preenchemos esse campo com o código de retorno.

No parágrafo TERMINA colocaremos os comandos para fechamento dos arquivos e exibirá os contadores.

O comando CANCEL, que destacamos abaixo, trabalha diretamente com o comando CALL. Ele serve para garantir que a subrotina será retirada de memória antes que nosso programa principal termine. Essa medida funciona mais ou menos como um garbage collection evitando eventuais vazamentos de memória que poderiam afetar o desempenho do sistema.

O uso desse comando depende basicamente do compilador que é usado na instalação. Alguns são mais eficientes do que outros nesse processo de garbage collection.

      *----------------------------------------------------------------*
      * FECHA ARQUIVOS E EXIBE OS CONTADORES DE REGISTROS LIDOS, VALI-
      * DOS E INVALIDOS
      *----------------------------------------------------------------*
       3-TERMINO.

           CANCEL WT-NM-VDAT

           CLOSE CRA0206 CRA0207V CRA0207I

           DISPLAY "LIDOS=" WT-CT-LIDOS
                   " VALIDOS= " WT-CT-VALIDOS
                   " INVALIDOS=" WT-CT-INVALIDOS.

 

Construindo subrotinas

Uma subrotina não é muito diferente de um programa principal. Para destacar as diferenças vamos analisar uma subrotina bem simples e muito comum em muitas empresas.

Para evitar que cada programa (e cada programador) exiba mensagens de erro do jeito que quiser, a maioria das empresas usa uma subrotina para padronizar essas mensagens. O programa XXRMSGE, que mostramos a seguir, é um exemplo desse tipo de subrotina.

A primeira diferença que notamos está na cláusula PROGRAM-ID da IDENTIFICATION DIVISION. A opção INITIAL após o nome do programa faz com que suas variáveis de trabalho sejam iniciadas toda vez que a subrotina for chamada. A menos que se queira aproveitar resultados de execuções anteriores, o uso da opção INITIAL na PROGRAM-ID é altamente recomendável para subrotinas.

      *================================================================*
       IDENTIFICATION DIVISION.
      *----------------------------------------------------------------*
       PROGRAM-ID.    XXRMSGE INITIAL.
       AUTHOR.        PAULO ANDRE DIAS.
       DATE-WRITTEN.  05/01/2017.
       REMARKS.
      *----------------------------------------------------------------*
      * SISTEMA:      XX   - SUBROTINAS DE APOIO
      * SUBROTINA:    MSGE - PADRONIZACAO DE MENSAGENS DE ERRO
      *
      * OBJETIVO:     RECEBE NOME DO PROGRAMA, NOME DO ARQUIVO, MNE-
      *               MONICO DA OPERACAO REALIZADA E O FILE STATUS
      *               RESULTANTE DA OPERACAO.
      *
      * VERSOES:      DATA    DESCRICAO
      *               ------  ---------------------------------------
      *               XXXXXX  XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
      *
      *----------------------------------------------------------------*

Como essa subrotina não trabalha com arquivos, podemos omitir toda a ENVIRONMENT DIVISION. Pelo mesmo motivo, a DATA DIVISION não terá FILE SECTION. Sua primeira seção será a WORKING, onde definiremos algumas variáveis para exibir a mensagem de erro:

      *================================================================*
       DATA DIVISION.
      *----------------------------------------------------------------*
       WORKING-STORAGE SECTION.
      *----------------------------------------------------------------*
       01 WT-LABELS.
           03 WT-LB-SEPARADOR  PIC  X(030) VALUE ALL "*".
           03 WT-LB-PROGRAMA   PIC  X(010) VALUE "PROGRAMA: ".
           03 WT-LB-ARQUIVO    PIC  X(010) VALUE "ARQUIVO:  ".
           03 WT-LB-OPERACAO   PIC  X(010) VALUE "OPERACAO: ".
           03 WT-LB-ERRO       PIC  X(010) VALUE "ERRO:     ".

Logo depois da WORKING-STORAGE temos uma seção da DATA DIVISION específica para programas que recebem parâmetros: a LINKAGE SECTION.

Nesta seção, criamos as variáveis que receberão os argumentos informados pelos programas chamadores e que serão atualizadas com os retornos gerados pelo subprograma. As regras para declaração dessas variáveis são as mesmas que usamos na FILE SECTION e na WORKING-STORAGE SECTION.

Variáveis de LINKAGE não possuem cláusula VALUE porque espera-se que seus valores sejam sempre informados pelo programa chamador.

      *----------------------------------------------------------------*
       LINKAGE SECTION.
      *----------------------------------------------------------------*
       01 LK-NM-PROGRAMA       PIC  X(008).
       01 LK-NM-ARQUIVO        PIC  X(008).
       01 LK-CD-OPERACAO       PIC  X(003).
       01 LK-ST-ERRO           PIC  X(002).

Todas as variáveis que foram declaradas na LINKAGE precisam ser mencionadas na opção USING da PROCEDURE DIVISION. É essa opção que efetivamente recebe os argumentos informados pelos programas chamadores.

A ordem dos campos na cláusula USING do comando CALL deve ser igual à ordem desses campos na opção USING da PROCEDURE DIVISION. O nome das variáveis, no entanto, não precisam ser iguais.

      *================================================================*
       PROCEDURE DIVISION USING LK-NM-PROGRAMA
                                LK-NM-ARQUIVO
                                LK-CD-OPERACAO
                                LK-ST-ERRO.
      *----------------------------------------------------------------*

Assim como no comando CALL, aqui também poderíamos ter declarado se cada argumento será recebido by reference (default), by content ou by value. Por exemplo, poderíamos ter declarado assim nossa PROCEDURE DIVISION:

       PROCEDURE DIVISION USING BY VALUE LK-NM-PROGRAMA
                                BY VALUE LK-NM-ARQUIVO
                                BY VALUE LK-CD-OPERACAO
                                BY REFERENCE LK-ST-ERRO.

Nesse caso, o subprograma receberia uma “cópia” em memória dos três primeiros argumentos e um endereço de memória para o quarto argumento. Na prática, tanto o programa principal quanto a subrotina estariam trabalhando com a mesma variável LK-ST-ERRO em memória.

Nosso subprograma só terá um parágrafo para exibir os argumentos fornecidos. A última grande diferença entre programas e subrotinas está no comando de encerramento: em subrotinas usamos GOBACK ao invés de STOP RUN, para retornar ao programa chamador.

      0-PRINCIPAL.

           DISPLAY WT-LB-SEPARADOR     
           DISPLAY WT-LB-PROGRAMA LK-NM-PROGRAMA
           DISPLAY WT-LB-ARQUIVO  LK-NM-ARQUIVO
           DISPLAY WT-LB-OPERACAO LK-CD-OPERACAO
           DISPLAY WT-LB-ERRO     LK-ST-ERRO
           DISPLAY WT-LB-SEPARADOR

           GOBACK.

Para testar essa subrotina, podemos codificar um programa mais simples ainda:

       IDENTIFICATION DIVISION.
       PROGRAM-ID.    TSTMSGE.
       REMARKS.       TESTAR SUBROTINA XXRMSGE.

       PROCEDURE DIVISION.
       PRINCIPAL.

           CALL "XXRMSGE" USING "CRP0205"
                                "CRA0205"
                                "RD"
                                "35".

           CANCEL "XXRMSGE"

           STOP RUN.

Repare que esse programa não precisou nem de ENVIRONMENT nem de DATA DIVISION, uma vez que não acessa arquivos convencionais nem usa variáveis de trabalho. A única coisa que esse programa faz é chamar a subrotina passando alguns argumentos hipotéticos para verificar seu funcionamento.

Ao executar esse programa, nossa subrotina será chamada e exibirá a seguinte mensagem:

******************************
PROGRAMA: CRP0205
ARQUIVO:  CRA0205
OPERACAO: RD
ERRO:     35
******************************

[1] Ou by value, dependendo do compilador.


Anterior Conteúdo Próxima