Direitos Autorais IBM Corporation 1987, 2004. Todos os Direitos Reservados.

A palavra "Rational" e os produtos Rational são marcas registradas da Rational Software Corporation. As referências a outras empresas e seus produtos utilizam marcas de propriedade de suas respectivas empresas e são apenas para fins de referência.

Conteúdo

Sobre Este Documento

Introdução

Princípios Fundamentais
Premissas
Classificação das Diretrizes
A Primeira e a Última Diretriz

Layout de Código

Geral
Tipo de Letra
Recuo
Comprimento da Linha e Quebras de Linha
Alinhamentos

Comentários

Geral
Diretrizes para o Uso de Comentários

Convenções de Nomenclatura

Geral
Pacotes
Tipos
Exceções
Subprogramas
Objetos e Parâmetros do Subprograma (ou Entrada)
Unidades Genéricas
Estratégias de Nomenclatura para Subsistemas

Declarações de Tipos, Objetos e Unidades do Programa

Tipos de Enumeração
Tipos Numéricos
Tipos Reais
Tipos de Registro
Tipos de Acesso
Tipos Privados
Tipos Derivados
Declarações de Objetos
Subprogramas e Unidades Genéricas

Expressões e Instruções

Expressões
Instruções
Dicas de Codificação

Problemas de Visibilidade

Sobrecarga e Homógrafos
Cláusulas de Contexto
Renomear
Observação sobre as Cláusulas de Uso

Estrutura do Programa e Problemas de Compilação

Decomposição de Pacotes
Estrutura de Partes Declarativas
Cláusulas de Contexto
Pedido de Elaboração

Simultaneidade

Manipulação de Erros e Exceções

Programação de Baixo Nível

Cláusulas de Representação e Atributos
Conversões Não Verificadas

Resumo

Referências

Glossário


Capítulo 1

Sobre Este Documento

Esse documento Rational Unified Process - Diretrizes de Programação Ada é um gabarito que pode ser utilizado para derivar um padrão de codificação para sua própria organização. Ele especifica como os programas Ada devem ser gravados. Seu público alvo inclui todos os designers de software aplicativo e desenvolvedores que utilizam o Ada como o idioma de implementação ou como um idioma de design para especificar as interfaces ou as estruturas de dados, por exemplo.

As regras descritas nesse documento abrangem a maioria dos aspectos da codificação. As regras gerais se aplicam ao layout do programa, convenções de nomenclatura e uso dos comentários. As regras específicas se aplicam aos recursos Ada selecionados e especificam construções proibidas, padrões de uso recomendados e dicas gerais para aprimorar a qualidade do programa.

Há um determinado grau de sobreposição entre as diretrizes de design do projeto e as diretrizes de programação presentes e isso é intencional. Várias regras de codificação, especialmente na área das convenções de nomenclatura, foram introduzidas para suportar ativamente e reforçar uma abordagem orientada ao objeto para o design do software.

As diretrizes foram escritas originalmente para Ada 83. Elas incluem as regras de compatibilidade com Ada 95, mas não com diretrizes específicas para uso do novos recursos do idioma introduzido no padrão de idioma revisado, como os tipos marcados, unidades filhas e tipos decimais.

A organização do documento segue abertamente a estrutura do Manual de Referência Ada [ISO 8052].

O Capítulo 2, Introdução, explica os princípios fundamentais nos quais as diretrizes são baseadas e apresenta uma classificação das diretrizes.

O Capítulo 3, Layout do Código, trata da organização visual geral do texto dos programas.

O Capítulo 4, Comentários, fornece orientação sobre como utilizar os comentários para documentar o código de uma forma estruturada, útil e preservável.

O Capítulo 5, Convenções de Nomenclatura, fornece algumas regras gerais sobre as entidades do idioma de nomenclatura e os exemplos. Esse capítulo deve ser ajustado para se adequar às necessidades do seu projeto específico ou organização.

O Capítulo 6, Declarações, e o Capítulo 7, Expressão e Instruções, fornecem avisos adicionais sobre cada tipo de construção de idioma.

O Capítulo 8, Problemas de Visibilidade e o Capítulo 9, Estrutura do Programa e Problemas de Compilação, fornecem a orientação sobre a estruturação global e a organização dos programas.

O Capítulo 10, Simultaneidade, trata do tópico especializado quanto à utilização de tarefas e recursos relacionados ao tempo da linguagem.

O Capítulo 11, Tratamento de Erro e Exceções fornece alguma orientação sobre como utilizar ou não utilizar a exceção para tratar dos erros de uma forma sistemática e reduzida.

O Capítulo 12, Programação de Baixo Nível, trata dos problemas das cláusulas de representação.

O Capítulo 13, Resumo, recapitula as diretrizes mais importantes.

Esse documento substitui Diretrizes Ada: Recomendações para os Designers e Programadores, Nota do Aplicativo Nº15, Rational, Santa Clara, CA., 1990.


Capítulo 2

Introdução

Princípios Fundamentais

O Ada foi explicitamente projetado para suportar o desenvolvimento do software de alta qualidade, confiável, reutilizável e portátil [ISO 87, seção. 1.3]. No entanto, nenhuma linguagem de programação de sua propriedade pode assegurar que isso seja alcançado. A programação precisa ser feita como parte de um processo bem disciplinado.

Limpo, o código fonte Ada compreensível é o principal objetivo da maioria das diretrizes fornecidas aqui. Esse é um fator principal de contribuição para a confiabilidade e a sustentabilidade. O que entende-se por código compreensível e limpo pode ser capturado nos três seguintes princípios fundamentais.

Surpresa Mínima

Superior a sua existência, o código fonte é lido com mais freqüência do que é escrito, especialmente as especificações. Em condições ideais, o código deve ser lido como uma descrição de idioma inglês daquilo que está sendo feito, com o benefício adicional que ele executa. Os programas são mais escritos para as pessoas do que para os computadores. O código de leitura é um processo mental complexo que pode ser suportado pela uniformidade, também conhecido nesse guia como princípio da surpresa mínima. Um estilo uniforme em um projeto inteiro é uma razão principal para que uma equipe de desenvolvedores de software concorde com os padrões de programação e não deve ser percebido como algum tipo de punição ou como um obstáculo à criatividade e à produtividade.

Ponto Único de Manutenção

Outro importante princípio sustentado nesse guia é o princípio do ponto-único-de-manutenção. Sempre que possível, uma decisão de design deve ser expressa em apenas um ponto na origem Ada e a maioria das conseqüências deve ser derivada programaticamente a partir desse ponto. As violações desse princípio representam grande risco à manutenção e à confiança, bem como ao entendimento.

Ruído Mínimo

Finalmente, como uma contribuição maior à legibilidade, o princípio de ruído mínimo foi aplicado. Ou seja, foi feito um esforço para evitar a confusão do código fonte com o "ruído" visual: barras, caixas e outros textos com conteúdo de poucas informações ou informações que não contribuam para o entendimento do propósito do software.

A portabilidade e a possibilidade de reutilização também são razões para várias diretrizes. O código terá que ser portado para diferentes compiladores por diferentes computadores de destino e eventualmente para uma versão mais avançada do Ada, denominada "Ada 95" [PLO92, TAY92].

Premissas

As diretrizes apresentadas aqui criam um pequeno número de premissas básicas:

O leitor conhece Ada.

O uso de recursos avançados Ada é encorajado sempre que for benéfico, em vez de ser desencorajado na base, na qual alguns programadores não estão familiarizados. Essa é a única maneira na qual o projeto pode realmente se beneficiar do uso do Ada. O Ada deve ser utilizado como se fosse Pascal ou FORTRAN. A paráfrase do código em comentários é desencorajado; caso contrário, o Ada deve ser utilizado no lugar dos comentários, sempre que viável.

O leitor conhece o inglês.

Várias das convenções de nomenclatura são baseadas no inglês, tanto no vocabulário quanto na sintaxe. Além disso, as palavras-chave Ada são palavras comuns em inglês e misturá-las com outros idiomas dificulta a legibilidade.

O uso das cláusulas use é altamente restrito.

As convenções de nomenclatura e algumas outras regras assumem que as cláusulas "use" não são utilizadas.

Um projeto bem grande está sendo tratado.

Várias regras oferecem maior valor em grandes sistemas Ada, embora eles também possam ser utilizados em um pequeno sistema, se destinados apenas à prática e à uniformidade no nível de projeto ou corporativo.

O código fonte está sendo desenvolvido no Ambiente Rational.

Ao utilizar o Ambiente Rational, problemas como o layout do código, os identificadores no fechamento das construções e assim por diante são tratados pelo editor e formatador Ada. No entanto, as recomendações de layout contidas nesse documento podem ser aplicadas em qualquer plataforma de desenvolvimento.

A codificação segue um design orientado ao objeto

Várias regras suportarão um mapeamento sistemático dos conceitos orientados ao objeto (OO) para os recursos Ada e convenções de nomenclatura específicas.

Classificação da Diretriz

Essas diretrizes não são de igual importância. Elas seguem a grosso modo essa escala:

Dica:Ícone de Dedo Indicador

A diretriz é uma parte simples do aviso; não há prejuízo real feito por não segui-la e ela pode ser selecionada ou rejeitada por uma questão de opinião. As dicas são marcadas nesse documento com o símbolo acima.

Recomendação:Ícone de Ok

A diretriz geralmente é baseada em fundamentos mais técnicos; a portabilidade ou a capacidade de reutilização pode ser afetada, bem como o desempenho em algumas implementações. As recomendações devem ser seguidas, a menos que haja um bom motivo para não segui-las. Algumas exceções são mencionadas nesse documento. As recomendações são marcadas nesse documento pelo símbolo acima.

Restrição:Ícone de Atenção com a Mão

O recurso em questão é perigoso de ser utilizado, mas não é completamente descartado; a decisão de utilizá-lo deve ser uma decisão no nível de projeto e ela deve ficar altamente visível. As restrições são marcadas nesse documento pelo símbolo apresentado acima.

Requisito:Ícone de Dedo Indicador

Uma violação conduziria definitivamente ao código inválido, não confiável ou não portátil. Os requisitos não podem ser violados. Os requisitos são marcados nesse documento com a mão indicadora acima.

O Recurso de Design Rational será utilizado para sinalizar o uso de recursos restritos e reforçar as regras requeridas e várias das recomendações.

Em oposição a vários outros padrões de codificação Ada, muito poucos recursos Ada são realmente descartados por inteiro nessas diretrizes. A chave para o bom software reside em:

A Primeira e a Última Diretriz

Ícone de Dedo IndicadorUtilize o senso comum.

Quando não for possível localizar uma regra ou diretriz, quando a regra não for aplicada de maneira óbvia ou quando todo o resto falhar: utilize o senso comum e verifique os princípios fundamentais. Essa regra substitui todas as outras. O senso comum é requerido.


Capítulo 3

Layout do Código

Geral

O layout de uma unidade de programa está sob o controle completo do Formatador de Ambiente Rational e o programador não terá que se preocupar muito com o layout de um programa, exceto nos comentários e no espaço em branco. As convenções de formatação adotadas por essa ferramenta são as expressas no Apêndice E do Manual de Referência para a Linguagem de Programação Ada [ISO87]. Em específico, sugere-se que as palavras-chave iniciadas e encerradas com uma construção estruturada sejam verticalmente alinhadas. Além disso, o identificador de uma construção é sistematicamente repetido ao final da construção.

O comportamento preciso do formatador é controlado por uma série de comutações de biblioteca que recebem um conjunto uniforme de valores em todo o projeto, com base em um mundo de modelos comuns. As comutações relevantes são listadas abaixo com seus valores reais para o mundo de modelos que recomendamos.

Tipo de Letra

Formato . Id_Case : Letter_Case := Primeira em Maiúscula

Especifica o tipo dos identificadores em unidades Ada: a primeira letra e cada primeira letra após um sublinhado ficam em maiúsculas. O formulário em maiúsculas é reconhecido como o mais legível pelos leitores humanos, com a tela mais moderna e fontes de impressora laser.

Formato . Keyword_Case : Letter_Case := Lower

Especifica o tipo de letra das palavras-chave Ada. Isso os distingue ligeiramente dos identificadores.

Formato . Number_Case : Letter_Case := Upper

Especifica o tipo da letra "E" nos literais de ponto flutuante e dígitos baseados ("A" até "F") em literais baseados.

Recuo

Uma unidade Ada é formatada de acordo com as convenções gerais expressas no Apêndice E do Manual de Referência Ada [ISO87]. Isso significa que as palavras-chave que iniciadas e encerradas com uma construção estruturada estão alinhadas. Por exemplo, "loop" e "end loop", "record" e "end record". Os elementos que estão dentro de construções estruturadas são recuados para à direita.

Formato . Major_Indentation : Indent_Range := 3

Especifica o número de colunas que o formatador recua nas construções estruturadas (principais) como instruções "if", instruções "case" e instruções "loop".

Formato . Minor_Indentation : Indent_Range := 2

Especifica o número de colunas que o formatador recua nas construções menores: declarações de registro, declarações de registro variáveis, declarações de tipo, rotinas de tratamento de exceção, alternativas, instruções de caso e instruções nomeadas e identificadas.

Comprimento da Linha e Quebras de Linha

Formato . Line_Length : Line_Range := 80

Especifica o número de colunas utilizadas pelo formatador para as linhas de impressão em unidades Ada antes de agrupá-las. Isso permite a exibição das unidades formatadas com o VT100 tradicional como terminais.

Formato . Statement_Indentation : Indent_Range := 3

Especifica o número de colunas que o formatador recua na segunda linha e nas linhas subseqüentes de uma instrução quando a instrução tiver que ser quebrada porque é superior a Line_Length. O formatador recua o número de colunas do Statement_Indentation apenas se não houver construção léxica com a qual o código recuado pode ser alinhado.

Formato . Statement_Length : Line_Range := 35

Especifica o número de colunas reservadas em cada linha para exibir uma instrução. Se o nível atual de recuo permitir menos de Statement_Length colunas em uma linha, o formatador será reiniciado com a coluna Wrap_Indentation como seu novo nível de recuo. Essa prática evita que as instruções profundamente aninhadas sejam impressas além da margem direita.

Formato . Wrap_Indentation : Line_Range := 16

Especifica a coluna na qual o formatador inicia o próximo nível de recuo quando o nível atual de recuo não permitir Statement_Length. Essa prática evita que as instruções profundamente aninhadas sejam impressas além da margem direita.

Alinhamentos

Formato . Consistent_Breaking : Integer := 1

Controla a formatação das listas do formulário (xxx:aaa; yyy:bbb), que aparecem nas partes formais do subprograma e como discriminantes nas declarações de tipo. Também controla a formatação das listas do formulário (xxx=>aaa, yyy=>bbb), que aparecem nas chamadas do subprograma e nos agregados. Como essa opção é diferente de (True), quando uma lista não se encaixar em uma linha, cada elemento da lista começará em uma nova linha.

Formato . Alignment_Threshold : Line_Range := 20

Especifica o número de espaços em branco que o formatador pode inserir para alinhar as construções léxicas em instruções consecutivas, como dois pontos, designações e setas na notação nomeada. Se um número superior a esse de espaço for necessário para alinhar uma construção, a construção ficará desalinhada.

Ícone de Dedo IndicadorObserve que para forçar um determinado layout, o programador pode inserir um fim-de-linha ou uma quebra de linha que não será removida pelo formatador, digitando <space> <space> <carriage-return>.

Ícone de OkUtilizando essa técnica e para aprimorar a legibilidade e a sustentabilidade, as linhas de elementos Ada devem ser quebradas para conter apenas um elemento por linha, quando a lista exceder 3 itens e quando eles não se encaixarem em uma linha. Em específico, isso se aplica às seguintes construções Ada (conforme definido no Apêndice E do Manual de Referência Ada [ISO87]):

associação de argumento

pragma Suppress (Range_Check,
                 On => This_Type,
                 On => That_Type,                 On => That_Other_Type);      

lista de identificadores, lista de componentes

Next_Position,
Previous_Position,
Current_Position : Position;
type Some_Record is 
    record
        A_Component,
        B_Component,
        C_Component : Component_Type;
    end record;      

definição do tipo de enumeração

type Navaid is 
       (Vor, 
        Vor_Dme, 
        Dme, 
        Tacan, 
        VorTac,
        NDB);      

restrição discriminante

subtype Constrained is Element 
        (Name_Length    => Name'Length,
         Valid          => True,
         Operation      => Skip);      

seqüências de instruções (feitas pelo formatador)

parte formal, parte formal genérica, parte real do parâmetro, parte real genérica do parâmetro

procedure Just_Do_It (This     : in Some_Type;
                      For_That : in Some Other_Type;
                      Status   : out Status_Type);
Just_Do_It (This     => This_Value;
            For_That => That_Value;
            Status   => The_Status);      

Capítulo 4

Comentários

Geral

Contrário a uma crença amplamente retida, os bons programas não são caracterizados pelo número de comentários, mas por suas qualidades.

Os comentários devem ser utilizados para complementar o código Ada, nunca o parafraseie. O Ada em si é uma linguagem de programação bem legível-ainda mais quando suportada por boas convenções de nomenclatura. Os comentários devem complementar o código Ada, explicando aquilo que não é óbvio; eles não devem duplicar a sintaxe Ada ou as semânticas. Os comentários devem ajudar o leitor a captar os conceitos de segundo plano, as dependências e as codificações de dados ou algoritmos especialmente complexos. Os comentários devem realçar os desvios dos padrões de codificação ou de design, utilizar os recursos restritos e "truques" especiais. Os quadros de comentários, ou formulários, que aparecem sistematicamente para cada principal construção Ada (como subprogramas e pacotes) terão o benefício da uniformidade e do restante do programador para documentar o código, mas geralmente conduzem a um estilo de paráfrase. Para cada comentário, o programador deve ser capaz de responder bem à pergunta: "Qual Valor é Agregado Por Esse Comentário?"

Um comentário mal interpretado ou incorreto é pior que nenhum comentário. Os comentários não são verificados pelo compilador (a menos que participem em algum ADL (Ada Design Language) formal ou PDL (Program Design Language), como com o Recurso de Design Rational) não são verificados pelo compilador. Portanto, de acordo com o princípio de ponto-único-de-manutenção, as decisões de design devem ser expressas no Ada em vez de serem expressas em comentários, mesmo nos gastos de algumas declarações a mais.

Como um exemplo (não tão bom), considere a seguinte declaração:

------------------------------------------------------------
-- procedure Create
------------------------------------------------------------
--
   procedure Create
              (The_Subscriber: in out Subscriber.Handle;
               With_Name     : in out Subscriber.Name);
--
-- Propósito: Esse procedimento cria um assinante em um determinado
-- nome.
--
-- Parâmetros:
-     The_Subscriber    :mode in out, type Subscriber.Handle
-               É o manuseio para o assinante criado
-     With_Name         :mode in, type Subscriber.Name
-               O nome do assinante a ser criado.
-               A sintaxe do nome é
--                 <letter> { <letter> | <digit> }
-- Exceções:
--    Subscriber.Collection_Overflow em que não há mais
--    espaço para criar um novo assinante
--    Subscriber.Invalid_Name quando o nome estiver em branco ou
--    mal formado
--
-------------------------------------------- end Create ----      

Diversos pontos podem ser estabelecidos sobre esse exemplo.

- Criação de Procedimento: Se o nome precisar ser alterado, há vários locais para alterá-lo; as alterações consistentes ao comentário não serão reforçadas pelo compilador.
- Os parâmetros, com seus nomes, modos e tipos, não precisam ser repetidos nos comentários.
- Bons nomes escolhidos por cada entidade Ada envolvida aqui tornam as explicações de parâmetro e de propósitos redundantes. Observe que isso é válido para um subprograma simples, conforme mostrado abaixo. Um subprograma mais complexo ainda requer a explicação de propósitos e parâmetros.

Nesse caso, são preferidas as seguintes versões úteis e mais concisas:

procedure Create (The_Subscriber : in out Subscriber.Handle;
                  With_Name      : in    Subscriber.Name);--
--Levanta Subscriber.Collection_Overflow.
--Levanta Subscriber.Invalid_Name quando o nome estiver
--em branco ou mal formado (consulte a descrição da sintaxe
--anexada à declaração do tipo Subscriber.Name).      

Diretrizes para o Uso de Comentários

Ícone de OkOs comentários devem ser colocados próximos do código aos quais estão associados, com o mesmo recuo e serão anexados nesse código-ou seja, com linhas de comentário em branco que se vinculam visualmente ao bloco de comentários para a construção Ada:

procedure First_One;
--
-- Esse comentário está relacionado ao First_One.
-- Mas esse comentário destina-se ao Second_One.
-- 
procedure Second_One (Times : Natural);      

Ícone de OkUtilize as linhas em branco para separar os blocos relacionados de código fonte (comentários e código) em vez das pesadas linhas de comentário.

Ícone de OkUtilize comentários vazios, em vez de linhas vazias, dentro de um único bloco de comentário para separar os parágrafos:

-- Alguma explicação aqui que precise ser continuada em um
-- parágrafo subseqüente.
--
-- A linha de comentário vazia acima deixa claro que estamos
-- tratando de um único bloco de comentário. 

Ícone de OkEmbora os comentários possam ser colocados acima ou abaixo da(s) construção(ões) Ada para as quais estão relacionadas, coloque os comentários como um título de seção ou uma parte maior de informações que se aplica a diversas construções Ada acima da(s) construção(ões). Coloque os comentários que sejam observações ou informações adicionais abaixo da construção Ada à qual se aplicam.

Ícone de OkAgrupe os comentários no início da construção Ada, utilizando a largura inteira da página. Evite comentários na mesma lista de uma construção Ada. Esses comentários geralmente ficam desalinhados. No entanto, tais comentários são tolerados nas descrições de cada elemento em longas declarações, como literais de tipo de enumeração.

Ícone de OkUtilize uma pequena hierarquia de blocos padrão de comentários para os títulos de seção, mas apenas em unidades Ada muito grandes (>200 declarações ou instruções):

--===========================================================
--
--               TÍTULO PRINCIPAL AQUI
--
--===========================================================


-------------------------------------------------------------
--               Título Secundário Aqui
-------------------------------------------------------------


--             --------------------
--               Cabeçalho da Subseção
--             --------------------      

Coloque mais linhas em branco acima desses comentários de título do que abaixo-por exemplo, duas linhas antes e uma linha depois. Isso associa visualmente o título ao seguinte texto.

Ícone de OkEvite o uso de cabeçalhos que contenham informações como o autor, os números de telefone, as datas de criação e modificação e o local da unidade (ou o nome do arquivo), porque essas informações ficam rapidamente obsoletas. Coloque os avisos de direitos autorais à propriedade no fim da unidade, especialmente ao utilizar o Ambiente Rational. Ao acessar a origem de uma especificação de pacote (pressionando [Definição] no Ambiente Rational, por exemplo), o usuário não deseja ter que se deslocar através de duas ou três páginas de texto que não sejam úteis para o entendimento do programa e/ou o texto que não carrega qualquer informação do programa, como um aviso de direitos autorais. Evite a utilização de barras verticais ou caixas ou quadros fechados, que apenas adicionam ruído visual e são difíceis de se manter consistentes. Utilize as notas do Rational CMVC (ou alguma outra forma de arquivos de desenvolvimento de software) para manter o histórico da unidade.

Ícone de OkNão replique as informações normalmente encontradas em todos os lugares; forneça um ponteiro para as informações.

Ícone de OkUtilize o Ada sempre que possível, em vez de um comentário. Para isso, você pode utilizar nomes melhores, variáveis temporárias extra, qualificação, renomeação, subtipos, expressões estáticas e atributos, todos que não afetam o código gerado (pelo menos, com um bom compilador). Você também pode utilizar funções menores de predicados seqüenciais e dividir o código em diversos procedimentos sem parâmetros, cujos nomes fornecem títulos para diversas seções discretas do código.

Exemplos:

Substitua:

exit when Su.Locate (Ch, Str) /= 0; 
-- Sair do loop de procura ao localizá-lo.      

Search_Loop : loop

Found_It := Su.Locate (Ch, Str) /= 0;

exit Search_Loop when Found_It

end Search_Loop;

Substitua:

if Value < 'A' or else Value > 'Z' then 
-- Se não for letras maiúsculas.      

subtype Uppercase_Letters is Character range 'A' .. 'Z';
if Value not in Uppercase_Letters then ...      

Substitua:

X := Green;         -- Isso é Verde no
                    -- Status, não na Cor.
raise Fatal_Error;  -- Do pacote Outer_Scope.
delay 384.0;        -- Igual a 6 minutos e 24
                    -- segundos.      

The_Status := Green;      

X := Status'(Green);
raise Outer_Scope.Fatal_Error;
delay 6.0 * Minute + 24.0 * Second;      

Substitua:

if Is_Valid (The_Table (Index).Descriptor(Rank).all) then
-- Esse é o valor atual para iteração; se for
-- válido, anexamos à lista que contém.
   Append (Item,           To_List => The_Table (Index).Descriptor(Rank).Ptr);|      

declare
    Current_Rank : Lists.List renames The_Table 
                    (Index).Descriptor (Rank);
begin
    if Is_Valid (Current_Rank.all) then
        Append (Item, To_List => Current_Rank.Ptr);
    end if;
end;      

Ícone de OkCuide do estilo, sintaxe e ortografia nos comentários. Não utilize um estilo telegráfico criptografado. Utilize o corretor ortográfico. (No Ambiente Rational chame Speller.Check_Image).

Ícone de Dedo IndicadorNão utilize letras acentuadas ou outros caracteres diferentes do inglês. Os caracteres diferentes do inglês podem ser suportados em alguns sistemas de desenvolvimento e em alguns compiladores Ada apenas nos comentários, de acordo com o Problema Ada AI-339. Mas isso não é portátil e provavelmente falhará em outros sistemas.

Ícone de OkPara os subprogramas, documente no mínimo:

Ícone de OkPara os tipos e os objetos, documente qualquer constante ou restrições adicionais que não possam ser expressas no Ada.

Ícone de Dedo IndicadorEvite repetições nos comentários. Por exemplo, a seção de propósito deve ser uma breve resposta à pergunta "o que isso faz?" e não a "como isso é feito?" A visão geral deve ser uma breve apresentação do design. A descrição não deve descrever os algoritmos utilizados, mas, em vez disso, deve explicar como o pacote deve ser utilizado.

O Data_Structure e a seção de algoritmo devem conter informações suficientes para ajudar a entender a estratégia principal de implementação (para que o pacote possa ser utilizado corretamente), mas não tenha que fornecer os detalhes de implementação ou as informações que não sejam relevantes para o uso apropriado desse pacote.


Capítulo 5

Convenções de Nomenclatura

Geral

A escolha de bons nomes para designar entidades Ada (unidades do programa, tipos, subtipos, objetos, literais, exceções) é um dos problemas mais delicados a serem endereçados em todos os aplicativos de software. Em aplicativos médios-para-grandes, surge outro problema: conflitos nos nomes ou então a dificuldade na localização de sinônimos suficientes para designar a distinção, mas noções semelhantes sobre o mesmo conceito de mundo real (ou para nomear um tipo, subtipo, objeto e parâmetro). Aqui, a regra para não utilizar as cláusulas "use" (ou apenas em condições altamente restritas) pode ser explorada. Em várias situações, isso permitirá a redução de um nome e a reutilização das mesmas palavras descritivas sem risco de confusão.

Ícone de OkEscolha nomes limpos, legíveis e significativos.

Diferentes de várias outras linguagens de programação, o Ada não limita o comprimento dos identificadores para 6, 8 ou 15 caracteres. A velocidade da digitação não é uma justificativa aceitável para os nomes abreviados. Os identificadores de uma letra geralmente são uma indicação de uma opção fraca ou lentidão. Pode haver algumas exceções, como utilizar E para a base dos logaritmos naturais, Pi, ou uma porção de outros casos bem reconhecidos.

Ícones de OkSepare diversas palavras de um nome por um sublinhado:

Is_Name_Valid em vez de IsNameValid

Ícone de OkUtilize nomes completos em vez de abreviações.

Ícone de OkUtilize apenas abreviações aprovadas pelo projeto

Se forem utilizadas abreviações, elas devem ser bem comuns para o domínio de aplicativos (por exemplo, FFT para Fast Fourier Transform) ou elas devem ser extraídas de uma lista no nível do projeto de abreviações reconhecidas. Caso contrário, é muito provável que ocorram aqui abreviações semelhantes, mas não idênticas, introduzindo problemas e erros posteriormente (por exemplo, Track_Identification sendo abreviado Tr_Id, Trck_Id, Tr_Iden, Trid, Tid, Tr_Ident e assim por diante).

Ícone de OkUtilize sufixos moderadores que indiquem a categoria da construção Ada. Eles não aprimoraram a legibilidade.

Os sufixos por categoria de entidades Ada, como _Package para pacotes, _Error para exceções, _Type para o tipo, e _Param para parâmetros de subprograma geralmente não são muito eficazes para o processo de leitura e entendimento do código. Isso é ainda pior com sufixos como _Array, _Record e _Function. Tanto o compilador Ada como o leitor humano podem distinguir uma exceção de um subprograma pelo contexto: é óbvio que apenas um nome de exceção pode aparecer em uma instrução raise ou em uma rotina de tratamento de exceção. Esses sufixos são úteis nas seguintes situações limitadas:

Tipo formal genérico com sufixo _Constrained

Tipo de acesso com sufixo _Pointer ou outra forma de referência indireta: _Handle ou _Reference

Subprograma ocultando uma chamada de entrada que esteja potencialmente bloqueando _Or_Wait

Ícone de OkNomes expresso de modo que parecem bons do ponto de vista de uso.

Tente imaginar o contexto no qual uma entidade exportada será utilizada e escolha o nome desse ponto de vista. Uma entidade é declarada uma vez e utilizada várias vezes. Isso é especialmente verdadeiro para os nomes de subprogramas e seus parâmetros: as chamadas resultantes, que utilizam associações nomeadas, devem ser o mais próximo possível do idioma natural. Lembre-se de que a ausência das cláusulas use tornará compulsório o nome qualificado da maioria das entidades declaradas. Bons acordos devem ser localizados para os parâmetros genéricos formais, que podem ser utilizados mais na unidade genérica do que em seu lado cliente, mas definitivamente dão preferência a uma boa aparência no lado cliente para os parâmetros formais do subprograma.

Ícone de OkUtilize palavras em inglês e as soletre corretamente.

A mistura de idiomas (por exemplo, francês e inglês) dificulta a leitura do código e, às vezes, apresenta ambigüidades no significado dos identificadores. Como as palavras-chave Ada já estão em inglês, são requeridas palavras em inglês. A forma americana é a preferida, para que seja possível utilizar o verificador ortográfico interno do Ambiente Rational.

Ícone de Dedo IndicadorNão redefina qualquer entidade do pacote Padrão. Isso é absolutamente proibido.

Fazer isso conduz à confusão e a erros dramáticos. A regra poderia ser estendida a outras unidades de biblioteca predefinidas: Calendário, Sistema. E isso inclui o próprio identificador Padrão.

Ícone de OkEvite a redefinição dos identificadores de outros pacotes predefinidos, como Sistema ou Calendário.

Ícone de Dedo IndicadorNão utilize como identificadores: Wide_Character e Wide_String que serão introduzidos no pacote Padrão no Ada 95. Não introduza uma unidade de compilação denominada Ada.

Ícone de Dedo IndicadorNão utilize como identificadores as palavras: abstract, aliased,protected, requeue, tagged e until, que se tornarão palavras-chave no Ada 95.

Seguem algumas sugestões de nomenclatura de diversas entidades Ada. É assumido um estilo de design geralmente de "tipo de objeto". Consulte o Anexo A para futuras explicações.

Pacotes

Ícone de OkQuando um pacote apresentar alguma classe de objeto, forneça o nome da classe de objeto, geralmente um nome comum no formato singular, com o sufixo _Generic, se necessário (ou seja, se uma classe de parâmetro for definida). Utilize a forma plural apenas se os objetos sempre aparecerem em grupos. Por exemplo:

package Text is
package Line is
package Mailbox is
package Message is
package Attributes is
package Subscriber is
package List_Generic is      

Ícone de OkQuando um pacote especificar uma interface ou algum agrupamento de funcionalidade e não se relacionar a um objeto, expresse isso no nome:

package Low_Layer_Interface is
package Math_Definitions is      

Ícone de OkQuando o pacote "lógico" precisar ser expresso como vários pacotes, utilizando uma decomposição simples, utilize os sufixos extraídos de uma lista concordada no nível do projeto. Um pacote lógico de Caixa Postal, por exemplo, poderia ser implementado com:

package Mailbox_Definitions is
package Mailbox_Exceptions is
package Mailbox_Io is
package Mailbox_Utilities is
package Mailbox_Implementation is
package Mailbox_Main is      

Os outros sufixos aceitos são:

_Test_Support 
_Test_Main 
_Log 
_Hidden_Definitions 
_Maintenance 
_Debug      

Tipos

Ícone de OkEm um pacote que define uma classe de objeto, utilize:

type Object is ...      

 

quando as semânticas de cópia estiverem implícitas-ou seja, quando o tipo for instanciável e alguma forma de designação for viável. Observe que o nome da classe não deve ser repetido no identificador, porque sempre será utilizado na sua forma completa:

Mailbox.Object
Line.Object      

Ícone de Dedo IndicadorQuando as semânticas compartilhadas estiverem implícitas-ou seja, o tipo é implementado com os valores de acesso (ou alguma outra forma de via indireta), e a designação, se disponível, não copiar o objeto-indique esse fato utilizando:

 

Os elementos são utilizados como sufixos quando seu uso sozinho, com o prefixo do nome do pacote, não for claro ou for ambíguo.

Ícone de Dedo IndicadorQuando vários objetos estiverem implícitos, utilize um dos seguintes:

Ícone de Dedo IndicadorPara alguma designação de cadeia do projeto, utilize:

type Name

Ícone de OkO nome qualificado do tipo também deve ser utilizado em todo o pacote de definição, para melhor legibilidade. No Ambiente Rational, isso também conduz ao melhor comportamento ao utilizar a função [Complete] em uma chamada de subprograma.

Por exemplo, observe o nome completo Subscriber.Object abaixo

package Subscriber is
    type Object is private;
    type Handle is access Subscriber.Object;
    subtype Name is String;
    package List is new List_Generic (Subscriber.Handle);
    Master_List : Subscriber.List.Handle;
    procedure Create (The_Handle : out Subscriber.Handle;
                      With_Name  : in  Subscriber.Name);
    procedure Append (The_Subscriber : in     Subscriber.Handle;
                      To_List        : in out Subscriber.List.Handle);
    function Name_Of (The_Subscriber : Subscriber.Handle) return
            Subscriber.Name;
    ...
private
    type Object is
        record
            The_Name : Subscriber.Name (1..20);
                    ...
end Subscriber;    

Ícone de Dedo IndicadorEm outras circunstâncias, utilize os nomes ou qualificadores+nome para o nome de um tipo. Você pode utilizar a forma plural para o tipo, deixando o singular para os objetos (variáveis):

type Point is record ...
type Hidden_Attributes is ( ...
type Boxes is array ...    

Para os tipos de enumeração, utilize Mode, Kind, Code, e assim por diante, sozinho ou como um sufixo.

Para o tipos de matrizes, o sufixo _Table pode ser utilizado quando o nome simples já for utilizado para o tipo de componente. Utilize nomes ou sufixos como _Set e _List apenas quando a matriz for mantida com semânticas implícitas. Reserve _Vector e _Matrix para os conceitos matemáticos correspondentes.

Ícone de Dedo IndicadorComo os objetos de tarefa singulares serão evitados (por motivos explicados posteriormente), um tipo de tarefa deve ser introduzido mesmo quando houver apenas um objeto desse tipo. Isso ocorre quando uma estratégia de sufixo simples como _Type for satisfatória:

task type Listener_Type is ...
for Listener_Type'Storage_Size use ...
Listener : Listener_Type;    

Ícone de Dedo IndicadorDe forma semelhante, quando existirem conflitos entre o uso de um substantivo (ou frase substantiva) para o nome do tipo e em vários locais para o nome do objeto ou parâmetro, coloque o sufixo nesse substantivo com _Kind para o tipo e mantenha um substantivo simples para o objeto:

type Status_Kind is (None, Normal, Urgent, Red);
Status : Status_Kind := None;    

Ícone de Dedo IndicadorOu, para itens que sempre aparecem em múltiplos, utilize a forma plural para o tipo.

Ícone de OkComo os tipos de acesso possuem perigos inerentes, o usuário deve ficar ciente disso. Em geral, eles são chamados Ponteiro. Utilize o sufixo _pointer, se o nome sozinho for ambíguo. Como é possível um _Access alternado. ;

Ícone de Dedo IndicadorÀs vezes, utilizar um subpacote aninhado para apresentar uma abstração secundária simplifica a nomenclatura:

package Subscriber is    ...
    package Status is
        type Kind is (Ok, Deleted, Incomplete, Suspended, 
                      Privileged);
        function Set (The_Status    : Subscriber.Status.Kind;
                      To_Subscriber : Subscriber.Handle);
    end Status;
    ...    

Exceções

Ícone de OkComo as exceções devem ser utilizadas apenas para manipular as situações de erro, utilize um substantivo ou uma frase substantiva que transmita claramente uma idéia negativa:

Overflow, Threshold_Exceeded, Bad_Initial_Value    

Quando definido em um pacote de classe, é irrelevante para o identificador conter o nome da classe-por exemplo, Bad_Initial_Subscriber_Value-como a exceção será sempre utilizada como Subscriber.Bad_Initial_Value.

Ícone de OkUtilize uma dessas palavras: Bad, Incomplete, Invalid, Wrong, Missing ou Illegal como parte do nome em vez de utilizar sistematicamente Error, que não transmite as informações específicas:

Illegal_Data, Incomplete_Data    

Subprogramas

Ícone de OkUtilize os verbos para os procedimentos (e entradas de tarefas). Utilize os substantivos com os atributos ou as características da classe de objeto para as funções. Utilize os adjetivos (ou particípios passados) para as funções que retornam um Booleano (predicados). s

Subscriber.Create
Subscriber.Destroy
Subscriber.List.Append
Subscriber.First_Name          -- Retorna uma cadeia.
Subscriber.Creation_Date       -- Retorna uma data.
Subscriber.List.Next
Subscriber.Deleted             -- Retorna um Booleano.
Subscriber.Unavailable         -- Retorna um Booleano.
Subscriber.Remote    

Ícone de Dedo IndicadorPara os predicados, em alguns casos, pode ser útil incluir o prefixo Is_ ou Has_ antes de um substantivo; seja preciso e consistente em relação ao tempo gramatical:

function Has_First_Name ...
function Is_Administrator ...
function Is_First...
function Was_Deleted ...    

É útil quando o nome simples já for utilizado como um nome de tipo ou um literal de enumeração.

Utilize os predicados na forma afirmativa, ou seja, eles não podem conter "Not_".

Para as operações comuns, utilize consistentemente os verbos retirados de uma lista de opções do projeto (lista a ser expandida conforme adquirimos conhecimento do sistema):

Create
Delete
Destroy
Initialize
Append
Revert
Commit
Show, Display    

Ícone de Dedo IndicadorUtilize nomes positivos para as funções de predicado e parâmetros Boolean. Utiliza nomes negativos pode criar negações duplas (por exemplo, Not Is_Not_Found) e pode dificultar mais a leitura do código.

function Is_Not_Valid (...) return Boolean
procedure Find_Client (With_The_Name : in  Name;
                       Not_Found     : out Boolean)    

deve ser definido como:

function Is_Valid (...) return Boolean;
procedure Find_Client (With_The_Name: in Name;
                       Found: out Boolean)    

que permite ao cliente negar suas expressões conforme necessário (não há penalidade de tempo de execução para isso)

if not Is_Valid (...) then ....    

Em alguns casos, um predicado negativo também pode se tornar positivo sem alterar sua semântica, utilizando um antônimo, como "Is_Invalid" em vez de "Is_Not_Valid." No entanto, nomes positivos são mais legíveis: "Is_Valid" é mais fácil de ser entendido do que "not Is_Invalid."

Ícone de OkUtilize a mesma palavra quando o mesmo significado geral for implícito, em vez de tentar localizar sinônimo ou variações. Portanto, a sobrecarga é encorajada para aprimorar a uniformidade, para manter o princípio da mínima surpresa.

Ícone de Dedo IndicadorSe os subprogramas forem utilizados como "skins" ou "wrappers" para chamadas de entrada, pode ser útil que o nome reflita esse fato, colocando o sufixo _Or_Wait no verbo ou fazendo com que uma frase como Wait_For_ seja seguida por um nome:

Subscriber.Get_Reply_Or_Wait
Subscriber.Wait_For_Reply    

Algumas operações sempre devem ser consistentemente definidas utilizando os mesmos nomes:

Ícone de Dedo IndicadorPara as convenções de tipo de e para as cadeias, as funções simétricas:

    function Image and function Value    

Ícone de Dedo IndicadorPara as convenções de tipo de e para alguma representação de baixo nível (como Byte_String para Data Interchange):

    procedure Read and Write    

Ícone de Dedo IndicadorPara os dados alocados:

    function Allocate (rather than Create)
    function Destroy (or Release, to express that the object will disappear)    

Quando isso é feito sistematicamente, utilizando nomenclatura consistente, a composição do tipo fica muito mais fácil.

Ícone de OkPara iteradores ativos, os seguintes primitivos sempre devem ser definidos:

Initialize
Next
Is_Done
Value_Of
Reset
. Se vários tipos de iteradores forem introduzidos no mesmo escopo, esses primitivos devem ser sobrecarregados em vez de introduzir um conjunto distinto de identificadores para cada iterador. Cf. [BOO87].

Ícone de OkAo utilizar os atributos predefinidos Ada como nomes de função, certifique-se de que eles sejam utilizados com as mesmas semânticas gerais: 'First, 'Last, 'Length, 'Image, 'Value e assim por diante. Observe que vários atributos (por exemplo, 'Range e 'Delta) não podem ser utilizados como nomes de função porque são palavras reservadas.

Objetos e Parâmetros de Subprograma (ou Entrada)

Ícone de OkPara indicar exclusividade ou para mostrar que essa entidade é o principal foco da ação, como o prefixo The_ ou This_ no objeto ou no nome do parâmetro. Para indicar um lado, temporário, objeto auxiliar, como o prefixo A_ ou Current_ nele:

procedure Change_Name (The_Subscriber : in Subscriber.Handle;
                       The_Name       : in Subscriber.Name );
            declare
    A_Subscriber : Subscriber.Handle := Subscriber.First;
            begin
    ...
    A_Subscriber := Subscriber.Next (The_Subscriber);
end;    

Ícone de OkPara os objetos Booleanos, utilize uma cláusula de predicado, com o formato positivo:

Found_It
Is_Available    

 

Is_Not_Available deve ser evitado.

Ícone de OkPara os objetos de tarefa, utilize um substantivo ou uma frase substantiva que implique uma entidade ativa:

Listener
Resource_Manager
Terminal_Driver    

Ícone de OkPara os parâmetros, colocar o prefixo do nome da classe ou algum substantivo característico com uma pré-posição também inclui a legibilidade, especialmente no lado do responsável pela chamada quando a associação nomeada for utilizada. Outros prefixos úteis para os parâmetros auxiliares possuem o formato Using_ ou, no caso de um parâmetro in out que é afetado como algum efeito secundário, Modifying_:

procedure Update (The_List     : in out Subscriber.List.Handle;
                  With_Id      : in     Subscriber.Identification;
                  On_Structure : in out Structure;
                  For_Value    : in     Value);
procedure Change (The_Object   : in out Object;
                  Using_Object : in     Object);    

Ícone de OkA ordem na qual os parâmetros são definidos também são muito importantes do ponto de vista do responsável pela chamada:

Isso permite tirar proveito dos padrões sem ter que utilizar a associação nomeada para o(s) principal(is) parâmetro(s).

Ícone de OkO modo "in" deve ser explicitamente indicado, mesmo em funções.

Unidades Genéricas

Ícone de OkEscolha o melhor nome que você utilizaria para uma versão não genérica: nome da classe para um pacote ou verbo transitivo (ou frase verbal) para um procedimento (consulte acima) e coloque nele o sufixo _Generic.

Ícone de OkPara tipos formais genéricos, quando o pacote genérico definir alguma estrutura de dados abstrata, utilize Item ou Element para formal genérico e Structure, ou algum outro substantivo mais apropriado, para a abstração exportada.

Ícone de OkPara iteradores passivos, utilize um verbo como Apply, Scan, Traverse, Process ou Iterate no identificador:

generic
		with procedure Act	(Upon : in out Element);
procedure Iterate_Generic	(Upon : in out Structure);    

Ícone de OkOs nomes de parâmetros formais genéricos não podem ser homógrafos.

generic
    type Foo is private;
    type Bar is private;
    with function Image (X : Foo) return String;
    with function Image (X : Bar) return String;
package Some_Generic is ...    

deve ser substituído por:

generic
    type Foo is private;
    type Bar is private;
    with function Foo_Image (X : Foo) return String;
    with function Bar_Image (X : Bar) return String;
package Some_Generic is ...    

Se necessário, os parâmetros formais genéricos podem ser renomeados na unidade genérica:

function Image (Item : Foo) return String Renames Foo_Image;
function Image (Item : Bar) return String Renames Bar_Image;    

Estratégias de Nomenclatura para Subsistemas

Quando um grande sistema for particionado nos subsistemas Rational (ou outra forma de bibliotecas de programas interconectados), é útil definir uma estratégia de nomenclatura que permita:

Escusa de Conflitos de Nomes

Em um sistema que consiste em várias centenas de objetos e subobjetos, é provável que ocorram alguns conflitos no nível de unidade de biblioteca e os programadores serão abreviações de sinônimos para nomes muito úteis como Utilities, Support, Definitions e assim por diante.

Fácil Localização de Entidades Ada

Utilizando os recursos de navegação no host Rational, localizar onde uma entidade é definida se torna uma tarefa fácil, mas quando o código é portado em um destino e utiliza as ferramentas de destino (depuradores, ferramentas de teste e assim por diante), o local de um procedimento Utilities.Get entre 2.000 unidade em 100 subsitema pode ser um desafio considerável para os novatos no projeto.

Ícone de OkOs nomes de unidade no nível de biblioteca de prefixo com a abreviação de quatro letras do subsistema no qual está contido.

A lista de subsistemas pode ser localizada no SAD (Software Architecture Document). Exclua dela, as bibliotecas de regra de componentes altamente reutilizáveis que provavelmente serão reutilizados entre inúmeros projetos, produtos COTS e unidades padrão.

Exemplo:

Comm Comunicação

Dbms Gerenciamento de banco de dados

Disp Exibições

Math Pacotes matemáticos

Drivers de Unidade

Por exemplo, todas as unidades exportadas do subsistema Disp receberão o prefixo Disp_, permitindo que a equipe ou a empresa encarregada do Disp tenha então liberdade completa de nomenclatura Se DBMS e Disp precisarem apresentar uma classe de objetos nomeada Subscriber, isso resultará em pacotes como:

Disp_Subscriber
Disp_Subscriber_Utilities
Disp_Subscriber_Defs
Dbms_Subscriber
Dbms_Subscriber_Interface
Dbms_Subscriber_Defs    

Capítulo 6

Declarações de Tipos, Objetos e Unidades do Programa

Ícone de OkO recurso de forte digitação Ada será utilizado para evitar a mistura de diferentes tipos. De forma conceitual, diferentes tipos devem ser realizados como diferentes tipos definidos pelo usuário. Os subtipos devem ser utilizados para aprimorarem a leitura do programa e para aprimorarem a efetividade das verificações de tempo de execução geradas pelo compilador.

Tipos de Enumeração

Ícone de OkSempre que possível, introduza na enumeração algum valor literal extra que represente o valor não inicializado, inválido ou nenhum valor:

type Mode  is (Undefined, Circular, Sector, Impulse);
type Error is (None, Overflow, Invalid_Input_Value,Ill-formed_Name);    

Isso suportará as regras para inicializar sistematicamente os objetos. Coloque esse literal no início, em vez de colocá-lo ao final da lista, para facilitar a manutenção e permitir subfaixas contíguas de valores válidos:

subtype Actual_Error is Error range Overflow .. Error'Last;    

Tipos Numéricos

Ícone de OkEvite o uso de tipos numéricos predefinidos.

Quando um alto grau de portabilidade e nova utilidade for o objetivo, ou quando o controle for necessário sobre o espaço de memória ocupado por objetos numéricos, os tipos numéricos predefinidos (do pacote Padrão) não devem ser utilizados. A razão para esse requisito é que as características dos tipos predefinidos Integer e Float são (deliberadamente) não-especificados no Manual de Referência para a Linguagem de Programação Ada [ISO87].

Ícone de Dedo IndicadorUma primeira estratégia sistemática é introduzir tipos numéricos específicos de projeto-em um pacote System_Types, por exemplo-com nomes que transportam uma indicação da exatidão ou do tamanho da memória:

package System_Types is
        type Byte is range -128 .. 127;
        type Integer16 is range -32568 .. 32567;
        type Integer32 is range ...
        type Float6 is digits 6; 
        type Float13 is digits 13;
...
end System_Types;    

Ícone de Dedo IndicadorNão redefina os tipos padrão (tipos do pacote Padrão).

Ícone de Dedo IndicadorNão especifique o tipo base do qual ele deve ser derivado; deixe o compilador escolher. O exemplo a seguir é incorreto:

type Byte is new Integer range -128 .. 127;    

Ícone de Dedo IndicadorFloat6 é um nome melhor do que Float32, mesmo se na maioria das máquinas as flutuações de 32 bits alcançarem 6 dígitos de exatidão.

Ícone de Dedo IndicadorNas diversas partes do objeto do projeto, derive os tipos com nomes mais significativos do que os existentes em Baty_System_Types. Alguns dos tipos mais precisos poderiam se tornar privados para suportarem uma porta eventual para um destino com suporte de precisão limitada.

Essa estratégia deve ser utilizada quando:

Se esse não for o caso, outra estratégia mais simples é definir sempre novos tipos, especificando o intervalo selecionado e a exatidão, mas nunca especificando o tipo base dos quais eles devem ser derivados. Por exemplo, declare:

type Counter is range 0 .. 100;
type Length is digits 5;    

Isso é preferível ao seguinte:

type Counter is new Integer range 1..100; -- poderia ser 64 bits
type Length is new Float digits 5; -- poderia ser 13    

Essa segunda estratégia força o programador a considerar os limites precisos e precisão que cada tipo requer, em vez de selecionar arbitrariamente um determinado número de bits. No entanto, certifique-se de que se o intervalo não for idêntico ao de um tipo base, o intervalo sistemático será aplicado pelo compilador-por exemplo, para o tipo Counter acima, se o tipo base for um inteiro de 32 bits.

Ícone de Dedo IndicadorSe as verificações de intervalo estiverem se tornando um problema, uma forma de evitá-las é declarar:

type Counter_Min_Range is range 0 .. 10_000;
type Counter is range Counter_Min_Range'Base'First .. Counter_Min_Range'Base'Last;    

Ícone de OkEvite os tipos padrão que correm para o código através de construções como loops, intervalos de índice e assim por diante.

Os subtipos de tipos numéricos predefinidos são utilizados apenas nas seguintes circunstâncias:

Exemplo:

for I in 1 .. 100 loop ...     
-- I é do tipo Standard.Integer
type A is array (0 .. 15) of Boolean; 
-- index é Standard.Integer.    

Em vez disso, utilize o formato: Some_Integer range L .. H

for I in Counter range 1 .. 100 loop ...
type A is array (Byte range 0 .. 15) of Boolean;    

Ícone de Dedo IndicadorNão tente implementar tipos não designados.

Os tipos inteiro com aritmética não designada não existem no Ada. Na definição de idiomas, todos os tipos de inteiros são derivados inteiramente ou não dos tipos predefinidos e, por sua vez, eles devem ser simétricos aproximando-se do zero.

Tipos Reais

Ícone de OkPara a portabilidade, conte apenas com tipos reais que tenham valores nos intervalos:

[-F'Large .. -F'Small]  [0.0]  [F'Small .. F'Large]    

Certifique-se de que F'Last e F'First não podem ser números de modelos e até podem não estar em qualquer intervalo de modelo. O local relativo de F'Last e F'Large depende da definição de tipo e do hardware subjacente. Um exemplo especialmente ruim é o caso em que 'Last do tipo de ponto fixo não pertença ao tipo, como em:

type FF is delta 1.0 range -8.0 .. 8.0;    

em que, de acordo com uma leitura rigorosa do Manual de Referência Ada 3.5.9(6), FF'Last = 8.0 não possa pertencer ao tipo.

Para representar números reais grandes ou pequenos, utilize atributos 'Large ou 'Small (e suas contrapartes negativas), não 'First e 'Last, como seria feito para os tipos inteiros.

Ícone de OkPara os tipos de ponto flutuante, utilize apenas <= e >=, nunca =, <, >, /=.

As semânticas da comparação absoluta são uma representação mal-definida (igualdade da representação e não igualdade dentro do grau requerido de exatidão). Por exemplo, X < Y pode não produzir o mesmo resultado de: não (X >= Y). Os testes para a igualdade, A = B, devem ser expressas como:

abs (A - B) <= abs(A)*F'Epsilon    

Para aprimorar a legibilidade e capacidade de manutenção, considere fornecer um operador Equal que envolva a expressão acima.

Observe também que a expressão mais simples:

abs (A - B) <= F'Small    

é válida apenas para os valores menores de A e B, e portanto não é geralmente recomendada.

Ícone de OkEvite qualquer referência à exceção predefinida Numeric_Error. Foi feita uma interpretação de ligação do Quadro Ada em todos os casos que costumavam acionar Numeric_Error e agora acionam Constraint_Error. A exceção Numeric_Error é obsoleta em Ada 95.

Ícone de Dedo IndicadorSe Numeric_Error ainda for acionado pela implementação (esse será o caso do compilador nativo Rational), sempre verifique o Constraint_Error junto com o Numeric_Error na mesma alternativa em uma rotina de tratamento de exceção:

when Numeric_Error | Constraint_Error => ...    

Ícone de Dedo IndicadorSeja cuidadoso com o fluxo baixo.

O fluxo baixo não é detectado no Ada. O resultado é 0.0 e nenhuma exceção é levantada. Observe que uma verificação para o fluxo baixo pode ser explicitamente alcançada testando o resultado de uma multiplicação ou divisão em 0.0, em que nenhum dos operandos será 0.0. Observe também que você pode implementar nossos próprios operadores para executar automaticamente essa verificação, embora com algum custo na eficiência.

Ícone de Atenção com a MãoO uso dos tipos de ponto fixo é restrito.

Utilize os tipos de ponto flutuante sempre que possível. A implementação irregular dos tipos de pontos fixos em uma implementação Ada provoca problemas de portabilidade.

Ícone de Dedo IndicadorPara os tipos de pontos fixos, 'Small deve ser igual a 'Delta.

É isso que o código deve especificar. O fato de que a opção padrão para 'Small seja um domínio 2 conduz a todos os tipos de problemas. Uma maneira de esclarecer a opção é gravar:

Fx_Delta : constant := 0.01;
type FX is delta Fx_Delta range L .. H;
for FX'Small use Fx_Delta;    

Se as cláusulas de comprimento para os tipos de ponto fixo não forem suportadas, a única maneira de obedecer a essa regra é especificar explicitamente um 'Delta que é um poder de 2. Os subtipos podem ter um 'Small diferente de 'Delta (a regra se aplica apenas à definição de tipo ou ao "primeiro subtipo nomeado" na terminologia do Manual de Referência Ada).

Tipos de Registro

Ícone de OkSempre que possível, forneça valores iniciais simples estáticos para os componentes de um tipo de registro (geralmente, podem ser utilizados valores como 'First ou 'Last).

Mas não aplique isso aos discriminantes. As regras do idioma são que os discriminantes sempre possuem valores. Os registros mutáveis (ou seja, os registros com valores padrão para os discriminantes) devem ser introduzidos apenas quando a mutabilidade for uma característica desejada. Caso contrário, os registros mutáveis introduzirão código extra no espaço de memória (geralmente será alocada a maior variante) e no tempo (as verificações de variantes são mais complexas de serem alcançadas).

Ícone de OkEvite as chamadas de função nos valores iniciais padrão de qualquer componente, uma vez que isso pode conduzir a um erro de "acesso antes da elaboração" (consulte "Estrutura de Programa e Problemas de Compilação").

Ícone de Dedo IndicadorPara registros mutáveis (registros cujos discriminantes possuam valores padrão), se um discriminante for utilizado no dimensionamento de algum outro componente, especifique-o para que seja de um pequeno intervalo razoável.

Exemplo:

type Record_Type (D : Integer := 0) is 
record
            S : String (1 .. D);
    end record;    A_Record : Record_Type;    

está propenso a levantar um Storage_Error na maioria das implementações. Especifique um intervalo mais razoável para o subtipo do discriminante D.

Ícone de OkNão assuma nada sobre o layout físico dos registros.

Especialmente, e diferente de outras linguagens de programação, os componentes não precisam estar dispostos na ordem fornecida na definição.

Tipos de Acesso

Ícone de Atenção com a MãoRestrinja o uso dos tipos de acesso.

Isso é especialmente válido para aplicativos destinados a serem executados permanentemente em pequenas máquinas sem memória virtual. Os tipos de acesso são perigosos, porque os erros de programação podem conduzir ao esgotamento de armazenamento e, mesmo com boa programação, podem fragmentar a memória. Os tipos de acesso também são mais lentos. O uso dos tipos de acesso deve fazer parte de uma ampla estratégia de projeto e coletas, seus tamanhos e pontos de alocação e de desalocação devem ser rastreados. Para que os clientes de uma abstração fiquem cientes de que os valores de acesso sejam manipulados, o nome escolhido deve indicar: o ponteiro ou um nome com sufixo _Pointer.

Ícone de OkAloque as coletas durante a elaboração do programa e especifique sistematicamente o tamanho de cada coleta.

O valor fornecido (em unidades de armazenamento) pode ser estático ou dinamicamente computado (lido de um arquivo, por exemplo). O racional para essa regra é que o programa deve falhar imediatamente na inicialização, em vez de acabar misteriosamente N dias depois. Os pacotes genéricos podem fornecer um formal genérico adicional especificando o tamanho.

Observe que geralmente há um código extra para cada objeto alocado: pode ser que os tempos de execução no sistema de destino aloquem algumas informações adicionais com cada parte da memória para procedimentos operacionais internos. Portanto, para armazenar N objetos de unidades de armazenamento de tamanho M, pode ser necessário alocar mais de N * M unidades de armazenamento para a coleta-por exemplo, N * (M + K). Obtenha o valor desse código extra K a partir do Apêndice F [ISO87] ou conduzindo os experimentos.

Ícone de OkEncapsule o uso dos alocadores (new primitivo Ada) e release. Se for viável, gerencie uma lista interna livre, em vez de contar com Unchecked_Deallocation.

Se um tipo de acesso for utilizado para implementar alguma estrutura de dados, é muito provável acessar um tipo de registro que possua (como um componente) o mesmo tipo de acesso. Isso permitirá reciclar as células livres, as encadeando em uma lista livre sem código extra de espaço adicional (diferente do ponteiro para a cabeça da lista).

Manipule as exceções Storage_Error explicitamente levantadas por new e exporte uma exceção mais significativa novamente, indicando o esgotamento do tamanho de armazenamento máximo da coleta.

Ter um único ponto de alocação e desalocação também torna mais fácil o rastreio e a depuração no caso de um problema.

Ícone de OkUtilize a desalocação apenas em células alocadas do mesmo tamanho (portanto, os mesmos discriminantes).

Isso é importante para evitar a fragmentação da memória. A Unchecked_Deallocation é muito improvável para fornecer um serviço de compactação de memória. Você pode desejar verificar se o sistema de tempo de execução fornece aglutinação dos blocos adjacentes liberados.

Ícone de OkFornece sistematicamente um Destroy (ou Free, ou Release) primitivo com tipos de acesso.

Isso é especialmente importante para os tipos de dados abstratos implementados com os tipos de acesso e deve ser feito sistematicamente para alcançar a capacidade de composição de vários desses tipos.

Ícone de OkLibere os objetos sistematicamente.

Tente mapear as chamadas para a alocalização e desalocação para assegurar que todos os dados alocados serão desalocados. Tente desalocar os dados no mesmo escopo no qual foi alocado. Lembre-se de desalocar também quando ocorrerem as exceções. Observe que esse é um caso para utilizar uma alternativa when others, terminada com uma instrução raise.

A estratégia preferida é aplicar o padrão: Get-Use-Release. O programador obtém (Gets) os objetos (que cria alguma estrutura de dados dinâmicos) e, em seguida, os utiliza (Uses) e depois deve liberá-lo (Release). Certifique-se de que as três operações estejam claramente identificadas no código e que o release é feito em todas as saídas possíveis do quadro, incluindo a exceção.

Ícone de Dedo IndicadorSeja cuidadoso ao desalocar as estruturas de dados compostas temporárias que podem estar contidas em registros.

Exemplo:

type Object is
record
    Field1: Some_Numeric;
    Field2: Some_String;
    Field3: Some_Unbounded_List;
    end record;    

em que 'Some_Unbounded_List' é uma estrutura ligada composta, ou seja, é composta por inúmeros objetos ligados entre si. Agora considere uma típica função de atributo, escrita como:

function Some_Attribute_Of(The_Object: Object_Handle) return 
Boolean is Temp_Object: The_Object;
            begin
    Temp_Object := Read(The_Object);
    return Temp_Object.Field1 < Some_Value;
end Some_Attribute_Of;    

A estrutura composta criada implicitamente no heap quando o objeto for lido no Temp_Object nunca é desalocada, mas agora é inatingível. Essa é uma fuga de memória. A solução apropriada é implementar um paradigma Get-Use-Release para essas onerosas estruturas. Em outras palavras, seu cliente deve obter (Get) o objeto primeiro, em seguida, utilizá-lo (Use) conforme necessário e, em seguida, liberá-lo (Release):

procedure Get (The_Object  : out Object;
               With_Handle : in  Object_Handle);
function Some_Attribute_Of(The_Object : Object) 
                        return Some_Value;
function Other_Attribute_Of(The_Object	: Object) 
                        return Some_Value;
...
procedure Release(The_Object: in out Object);    

O código do cliente pode ficar semelhante ao seguinte:

             declare
    My_Object: Object;
            begin
    Get (My_Object, With_Handle => My_Handle);
    ...
    Do_Something
      (The_Value => Some_Attribute_Of(My_Object));
      ...
    Release(My_Object);
end;    

Tipos Privados

Ícone de OkDeclare os tipos como privados sempre que for necessário ocultar os detalhes da implementação.

Os detalhes da implementação precisam ser ocultos com um tipo privado quando:

No Ambiente Rational, os tipos de privilégio, em conjunto com as partes privadas e os subsistemas, reduzem muito o impacto de uma alteração de design de interface eventual.

Ícone de Dedo IndicadorEm contradição à conhecida programação orientada ao objeto "puro", não utilize os tipos privados quando o tipo completo correspondente for a melhor abstração possível. Seja pragmático; pergunte se o tipo privado inclui algo.

Por exemplo, um vetor matemático é melhor representado como uma matriz, ou um ponto em um plano como um registro, do que como um tipo privado:

type Vector is array (Positive range <>) of Float;
Type Point is 
record
        X, Y : Float := Float'Large;
    end record;    

A indexação da matriz, seleção de componente de registro e notação agregada será muito mais legível (e eventualmente mais eficiente) do que uma série de chamadas de subprograma, como seria requerido se o tipo fosse desnecessariamente privado.

Ícone de OkDeclare os tipos privados como limitados quando a designação padrão ou a comparação dos objetos reais e os valores fossem sem sentido, não intuitivo ou impossível.

Esse é o caso quando:

Ícone de OkUm tipo privado limitado deve se auto-inicializar.

Uma declaração de objeto desse tipo deve receber um valor inicial razoável, uma vez que geralmente não será viável para designar um posteriormente, sem riscos de levantar alguma exceção durante a chamada do subprograma.

Ícone de Dedo IndicadorSempre que viável ou significativo, forneça aos tipos limitados um procedimento Copy (ou Assign) e um procedimento Destroy.

Ícone de Dedo IndicadorAo designar os tipos formais de um genérico, especifique os tipos privados limitados contanto que a igualdade ou a designação não seja requerida internamente, para maior utilidade da unidade genérica correspondente.

Em linha com a regra anterior, você pode então importar um procedimento formal genérico Copy e Destroy e um predicado Are_Equal, se significativo.

Ícone de Dedo IndicadorPara os tipos privados formais genéricos, indique na especificação se o real correspondente deve ser restrito, ou não.

Isso pode ser alcançado por uma convenção de nomenclatura e/ou comentário:

generic
    --Deve ser restrito.
    type Constrained_Element is limited private;
package ...    

Como alternativa, você pode utiliza o pragma definido pelo Rational Must_Be_Constrained:

generic
    type Element is limited private;
    pragma Must_Be_Constrained (Element);
package ...    

Tipos Derivados

Ícone de Dedo IndicadorLembre-se de que derivar um tipo também deriva todos os subprogramas que são declarados na mesma parte declarativa como o tipo pai: os subprogramas deriváveis. Portanto, é inútil redefinir todos como skins na parte declarativa do tipo derivado. Mas os subprogramas genéricos não são deriváveis e pode ser necessário redefini-los como skins.

Exemplo:

package Base is
    type Foo is
record
            ...
    end record;        procedure Put(Item: Foo);
    function Value(Of_The_Image: String) return Foo;
end Base;
with Base;
package Client is
    type Bar is new Foo;
    -- Nesse ponto, as seguintes declarações são
    -- implicitamente criadas:
    -- 
    -- function "="(L,R: Bar) return Boolean;
    -- 
    -- procedure Put(Item: bar);
    -- function Value(Of_The_Image: String) return Bar;
    -- 
end Client;

Portanto, não é necessário redefinir essas operações como skins. No entanto, observe que os subprogramas genéricos (como iteradores passivos) não são derivados juntamente com outras operações e, portanto, devem ser reexportados como skins. Os subprogramas definidos em outro lugar que não seja o da especificação que contém a declaração de tipo base também não são deriváreis e também devem ser reexportados como skins.

Declarações de Objeto

Ícone de OkEspecifique os valores iniciais nas declarações do objeto, a menos que o objeto inicialize sozinho ou exista um valor inicial padrão implícito (por exemplo, tipos de acesso, tipos de tarefa, registros com valores padrão para campos não discriminantes).

O valor designado deve ser real e significativo, não apenas qualquer valor do tipo. Se o valor inicial real estiver disponível, como, por exemplo, um dos parâmetros de entrada, designe-o. Se não for possível computar um valor significativo, considere declarar o objeto posteriormente ou designe qualquer valor "nil", se disponível.

Ícone de Dedo IndicadorO nome "Nil" significa "Uninitialized" e é utilizado para declarar as constantes que podem ser utilizadas como "valor não utilizável, mas conhecido" que pode ser rejeitado de uma maneira controlada pelos algoritmos.

Sempre que viável, o valor Nil não deve ser utilizado para qualquer outro propósito que não seja a inicialização, de modo que sua aparência sempre possa indicar um erro de variável não inicializada.

Observe que nem sempre é possível declarar um valor Nil para todos os tipos, especialmente os tipos modulares, como um ângulo. Nesse caso, escolha o valor menos provável.

Ícone de Dedo IndicadorObserve que o código para inicializar grandes registros pode ser caro, especialmente se o registro tiver variantes e se algum valor inicial for não estático (ou, mais precisamente, se o valor não puder ser computado no momento da compilação). Às vezes, é mais eficiente elaborar uma vez e para todos um valor inicial (talvez, no pacote que define o tipo) e designá-lo explicitamente:

R : Some_Record := Initial_Value_For_Some_Record;    

Nota:

A experiência mostra que as variáveis não inicializadas são uma das principais origens dos problemas no código de portabilidade e uma das principais origens de erros na programação. Isso é agravado quando o host de desenvolvimento tenta ser "agradável" ao programador fornecendo valores padrão, para ao menos alguns dos objetos (por exemplo, digite Integer no computador nativo Rational) ou quando o sistema de destino zerar a memória antes do carregamento do programa (por exemplo, em um DEC VAX). Para alcançar a portabilidade, sempre assuma o pior.

Ícone de Dedo IndicadorA designação de um valor inicial na declaração pode ser omitida quando for onerosa e quando for óbvio que o objeto recebeu um valor antes de ser utilizado.

Exemplo:

procedure Schmoldu is
    Temp : Some_Very_Complex_Record_Type;
 -- initialized later
            begin
loop
        Temp := Some_Expression ...
        ...    

Ícone de OkEvite o uso de valores literais no código.

Utilize as constantes (com um tipo) quando o valor definido for vinculado a um tipo. Caso contrário, utilize os números nomeados, especialmente para todos os valores sem dimensão (valores puros):

Earth_Radius : constant Meter := 6366190.7;   -- In meters.
Pi           : constant       := 3.141592653; -- No units.    

Ícone de Dedo IndicadorDefina as constantes relacionadas com expressões universais estáticas:

Bytes_Per_Page :   constant := 512;
Pages_Per_Buffer : constant := 10;
Buffer_Size :      constant := Bytes_Per_Page * Pages_Per_Buffer;
Pi_Over_2   :      constant := Pi / 2.0;    

Isso tira proveito do fato de que essas expressões devem ser computadas exatamente no momento da compilação.

Ícone de OkNão declare objetos com tipos anônimos. Para obter informações adicionais, consulte o Manual de Referência Ada 3.3.1.

A capacidade de manutenção é reduzida, os objetos não podem ser transmitidos como parâmetros e geralmente conduz aos erros de conflito de tipo.

Subprogramas e Unidades Genéricas

Ícone de Dedo IndicadorOs subprogramas podem ser declarados como procedimentos ou funções; seguem alguns critérios que podem ser utilizados para escolher qual forma declarar.

Declare uma função quando:

Declare um procedimento quando:

Ícone de OkEvite fornecer os valores padrão aos parâmetros formais genéricos utilizados para as estruturas de dimensionamento (tabelas, coletas, etc.)

Ícone de OkGrave os procedimentos locais com alguns efeitos colaterais, conforme a possibilidade, e as funções sem qualquer efeito colateral. Documente o efeito colateral.

Os efeitos colaterais geralmente são modificações das variáveis globais e só podem ser observados ao ler o corpo do subprograma. O programa pode não estar ciente dos efeitos colaterais no site de chamada.

Transmitir os objetos requeridos como parâmetros torna o código mais robusto, mais fácil de ser lido e menos dependente de seu conteúdo.

Essa regra se aplica principalmente aos subprogramas locais: os subprogramas exportados geralmente requerem acesso legítimo às variáveis globais no corpo do pacote.


Capítulo 7

Expressões e Instruções

Expressões

Ícone de Dedo IndicadorUtilize os parênteses redundantes para esclarecer as expressões compostas.

O nível de aninhamento de uma expressão é definido como o número de conjuntos aninhados de parênteses requeridos para avaliar uma expressão da esquerda para a direita, se as regras de precedência do operador forem ignoradas.

Ícone de Dedo IndicadorLimite o nível de aninhamento das expressão para quatro.

Ícone de OkOs agregados de registro devem utilizar associações nomeadas e devem ser qualificados:

Subscriber.Descriptor'(Name    => Subscriber.Null_Name,
                       Mailbox => Mailbox.Nil,
                       Status  => Subscriber.Unknown,
                   ...);    

Ícone de Dedo IndicadorO uso de when others é proibido para os agregados de registro.

Isso ocorre porque, diferente das matrizes, os registros são estruturas naturalmente heterogêneas e, portanto, a designação uniforme não é razoável.

Ícone de Dedo IndicadorUtilize expressões Booleanas simples em substituição às instruções "if...then...else" para predicados simples:

function Is_In_Range(The_Value: Value; The_Range: Range)
     return Boolean is
            begin
            if The_Value >= The_Range.Min and The_Value <= The_Range.Max then return True;
        end if;
end Is_In_Range;    

Seria melhor utilizar o seguinte:

function Is_In_Range(The_Value: Value; The_Range: Range)
     return Boolean is
            begin
    return The_Value >= The_Range.Min 
        and The_Value <= The_Range.Max;
end Is_In_Range;    

As expressões complexas que contêm duas ou mais instruções if não devem ser alteradas dessa maneira, se afetar a legibilidade.

Instruções

Ícone de OkAs instruções de loop devem ter nomes:

Forever: loop
   ...
end loop Forever;    

Ícone de OkQuando um loop tiver um nome, qualquer instrução exit que ele contiver deve especificá-lo.

Ícone de OkLoops que requerem um teste de conclusão no início devem utilizar o formulário de loop "while". Loops que requerem um teste de conclusão em algum lugar devem utilizar o formulário geral e uma instrução exit.

Ícone de Dedo IndicadorMinimize o número de instruções exit em um loop.

Ícone de OkEm um loop "for" que itera sobre uma matriz, utilize o atributo 'Range aplicado no objeto de matriz, em vez de um intervalo explícito ou algum outro subtipo.

Ícone de Dedo IndicadorMova qualquer código independente de loop para fora do loop. Embora "code hoisting" seja uma otimização do compilador comum, ele não pode ser feito quando o código invariante fizer chamadas a outras unidades de compilação.

Exemplo:

World_Search:
while not World.Is_At_End(World_Iterator) loop
    ...
Country_Search:
    while not Nation.Is_At_End(Country_Iterator) loop
            declare
        City_Map: constant City.Map := City.Map_Of
            (The_City => Nation.City_Of(Country_Iterator),
             In_Atlas => World.Country_Of(World_Iterator).Atlas);
            begin
        ...    

No código acima, a chamada "World.Country_Of" é independente de loop (ou seja, o país permanece inalterado no loop interno). No entanto, na maioria dos casos, o compilador está proibido de mover a chamada para fora do loop, porque a chamada pode ter efeitos colaterais que possam afetar a execução do programa. Portanto, o código será executado desnecessariamente em cada vez através do loop.

O loop será mais eficiente e mais fácil de ser entendido e mantido se reescrito como:

Country_Search:
while not World.Is_At_End(World_Iterator) loop
            declare
        This_Country_Atlas: constant Nation.Atlas 
            := World.Country_Of
                    (World_Iterator).Atlas;
            begin
        ...
        City_Search:
        while not Nation.Is_At_End (The_City_Iterator) loop
            declare
                City.Map_Of (
                    The_City => Nation.City_Of
                                        (Country_Iterator),
                    In_Atlas => This_Country_Atlas );
            begin
                ...    

Ícone de OkO subprograma e as chamadas de entrada devem utilizar associações nomeadas.

No entanto, se ficar claro que o primeiro parâmetro (ou único) é o foco principal da operação (por exemplo, um objeto direto de um verbo transitivo), o nome poderá ser omitido apenas para esse parâmetro:

Subscriber.Delete (The_Subscriber => Old_Subscriber);    

em que Subscriber.Delete é o verbo transitivo e Old_Subscriber é o objeto direto. As seguintes expressões sem a associação nomeada The_Subscriber => Old_Subscriber são aceitas:

Subscriber.Delete	(Old_Subscriber);
Subscriber.Delete (Old_Subscriber, 
                   Update_Database  => True,
                   Expunge_Name_Set => False);
if Is_Administrator (Old_Subscriber) then ...    

Também há casos em que o significado dos parâmetros é tão óbvio que a associação nomeada apenas degradaria a legibilidade. Isso é válido, por exemplo, quando todos os parâmetros forem do mesmo tipo e modo e não tiverem valores padrão:

if Is_Equal (X, Y) then ...
Swap (U, B);    

Ícone de OkUm when others não deve ser utilizado em instruções case ou nas definições de tipo de registro (para variantes).

Não utilizar um when others ajudará durante a fase de manutenção tornando essas construções inválidas sempre que a definição de tipo distinto for modificada, forçando o programador a considerar o que deveria ser feito para tratar da modificação. No entanto, isso é tolerado quando o seletor for um grande intervalo de inteiro.

Ícone de Dedo IndicadorUtilize uma instrução case em vez de uma série de "elsif" quando a condição da ramificação for um valor distinto.

Ícone de OkOs subprogramas devem ter um único ponto de retorno.

Tente sair dos subprogramas ao final da parte da instrução. As funções devem ter uma instrução única de retorno. As instruções de retorno dispersas livremente sobre um corpo da função são comparáveis às instruções goto, dificultando a leitura e a manutenção do código.

Os procedimentos não devem ter nenhuma instrução de retorno.

Ícone de Dedo IndicadorVários returns podem ser tolerados apenas em funções muito pequenas, quando todos os returns puderem ser vistos simultaneamente e quando o código tiver uma estrutura muito regular:

function Get_Some_Attribute return Some_Type is
            begin
    if Some_Condition then
        return This_Value;
else
        return That_Other_Value;
        end if;
end Get_Some_Attribute;    

Ícone de Atenção com a MãoO uso das instruções goto é restrito.

Na defesa da instrução "goto"; deve ser observado que a sintaxe das etiquetas goto e as condições restritas do uso de goto no Ada torne essa instrução não tão prejudicial como se imagina e, em vários casos, isso é preferível e mais legível e significativo do que algumas construções equivalentes (uma construção goto falha com uma exceção, por exemplo).

Dicas de Codificação

Ícone de Dedo IndicadorAo manipular as matrizes, não assuma que seus índices iniciem com 1. Utilize os atributos 'Last, 'First, 'Range.

Ícone de Dedo IndicadorDefina o subtipo restrito mais comum dos tipos não restritos-na maioria registros-e utilize esses subtipos para os parâmetros e os valores de retorno para aumentar a auto-verificação no código do cliente.

type Style is (Regular, Bold, Italic, Condensed);
type Font (Variety: Style) is ...
subtype Regular_Font is Font (Variety => Regular);
subtype Bold_Font is Font (Variety => Bold);
function Plain_Version (Of_The_Font: Font) return Regular_Font;
procedure Oblique (The_Text   : in out Text;
                   Using_Font : in     Italic_Font);
...    

Capítulo 8

Problemas de Visibilidade

Sobrecarga e Homógrafos

As seguintes diretrizes são recomendadas:

Ícone de OkSubprogramas de Sobrecarga.

No entanto, certifique-se de que ao utilizar o mesmo identificador, isso realmente esteja implicando o mesmo tipo de operação.

Ícone de OkEvite o ocultamento dos identificadores homógrafos nos escopos aninhados.

Isso conduz à confusão para o leitor e os riscos em potencial na manutenção. Além disso, certifique-se da existência e do escopo das variáveis de controle de loop "for".

Ícone de OkNão sobrecarregue as operações sobre os subtipos, sempre no tipo.

Ao contrário daquilo que o leitor ingênuo pode acabar acreditando, a sobrecarga será aplicada ao tipo base e a seus subtipos.

Exemplo:

subtype Table_Page is Syst.Natural16 range 0..10;
function "+"(Left, Right: Table_Page) return Table_Page;    

O compilador procura pelo tipo base e não pelo subtipo de um parâmetro ao combinar os subprogramas. Portanto, no exemplo acima, "+" é redefinido realmente para todos os valores Natural16 no pacote atual, não apenas Table_Page. Portanto, qualquer expressão "Natural16 + Natural16" agora seria mapeado para uma chamada para "+"(Table_Page, Table_Page), que provavelmente retornaria o resultado incorreto ou produziria uma exceção.

Cláusulas de Contexto

Ícone de OkMinimize o número de dependências introduzidas pelas cláusulas "with".

Onde a visibilidade for estendida pelo uso de uma cláusula "with", a cláusula deve abranger a menor região de código possível. Utilize uma cláusula "with" apenas quando necessário, idealmente apenas em um corpo ou mesmo em um grande stub de corpo.

Utilize os pacotes de interface para exportar novamente as entidades de baixo nível, evitando assim a visibilidade do "with" de um grande número de pacotes de baixo nível. Para isso, utilize os tipos derivados, a nova nomenclatura, os subprogramas de skin e, talvez, os tipos predefinidos como cadeias (assim como é feito nos pacotes de comando do Ambiente).

Utilize o acoplamento soft (fraco) entre as unidades utilizando os parâmetros formais genéricos, em vez do acoplamento hard (forte), utilizando as cláusulas "with".

Exemplo: Para exportar um procedimento Put em um tipo composto, importe como formais genéricos algum procedimento Put para seus componentes, em vez de with diretamente no Text_Io.

Ícone de OkAs cláusulas "Use" não devem ser utilizadas.

Evitar as cláusulas "use" o máximo possível aumenta a leitura e a legibilidade, contanto que essa regra seja suportada adequadamente pelas convenções de nomenclatura que fazem uso efetivo do contexto e da nomenclatura apropriada. (Consulte "Convenções de Nomenclatura", acima). Isso também ajuda a evitar algumas surpresas na visibilidade, especialmente durante a fase da manutenção.

Para um pacote que define um tipo de caractere, uma cláusula "use" é necessária em qualquer unidade compilação que precise definir os literais de cadeia com base nesse tipo de caractere:

package Internationalization is
    type Latin_1_Char is (..., 'A', 'B', 'C', ..., U_Umlaut, ...);
    type Latin_1_String is array (Positive range <>) of 
            Latin_1_Char;
end Internationalization ;
use Internationalization;
Hello : constant Latin_1_String := "Baba"    

A ausência de uma cláusula "use" evita o uso de operadores no formato infix. Eles podem ser renomeados na unidade cliente:

function "=" (X, Y : Subscriber.Id) return Boolean 
            renames Subscriber."=";
function "+" (X, Y :Base_Types.Angle) return Base_Types.Angle
            renames Base_Types."+";    

Ícone de OkComo a ausência de uma cláusula "use" geralmente conduz a incluir o mesmo conjunto de renomeações em inúmeras unidades clientes, todas essas renomeações podem ser fatoradas no próprio pacote de definição, através de um pacote Operations aninhado no pacote de definição. Uma cláusula "use" no pacote Operations é então recomendado na unidade cliente:

package Pack is
    type Foo is range 1 .. 10;
    type Bar is private;
     ...
    package Operations is
        function "+" (X, Y : Pack.Foo) return Pack.Foo 
                renames Pack."+";
        function "=" (X, Y : Pack.Foo) return Boolean 
                renames Pack."=";
        function "=" (X, Y : Pack.Bar) return Boolean 
                renames Pack."=";
        ...
    end Operations;
private
	...
end Pack;
with Pack;
package body Client is
    use Pack.Operations; -- Torna APENAS as Operações diretamente visíveis.
    ...
    A, B : Pack.Foo;    -- Ainda precisa do prefixo Pack.
    ...
    A := A + B ;        -- Observe que "+" é diretamente
                        -- visível. 

As Operações de Pacote sempre devem ter esse nome e sempre devem ser colocados na parte inferior da parte visível do pacote de definição. A cláusula "use" deve ser colocada apenas onde necessário-ou seja, deve ser colocada apenas no corpo de Client se nenhuma operação for utilizada na especificação, o que geralmente é o caso.

with Defs;
package Client is
    ...
    package Inner is
        use Defs;
        ...
    end Inner;		-- O escopo da cláusula use é encerrado aqui.
    ...
end Client;
            declare
    use Special_Utilities;
            begin
    ...
end;                -- O escopo da cláusula use é encerrado aqui. 

Renomeações

Ícone de Dedo IndicadorUtilize as declarações de renomeação.

A renomeação é recomendada em conjunto com a restrição nas cláusulas "use" para facilitar a leitura do código. Quando uma unidade com um nome muito longo for referida várias vezes, fornecer um nome muito abreviado para ele aprimorará a legibilidade:

with Directory_Tools;
with String_Utilities;
with Text_Io;
package Example is
    package Dt renames Directory_Tools;
    package Su renames String_Utilities;
    package Tio renames Text_Io;
    package Dtn renames Directory_Tools.Naming;
    package Dto renames Directory_Tools.Object;
        ...    

Ícone de OkA opção dos nomes abreviados deve ser consistente em todo o projeto, ao manter o princípio da mínima surpresa. A forma de alcançar isso é fornecer o nome abreviado no próprio pacote:

package With_A_Very_Long_Name is package Vln renames 
            With_A_Very_Long_Name;
    ...
end
with With_A_Very_Long_Name;
package Example is package Vln renames With_A_Very_Long_Name;
-- A partir daqui on Vln é uma abreviação. 

Certifique-se de que uma renomeação do pacote fornece a visibilidade apenas na parte visível do pacote renomeado.

Ícone de OkAs renomeações de pacote importadas devem ser agrupadas no início da parte declarativa e ser alfabeticamente classificadas.

Ícone de Dedo IndicadorA renomeação pode ser utilizada localmente sempre que aprimorar a legibilidade (não há penalidade de tempo de execução para isso). Os tipos podem ser renomeados como subtipos sem restrição.

Conforme mostrado na seção dos comentários, renomear geralmente fornece uma forma elegante e passível de manutenção para documentar o código-por exemplo, para fornecer um nome simples para algum objeto complexo ou para refinar localmente o significado de um tipo. O escopo do identificador de renomeação deve ser escolhido para evitar o princípio da confusão.

Ícone de Dedo IndicadorAs exceções de renomeação permitem que as exceções sejam fatoradas entre diversas unidades-por exemplo, dentre todas as instanciações de um pacote genérico. Observe que, em um pacote derivado de um tipo, as exceções potencialmente levantadas pelos subprogramas derivados devem ser exportadas novamente, juntamente com o tipo derivado, para evitar que os clientes tenham "with" do pacote original:

with Inner_Defs;
package Exporter is ...
procedure May_Raise_Exception; -- Raises exception Inner_Defs.Bad_Schmoldu when ... ...
Bad_Schmoldu : exception renames Inner_Defs.Bad_Schmoldu; ...

Ícone de Dedo IndicadorOs subprogramas de renomeação com diferentes valores padrão para os parâmetros "in" podem permitir a fatoração de código simples e aprimorar a legibilidade:

procedure Alert (Message : String;
                 Beeps   : Natural);
procedure Bip (Message : String := "";
                   Beeps   : Natural := 1)
        renames Alert;
procedure Bip_Bip (Message : String := "";
                   Beeps   : Natural := 2) 
        renames Alert;    procedure Message (Message : String;
                   Beeps   : Natural := 0)
        renames Alert;    procedure Warning (Message : String;
                   Beeps   : Natural := 1)
        renames Alert;    

Ícone de OkEvite utilizar o nome da entidade renomeada (o antigo nome) dentro do escopo imediato da declaração de renomeação; utilize apenas o identificador ou o símbolo do operador introduzido pela declaração de renomeação (o novo nome).

Observação Sobre Cláusulas Use

Por vários anos, havia uma controvérsia de cláusula "use" na comunidade Ada, que, às vezes, se aproximava de uma guerra religiosa. Ambas as partes utilizaram diversos argumentos que geralmente não são escalados bem em projetos grandes ou exemplos que estejam muito fora da realidade-ou deliberadamente desiguais.

Os defensores da cláusula "use" alegam que ela aumenta a legibilidade e fornecem exemplos de nomes especialmente ilegíveis, longos e redundantes, que se beneficiariam de serem renomeados, se utilizados várias vezes. Eles também alegam que um compilador Ada possa resolver a sobrecarga, o que é verdade, mas um humano sendo submetido a um grande programa Ada não pode executar uma resolução de sobrecarga como um compilador e certamente não tão rápido. Eles alegam que os APSEs sofisticados, como o Ambiente Rational, inutilizam os nomes explícitos completos; mas isso não é verdadeiro-o usuário não precisa pressionar [Definition] para cada identificador em dúvida. O usuário não tem que adivinhar, mas deve conseguir ver imediatamente quais objetos e quais abstrações são utilizados. Os defensores de Rosen da cláusula "use" negam seus perigos em potencial na manutenção do programa e sugerem a atribuição de um grau F ao programador que cria tais riscos; acreditamos que os nomes completos eliminam esse risco.

Se os métodos sugeridos acima para aliviar o impacto da restrição nas cláusulas "use" aparentemente precisarem de muita digitação, considere a conclusão de Norman H. Cohen: " Todo o tempo economizado durante a escrita de um programa será perdido inúmeras vezes quando o programa for revisado, depurado e mantido."

Finalmente, foi mostrado que em grandes sistemas a ausência das cláusulas "use" aprimora o tempo de compilação, reduzindo o código extra de consulta nas tabelas de símbolo.

O leitor interessado em aprender mais sobre a controvérsia da cláusula use pode consultar as seguintes origens:

D. Bryan, "Dear Ada," Ada Letters, 7, 1, January-February 1987, pp. 25-28.

J. P. Rosen, "In Defense of the Use Clause," Ada Letters, 7, 7, November-December 1987, pp. 77-81.

G. O. Mendal, "Three Reasons to Avoid the Use Clause," Ada Letters, 8, 1, January-February 1988, pp. 52-57.

R. Racine, "Why the Use Clause Is Beneficial," Ada Letters, 8, 3, May-June 1988, pp. 123-127.

N. H. Cohen, Ada as a Second Language, McGraw-Hill (1986), pp. 361-362.

M. Gauthier, Ada-Un Apprentissage, Dunod-Informatique, Paris (1989), pp. 368-370.]


Capítulo 9

Estrutura do Programa e Problemas de Compilação

Decomposição de Pacotes

Existem duas maneiras fundamentais de se decompor um grande pacote "lógico", resultante de uma fase de design inicial em várias unidades menores da biblioteca Ada que são mais fáceis de gerenciar, compilar, manter e entender:

a) A decomposição aninhada

Essa abordagem enfatiza o uso de subunidades e/ou subpacotes Ada. Os principais subprogramas, corpos de tarefas e corpos internos de pacote são sistematicamente separados. O processo é recursivamente repetido dentro dessas subunidades/subpacotes.

b) A decomposição flat

O pacote lógico é decomposto em uma rede de pacotes menores que são interconectados por cláusulas "with" e o pacote lógico original é em grande parte um skin de nova exportação (ou um artefato de design que não existe mais).

Cada abordagem possui suas vantagens e desvantagens. A decomposição aninhada requer menos código para ser escrito e conduz à nomenclatura mais simples (vários identificadores não precisam de prefixação); e, no Ambiente Rational mínimo, a estrutura é muito visível na imagem de biblioteca e a estrutura é mais fácil de ser transformada (comandos Ada.Make_Separate, Ada.Make_Inline). A decomposição simples geralmente conduz a menos recompilação e estrutura melhor ou mais limpa (especialmente nos limites do subsistema); também estimula a reutilização. Isso também torna mais fácil gerenciar as ferramentas de recompilação automática e gerenciamento de configuração. No entanto, com a estrutura simples, há um risco maior de violar o design original executando "with" de alguns dos pacotes de nível inferior que foram criados na decomposição.

Ícone de OkO nível de aninhamento deve ser limitado para três para os subprogramas e para dois nos pacotes; não aninhe os pacotes dentro dos subprogramas.

package Level_1 is
    package Level_2 is
package body Level_1 is
    procedure Level_2 is
        procedure Level_3 is    

Ícone de Dedo IndicadorUtilize os stubs do corpo para as unidades aninhadas ("corpos separados") quando ocorrer o seguinte:

Estrutura das Partes Declarativas

Especificação do Pacote

Ícone de OkA parte declarativa de uma especificação do pacote contém declarações que devem ser organizadas na seguinte seqüência:

1) Declaração de renomeação para o pacote em si

2) Declaração de renomeação para as entidades importadas

3) Cláusulas "Use"

4) Números nomeados

5) Declarações de tipo e subtipo

6) Restrições

7) Declarações de exceção

8) Especificações exportadas do subprograma

9) Os pacotes aninhados, se houver

10) Parte privada.

Ícone de Dedo IndicadorPara obter um pacote que apresenta diversos tipos principais, pode ser melhor ter vários conjuntos de declarações relacionadas:

5) Declarações de tipo e subtipo para A

6) Restrições

7) Declarações de exceção

8) Especificações exportadas do subprograma para as operações em A

5) Declarações de tipo e subtipo para B

6) Restrições

7) Declarações de exceção

8) Especificações exportadas do subprograma para as operações em B

Etc.

Ícone de Dedo IndicadorQuando a parte declarativa for grande (>100 linhas), utilize os blocos de comentários para delimitar as diversas seções.

Corpo do Pacote

Ícone de OkA parte declarativa das declarações do corpo do pacote contém declarações que devem ser organizadas na seguinte seqüência:

1) Declarações de renomeação (para as entidades importadas)

2) Cláusulas "Use"

3) Números nomeados

4) Declarações de tipo e subtipo

5) Restrições

6) Declarações de exceção

7) Especificações locais do subprograma

8) Corpos locais do subprograma

9) Corpos exportados do subprograma

10) Corpos aninhados do pacote, se houver.

Outras Construções

Ícone de OkOutras partes declarativas, como nos corpos do subprograma, corpos de tarefa e instruções de bloco seguem o mesmo padrão geral.

Cláusulas de Contexto

Ícone de OkUtilize uma cláusula "with" por unidade de biblioteca importada. Classifique as cláusulas with em ordem alfabética. Se uma cláusula "use" em uma unidade com "with" for apropriada, ela deve seguir imediatamente a cláusula "with" correspondente. Consulte abaixo para o pragma Elaborar.

Pedido de Elaboração

Ícone de OkNão conte com o pedido da elaboração das unidades da biblioteca para alcançar qualquer efeito específico.

Cada implementação Ada está livre para escolher uma estratégia para calcular o pedido de elaboração, contanto que satisfaça as regras bem simples indicadas no Manual de Referência Ada [ISO87]. Algumas implementações utilizam estratégias mais inteligentes que outras (como elaboração dos corpos assim que viáveis após a especificação correspondente) e algumas implementações não se incomodam em serem inteligentes (especialmente para instanciações genéricas), conduzindo a problemas de portabilidade muito severas.

Há três origens principais para o erro infame "access before elaboration" durante a elaboração do programa (que geralmente provoca a exceção Program_Error):

task type T;
type T_Ptr is access T;
SomeT : T_Ptr := new T; -- Acesso antes da elaboração. 

Ícone de OkPara evitar problemas nos aplicativos de portabilidade de um compilador Ada a outro, o programador deve eliminar os problemas reestruturando o código (que nem sempre é possível) ou tomar controle do pedido de elaboração através de um pragma Elaborate, utilizando a seguinte estratégia:

Na cláusula context de uma unidade Q, um pragma Elaborate deve ser aplicado a cada unidade P que aparece em uma cláusula "with":

Além disso, se P exportar um tipo T, de modo que a elaboração dos objetos do tipo T chame uma função no pacote R, a cláusula context de Q deve conter:

with R;
pragma Elaborate (R);    

mesmo se não houver referências diretas para R em Q!

Praticamente, pode ser mais fácil (mas nem sempre possível) indicar a regra que o pacote P deve incluir:

with R;
pragma Elaborate (R);    

O pacote Q deve simplesmente carregar:

with P;
pragma Elaborate (P);    

fornecendo, portanto, o pedido de elaboração correto pela transitividade.


Capítulo 10

Coincidência

Ícone de Atenção com a MãoRestrinja o uso de tarefas.

As tarefas são um recurso muito poderoso, mas são delicados de serem utilizados. O código extra grande em tempo e espaço pode estar associado ao uso indiscreto das tarefas. As pequenas alterações em alguma parte do sistema podem arriscar complemente a vitalidade de um conjunto de tarefas, conduzindo à fraqueza e/ou conflitos. É difícil testar e depurar os programas de criação de tarefas. Portanto, o uso de tarefas, seus posicionamentos e suas interações é uma decisão de nível do projeto. As tarefas não podem ser utilizadas de uma maneira oculta ou escritas por programadores inexperientes. O modelo de criação de tarefas de um programa Ada precisa ficar visível e inteligível.

A menos que haja suporte efetivo do hardware paralelo, as tarefas devem ser introduzidas apenas quando a simultaneidade for realmente necessária. Esse será o caso quando ao expressar ações que dependam do tempo: atividades periódicas ou introdução de tempos limites ou ações que dependam de um evento externo como uma interrupção ou chegada de uma mensagem externa. As tarefas também precisam ser introduzidas para desacoplar outras atividades, como: armazenamento em buffer, enfileiramento, dispatch e acesso de sincronização aos recursos comuns.

Ícone de OkEspecifique o tamanho da pilha da tarefa com uma cláusula de comprimento 'Storage_Size.

Para as mesmas razões e sob as mesmas circunstâncias que conduziram ao requisito que as coletas possuem nas cláusulas de comprimento (seção "Access Types", acima), o tamanho de uma tarefa deve ser especificado nos casos em que a memória é um recurso precioso. Para isso, sempre declare as tarefas de um tipo explicitamente declarado (uma vez que a cláusula de comprimento pode ser aplicada apenas a um tipo). Uma chamada de função pode ser utilizada para dimensionar dinamicamente a pilha.

Nota: Pode ser muito difícil adivinhar a quantidade de pilha requerida por tarefa. Para facilitar, o sistema do tempo de execução pode ser instrumentado com um mecanismo "high-water mark".

Ícone de OkUtilize uma rotina de tratamento de exceção no corpo de uma tarefa para evitar ou, pelo menos, relatar a morte inexplicada de uma tarefa.

As tarefas que não manipulam as exceções são encerradas-geralmente silenciosamente. Caso seja totalmente viável, tente relatar a natureza do encerramento, especialmente Storage_Error. Isso permitirá o ajuste fino do tamanho da pilha. Observe que isso requer que a alocação (primitiva nova) seja encapsulada em um subprograma que exporte novamente uma exceção diferente de Storage_Error.

Ícone de OkCrie tarefas durante a elaboração do programa.

Para as mesmas razões e sob as mesmas circunstâncias que conduziram ao requisito no qual as coletas serão alocadas durante a elaboração do programa (seção "Access Types", acima), a criação de tarefas inteira deve ser criada bem cedo na inicialização do programa. É melhor fazer com que o programa não seja iniciado devido ao esgotamento de memória do que encerrar alguns dias mais tarde.

Nas regras subseqüentes, a distinção é feita entre as tarefas service e as tarefas application. As tarefas de serviço são tarefas pequenas e algoritmicamente simples que são utilizadas para fornecer a "cola" entre as tarefas relacionadas ao aplicativo. Os exemplos das tarefas de serviço (ou tarefas intermediárias) incluem buffers, transportadores, relays, agentes, monitores e assim por diante, que geralmente fornecem sincronização, desconexão, buffer e serviços de espera. As tarefas do aplicativo, como o nome indica, são relacionadas mais diretamente às funções primárias do aplicativo.

Ícone de OkEvite as tarefas híbridas: as tarefas de aplicativos devem se tornar puros responsáveis pela chamada; as tarefas de serviços devem se tornar puros aceitadores da chamada.

Um puro aceitador da chamada inclui uma tarefa que contém apenas as instruções aceitas ou esperas seletivas e sem chamadas de entrada.

Ícone de OkEvite circularidades no gráfico de chamadas de entrada.

Isso reduzirá consideravelmente o risco de conflitos. Evite circularidades, pelo menos, no estado fixo do sistema, se elas não puderem ser completamente evitadas. Essas duas regras também facilitam o entendimento da estrutura.

Ícone de Atenção com a MãoRestrinja o uso das variáveis compartilhadas.

Fique ciente especialmente das variáveis compartilhadas ocultas-ou seja, variáveis que ficam ocultas nos corpos do pacote, por exemplo, e acessadas por primitivos visíveis a diversas tarefas. As variáveis compartilhadas podem ser utilizadas em casos extremos para sincronização de acesso a estruturas de dados comuns, quando o custo dos encontros for muito alto. Verifique se o pragma Shared é suportado efetivamente.

Ícone de Atenção com a MãoRestrinja o uso de instruções de interrupção.

A instrução de interrupção é reconhecida universalmente como um dos primitivos mais perigosos e prejudiciais do idioma. Seu uso para terminar as tarefas incondicionalmente (e quase de for assíncrona) faz com que seja quase impossível pensar sobre o comportamento de uma determinada estrutura de criação de tarefas. No entanto, há circunstâncias muito limitadas nas quais é necessária uma instrução de interrupção.

Exemplo: Alguns serviços de baixo nível não recebem nenhum recurso para o tempo limite. A única forma de introduzir um tempo limite é fazer com que o serviço seja fornecido por alguma tarefa de agente auxiliar, aguardar (com um tempo limite) por uma resposta do agente e, em seguida, eliminar o agente com uma interrupção, se o serviço não for fornecido dentro do tempo de espera.

Uma interrupção é tolerável quando puder ser demonstrado que apenas outro anulador e anulado podem ser afetados-por exemplo, quando nenhuma outra tarefa puder possivelmente chamar a tarefa abortada.

Ícone de Atenção com a MãoRestrinja o uso das instruções de retardo.

A suspensão arbitrária de uma tarefa pode conduzir a graves problemas no planejamento, que são difíceis de serem rastreados e corrigidos.

Ícone de Atenção com a MãoRestrinja o uso dos atributos 'Count, 'Terminated e 'Callable.

O atributo 'Count deve ser utilizado apenas como uma indicação bruta e as decisões de planejamento não devem ser baseadas em seus valores como sendo zero ou não, uma vez que o número real de tarefas em espera pode mudar entre o tempo em que o atributo é avaliado e o tempo em que seu valor é utilizado.

Utilize as chamadas de entrada condicionais (ou construção equivalente com aceitação) para verificar confiavelmente a ausência das tarefas de espera.

select
    The_Task.Some_Entry;
else
    -- do something else
end select;    

É preferível fazer o seguinte:

if The_Task.Some_Entry'Count > 0 then
    The_Task.Some_Entry;
else
    -- do something else
        end if;

O atributo 'Terminated é significativo apenas quando produzir True, e 'Callable quando produzir False; portanto, limitando consideravelmente sua utilidade. Eles não devem ser utilizados para fornecer sincronização entre as tarefas durante o encerramento do sistema.

Ícone de Atenção com a MãoRestrinja o uso das prioridades.

As prioridades no Ada possuem um impacto limitado no planejamento. Em específico, as prioridades das tarefas em espera nas entradas não são consideradas para solicitar as filas de entrada ou para selecionar a entrada que atenderá em uma espera seletiva. Isso pode conduzir à inversão de prioridades (consulte [GOO88]). As prioridades são utilizadas pelo planejador apenas para selecionar a próxima tarefa a ser executada entre as tarefas prontas para execução. Devido ao risco da inversão de prioridade, não conte com as prioridades para exclusão mútua.

Ao utilizar as famílias de entradas, é possível dividir a fila de entrada em várias subfilas e com isso geralmente é possível introduzir um conceito explícito de urgência.

Se as prioridades não forem necessárias, não designe qualquer prioridade a qualquer tarefa.

Ícone de OkAssim que uma prioridade for designada a uma tarefa, designe uma prioridade a todas as tarefas no aplicativo.

Essa regra é necessária porque as prioridades das tarefas sem um pragma Priority são indefinidas.

Ícone de OkPara portabilidade, mantenha pequeno o número de níveis de prioridade.

O intervalo do subtipo System.Priority é definido pela implementação e a experiência mostra que o intervalo real disponível varia de forma enorme de sistema para sistema. Além disso, é uma boa idéia definir centralmente as prioridades, fornecendo a elas nomes e definições, em vez de utilizar os literais de inteiros em todas as tarefas. Ter esse pacote System_Priorities central facilita a portabilidade e, juntamente com a regra anterior, permite a fácil localização das especificações de todas as tarefas.

Ícone de Dedo IndicadorPara evitar a tendência em tarefas cíclicas, programe a instrução de atraso a ser considerada no tempo de processamento, código extra e preempção de tarefa:

Next_Time := Calendar.Clock;
loop
    -- Execute a tarefa.
    Next_Time := Next_Time + Period;
    delay Next_Time - Clock;
end loop;    

Observe que Next_Time - Clock pode ser negativo, indicando que a tarefa cíclica está sendo executada com atraso. Pode ser possível eliminar um ciclo.

Ícone de Dedo IndicadorPara garantir a capacidade de planejamento, designe as prioridades para as tarefas cíclicas de acordo com o Algoritmo de Planejamento Monotônico de Taxa-ou seja a mais alta prioridade para a tarefa mais freqüente. (Consulte [SHA90] para obter detalhes adicionais.)

Ícone de Dedo IndicadorDesigne uma prioridade mais alta para servidores intermediários muito rápidos: monitores, buffers.

Mas, em seguida, certifique-se de que esses servidores não se comprometem outras tarefas. Documente essa prioridade no código de modo que possa ser respeitada durante a manutenção do programa.

Ícone de Dedo IndicadorPara minimizar o efeito de "jitter", conte com as amostras de entrada de marca de hora ou dados de saída, e não com o próprio período.

Ícone de OkEvite a espera ocupada (polling).

Certifique-se de que as tarefas esperam com chamadas de seleção ou de entrada, ou que sejam atrasadas, em vez de procurar furiosamente por algo a ser feito.

Ícone de Dedo IndicadorPara cada encontro, certifique-se de que, pelo menos, um lado está aguardando e que apenas um lado possua uma chamada de entrada em condicional ou chamada de entrada marcada ou esperas.

Caso contrário, de forma notável em loops, há o risco da execução do código em uma condição de disputa, altamente semelhante em resultado de uma espera ocupada. Isso pode ser agravado pelo uso insuficiente das prioridades.

Ícone de Dedo IndicadorAo encapsular as tarefas, certifique-se de deixar algumas de suas características especiais altamente visíveis.

Se as chamadas da entrada forem ocultas nos subprogramas, certifique-se de que o leitor da especificação desses subprogramas estejam cientes de que a chamada a esse subprograma pode ser bloqueada. Além disso, especifique se a espera é limitada; se estiver, forneça alguma estimativa do limite superior. Utilize uma convenção de nomenclatura para indicar a espera em potencial (seção "Subprograms", acima).

Se a elaboração de um pacote, a chamada de um subprograma ou a instanciação de uma unidade genérica ativarem uma tarefa, torne esse fato visível ao cliente:

package Mailbox_Io is
    -- Esse pacote elabora uma tarefa interna Control
    -- que sincroniza todo o acesso à
    -- caixa postal externa
    procedure Read_Or_Wait
        (Name: Mailbox.Name; Mbox: in out Mailbox.Object);
        --
        -- Bloqueando (espera não limitada). 

Ícone de OkNão dependa de qualquer pedido específico para a seleção de entrada em uma espera seletiva.

Se forem necessárias algumas justificativas na seleção das tarefas enfileiradas nas entradas, faça isso verificando explicitamente as filas sem espera no pedido desejado e, em seguida, aguarde em todas as entradas. Não utilize 'Count.

Ícone de OkNão dependa de qualquer pedido de ativação específica para as tarefas elaboradas na mesma parte declarativa.

Se for procurado um pedido de inicialização específico, isso deve ser feito através de um encontro com as entradas especiais de inicialização.

Ícone de OkImplemente as tarefas a serem terminadas de forma normal.

A menos que a natureza do aplicativo necessite que as tarefas, depois de ativadas, sejam executadas eternamente, as tarefas devem ser terminadas, através da conclusão normal ou através de uma alternativa de terminação. Isso pode ser impossível para tarefas cujo master é um pacote no nível de biblioteca, uma vez que o Manual de Referência Ada não especifica sob qual condição elas devem ser terminadas.

Se a estrutura dependente de master não permitir a terminação limpa, as tarefas devem fornecer e esperar por entradas especiais de encerramento, que são chamadas durante o encerramento do sistema.


Capítulo 11

Manipulação de Erros e Exceções

A filosofia geral é utilizar as exceções apenas para os erros: erros lógicos e de programação, erros de configuração, dados corrompidos, esgotamento de recursos, etc. A regra geral é que os sistemas em condição normal e na ausência de sobrecarga ou falha de hardware não levante qualquer exceção.

Ícone de Dedo IndicadorUtilize as exceções para manipular os erros lógico e de programação, erros de configuração, dados corrompidos, esgotamento de recursos. Relate as exceções pelo mecanismo de log apropriado assim que possível, incluindo no ponto de elevação.

Ícone de Dedo IndicadorMinimize o número de exceções exportadas de uma determinada abstração.

Em grandes sistemas, ter que manipular um grande número de exceções em cada nível dificulta a leitura e a manutenção do código. Às vezes, o processamento da exceção atrapalha o processamento normal.

Há várias formas de minimizar o número de exceções:

Ícone de OkNão propague as exceções não especificadas no design.

Ícone de OkEvite uma alternativa when others nas rotinas de tratamento de exceção, a menos que a exceção capturada seja levantada novamente.

Isso permite que haja alguma organização interna local sem interferir nas exceções que não podem ser manipuladas nesse nível:

exception
    when others => 
        if Io.Is_Open (Local_File) then
            Io.Close (Local_File);
        end if;
        raise;
end;    

Outro local em que uma alternativa when others pode ser utilizada é na parte inferior de um corpo de tarefa.

Ícone de OkNão utilize as exceções para eventos freqüentes e antecipados.

Há várias inconveniências na utilização de exceções para representar condições que não são claramente erros:

Por exemplo, não utilize uma exceção como alguma forma de valor extra retornado por uma função (como Value_Not_Found em uma procura); utilize um procedimento com um parâmetro "out" nem o introduza algum valor especial indicando Not_Found, nem compacte o tipo retornado em um registro com um Not_Found discriminante.

Ícone de OkNão utilize as exceções para implementar as estruturas de controle.

Esse é um caso especial da regra anterior: as exceções não devem ser utilizadas como uma forma de instrução "goto".

Ícone de Dedo IndicadorAo capturar as exceções predefinidas, coloque a rotina de tratamento em um quadro bem pequeno que cerca a construção que a levanta.

As exceções predefinidas como Constraint_Error, Storage_Error e assim por diante podem ocorrer em vários locais. Se uma exceção desse tipo precisar ser capturada por alguma razão específica, a rotina de tratamento deve ser limitada no espaço, assim que possível:

            begin
    Ptr := new Subscriber.Object;
exception
    when Storage_Error => 
        raise Subscriber.Collection_Overflow;
end;    

Ícone de OkTermine as rotinas de tratamento de exceção nas funções com uma instrução "return" ou uma instrução "raise". Caso contrário, a exceção Program_Error será levantada no responsável pela chamada;

Ícone de Atenção com a MãoRestrinja a supressão de verificações.

Com os compiladores Ada atuais, as reduções potenciais no tamanho do código e os aumentos no desempenho são obtidas pela supressão das verificações se tornaram marginais. Portanto, a supressão das verificações deve ser restrita a partes bem limitadas do código que deve ser identificada (por medições) como gargalos de desempenho; ela nunca deve ser aplicada amplamente em um sistema inteiro.

Como resultado, não inclua um intervalo extra explícito e a verificação discriminante apenas para o caso improvável que alguém decidirá posteriormente para suprimir as verificações. Confie nos recursos de verificação de restrição internos Ad's.

Ícone de Dedo IndicadorNão propague exceções fora do escopo de suas declarações.

Isso tornará impossível para o código do cliente manipular explicitamente a exceção, diferente de uma alternativa when others, que pode não ser específica o suficiente.

Um resultado para essa regra é: ao exportar novamente um tipo por derivação, pense em exportar novamente as exceções que os subprogramas derivados possam levantar-renomeando, por exemplo. Caso contrário, os clientes terão que executar "with" do pacote de definição original.

Ícone de Dedo IndicadorSempre manipule Numeric_Error e Constraint_Error em conjunto.

O Quadro Ada decidiu que todas as circunstâncias teriam levantado Numeric_Error se Constraint_Error for levantado, em substituição.

Ícone de Dedo IndicadorCertifique-se de que os códigos de status possuam um valor apropriado.

Ao utilizar o código de status retornado pelos subprogramas como um parâmetro "out", sempre certifique-se de que um valor seja atribuído ao parâmetro "out", fazendo com que essa seja a primeira instrução executável no corpo do subprograma. Sistematicamente, torne todos os status um êxito por padrão ou uma falha por padrão. Considere todas as saídas possíveis do subprograma, incluindo as rotinas de tratamento de exceção.

Ícone de Dedo IndicadorExecute as verificações de segurança localmente; não espere que seu cliente faça isso.

Ou seja, se um subprograma produzir saída errônea, a menos que seja fornecida a entrada apropriada, instale o código no subprograma para detectar e relatar a entrada inválida de uma maneira controlada. Não confie em um comentário que informe ao cliente para transmitir os valores apropriados. É virtualmente garantido que, mais cedo ou mais tarde, esse comentário será ignorado, resultando em erros de difícil depuração, se os parâmetros inválidos não forem detectados.

Para obter informações adicionais, consulte [KR90b].


Capítulo 12

Programação de Baixo Nível

Essa seção trata dos recursos Ada que são uma priori não portáteis. Elas são definidas no capítulo 13 do Manual de Referência para a Linguagem de Programação Ada [ISO87] e os recursos específicos do compilador são descritos no "Apêndice F" fornecido pelos fornecedores do compilador Ada.

Cláusulas de Representação e Atributos

Ícone de Dedo IndicadorEstude cuidadosamente o Apêndice F do Manual de Referência Ada (e conduza os experimentos pequenos para garantir que sejam bem entendidos).

Ícone de Atenção com a MãoRestrinja o uso das cláusulas de representação.

As cláusulas de representação não são suportadas uniformemente de implementação para implementação. Seus usos contêm várias interrupções. Portanto, não devem ser utilizadas livremente em um sistema.

As cláusulas de representação devem fazer o seguinte:

As cláusulas de representação podem ser evitadas nos seguintes tipos de situações:

Exemplo:

Substitua:

type Foo is (Bla, Bli, Blu, Blo);
for Foo use (Bla => 1, Bli =>3, Blu => 4, Blo => 5);    

type Foo is (Invalid_0, Bla, Invalid_2, Bli, Blu, Blo);    

Ícone de OkAgrupe os tipos que possuem cláusulas de representação em pacotes claramente identificados como contendo o código dependente de implementação.

Ícone de OkNunca assuma um pedido específico no layout de registro.

Ícone de OkEm uma cláusula de representação de registro, sempre especifique o posicionamento de todos os discriminantes e faça isso especificando todos os componentes nas variantes.

Ícone de OkEvite as cláusulas de alinhamento.

Confie no compilador para fazer um bom trabalho; ele conhece as restrições de alinhamento de destino. O uso do programador das cláusulas de alinhamento provavelmente conduzirá posteriormente a conflitos no alinhamento.

Ícone de Dedo IndicadorCertifique-se da existência de campos gerados pelo compilador nos tipos compostos não restritos:

em registros: deslocamento de campos dinâmicos, índice de cláusula variante, bit restrito e assim por diante

nas matrizes: vetores dope.

Consulte o Apêndice F para o compilador para obter detalhes. Não se apoie naquilo que está escrito no capítulo 13 do Manual de Referência Ada [ISO87].

Conversões Não Verificadas

Ícone de Atenção com a MãoRestrinja o uso de Unchecked_Conversion.

A extensão do suporte para Unchecked_Conversion varia muito de um compilador Ada para outro, e seu comportamento preciso pode ser ligeiramente diferente, especialmente quando aplicado aos tipos compostos e tipos de acesso.

Ícone de OkEm uma instância Unchecked_Conversion, certifique-se de que os tipos de origem e de destino são restritos e possuem o mesmo tamanho.

Essa é a única maneira de alcançar alguma portabilidade limitada e evitar problemas na implementação-informações incluídas como vetores dope. Uma maneira de garantir que ambos os tipos possuem o mesmo tamanho é "agrupá-los" em um tipo de registro com uma cláusula de representação de registro.

Uma maneira de restringir o tipo é executar a instância em uma função "skin", em que a restrição é computada antecipadamente.

Ícone de Dedo IndicadorNão aplique Unchecked_Conversion para acessar valores ou tarefas.

Não apenas isso não é suportado por todos os sistemas (por exemplo, o compilador Rational nativo), mas também não deve ser assumido que:


Capítulo 13

Sumário

Recapitulamos aqui os fatos mais importantes a serem observados:

Recursos restritos (Ícone de Atenção com a Mão):

"Contras" Absolutos (Ícone de Dedo Indicador)


Referências

Esse documento é derivado diretamente de Diretrizes Ada: Recomendações para Designer e Programadores, Nota do Aplicativo Nº15, Rev. 1.1, Rational, Santa Clara, Ca., 1990. [KR90a]. No entanto, várias origens diferentes contribuíram para sua elaboração.

BAR88 B. Bardin & Ch. Thompson, "Composable Ada Software Components and the Re-export Paradigm", Ada Letters, VIII, 1, Jan.-Feb. 1988, p.58-79.

BOO87 E. G. Booch, Software Components with Ada, Benjamin/Cummings (1987)

BOO91 Grady Booch: Object-Oriented Design with Applications, Benjamin-Cummings Pub. Co., Redwood City, California, 1991, 580p.

BRY87 D. Bryan, "Dear Ada," Ada Letters, 7, 1, January-February 1987, pp. 25-28.

COH86 N. H. Cohen, Ada as a Second Language, McGraw-Hill (1986), pp. 361-362.

EHR89 D. H. Ehrenfried, Tips for the Use of the Ada Language, Application Note #1, Rational, Santa Clara, Ca., 1987.

GAU89 M. Gauthier, Ada-Un Apprentissage, Dunod-Informatique, Paris (1989), pp. 368-370.

GOO88John B. Goodenough and Lui Sha: "The Priority Ceiling Protocol," special issue of Ada Letters, Vol., Fall 1988, pp. 20-31.

HIR92 M. Hirasuna, "Using Inheritance and Polymorphism with Ada in Government Sponsored Contracts", Ada Letters, XII, 2, March/April 1992, p.43-56.

ISO87 Reference Manual for the Ada Programming Language, International Standard ISO 8652:1987.

KR90a Ph. Kruchten, Ada Guidelines: Recommendations for Designer and Programmers, Application Note #15, Rev. 1.1, Rational, Santa Clara, Ca., 1990.

KR90b Ph. Kruchten, "Error-Handling in Large, Object-Based Ada Systems," Ada Letters, Vol. X, No. 7, (Sept. 1990), pp. 91-103.

MCO93 Steve McConnell, Code Complete-A Practical Handbook of Software Construction, Microsoft® Press, Redmond, WA, 1993, 857p.

MEN88 G. O. Mendal, "Three Reasons to Avoid the Use Clause," Ada Letters, 8, 1, January-February 1988, pp. 52-57.

PER88 E. Perez, "Simulating Inheritance with Ada", Ada letters, VIII, 5, Sept.-Oct. 1988, p. 37-46.

PLO92 E. Ploedereder, "How to program in Ada 9X, Using Ada 83", Ada Letters, XII, 6, November 1992, pp. 50-58.

RAC88 R. Racine, "Why the Use Clause Is Beneficial," Ada Letters, 8, 3, May-June 1988, pp. 123-127.

RAD85 T. P. Bowen, G. B. Wigle & J. T. Tsai, Specification of Software Quality Attributes, Boeing Aerospace Company, Rome Air Development Center, Technical Report RADC-TR-85-37 (3 volumes).

ROS87 J. P. Rosen, "In Defense of the Use Clause," Ada Letters, 7, 7, November-December 1987, pp. 77-81.

SEI72 E. Seidewitz, "Object-Oriented Programming with Mixins in Ada", Ada Letters, XII, 2, March/April 1992, p.57-61.

SHA90 Lui Sha and John B. Goodenough: "Real-Time Scheduling Theory and Ada," Computer, Vol. 23, #4 (April 1990), pp. 53-62.)

SPC89 Software Productivity Consortium: Ada Quality and Style-Guidelines for the Professional Programmer, Van Nostrand Reinhold (1989)

TAY92 W. Taylor, Ada 9X Compatibility Guide, Version 0.4, Transition Technology Ltd., Cwmbran, Gwent, U.K., Nov. 1992.

WIC89 B. Wichman: Insecurities in the Ada Programming Language, Report DITC137/89, National Physical Laboratory (UK), January 1989.


Glossário

A maioria dos termos utilizados nesse documento são definidos no Apêndice D do Manual de Referência da Linguagem de Programação Ada, [ISO87]. Os termos adicionais são definidos aqui:

ADL: Ada como uma Linguagem de Design; refere-se à maneira como o Ada é utilizado para expressar os aspectos de um design; também conhecido como PDL ou Program Design Language.

Ambiente: o ambiente de desenvolvimento de software Ada em uso.

Comutador de biblioteca: No Ambiente Rational, uma opção de compilação que se aplica a uma biblioteca inteira de programa.

Mundo de modelo: No Ambiente Rational, uma biblioteca especial que é utilizada para capturar o projeto uniforme-configurações de comutação de biblioteca ampla.

Mutável: Propriedade de um registro cujos discriminantes possuem valores padrão; um objeto de um tipo mutável pode ser designado a qualquer valor do tipo, mesmo os valores que fazem com que sejam alterados seus discriminantes, daí sua estrutura.

Skin: Um subprograma cujo corpo atua unicamente como um relay. O ideal é que contenha apenas uma instrução: uma chamada para outro subprograma, com um conjunto idêntico de parâmetros ou parâmetros que possam ser convertidos de e para o parâmetro.

PDL: Program Design Language.