Copyright (c) 1999 Scott Ambler, Ambysoft, Inc.

As Diretrizes de Codificação Java são fornecidas sob licença de Scott Ambler, Ambysoft Inc., www.ambysoft.com. Elas foram reformatadas para inclusão no Processo Unificado da Rational.


Conteúdo

1      Introdução
       
1.1    A Primeira e Última Diretriz

2    Padrões de Codificação
     
2.1    Convenções de Nomenclatura
      2.2    Convenções de Documentação
              2.2.1  Tipos de Comentários Java
              2.2.2  Um Visão Geral Rápida do javadoc   

3    Padrões para Funções-membro
     
3.1    Nomeando Funções-membro
              3.1.1    Nomeando Funções-membro do Acessador
                         3.1.1.1    Getters
                         3.1.1.2    Setters
      3.2    Nomeando Construtores
      3.3    Visibilidade da Função-membro
      3.4    Documentando Funções-membro
               3.4.1    O Cabeçalho da Função-membro
               3.4.2    Documentação Interna
      3.5    Técnicas para Gravar Código Limpo
               3.5.1    Documente seu Código
               3.5.2    Utilize Parágrafo ou Recue seu Código
               3.5.3    Utilize Espaço em Branco em seu Código
               3.5.4    Siga a Regra dos 30 Segundos
               3.5.5    Grave Linhas de Comandos Simples e Curtas
               3.5.6    Especifique a Ordem das Operações

4    Padrões para Campos e Propriedades
      4.1    Nomeando Campos
              4.1.1    Nomeando Componentes (widgets)
                         4.1.1.1    Alternativa para Nomenclatura de Componentes: Notação Húngara
                         4.1.1.2    Alternativa para Nomenclatura de Componentes: Notação Húngara com Sufixo
                         4.1.1.3     Definir Padrões de Nome de Componente
              4.1.2    Nomeando Constantes
               4.1.3    Nomeando Coletas
      4.2    Visibilidade do Campo
               4.2.1    Não "Ocultar" Nomes
      4.3    Documentando um Campo
      4.4    Utilizando Funções-membro do Acessador
              4.4.1    Por que Utilizar Acessadores?               
                          4.4.1.1   Quando Não Utilizar Acessadores
              4.4.2    Nomeando Acessadores
              4.4.3    Técnicas Avançadas para Acessadores
                         4.4.3.1    Inicialização Adiada
                         4.4.3.2    
Acessadores para Constantes
                         4.4.3.3    
Acessadores para Coleções
                         4.4.3.4    Acessando Vários Campos Simultaneamente   
      4.5    Visibilidade dos Acessadores
      4.6    Sempre Inicialize Campos Estáticos

5    Padrões para Variáveis Locais
      5.1    Nomeando Variáveis Locais
                  5.1.1    Nomeando Fluxos
                   5.1.2    Nomeando Contadores de Loop
                   5.1.3    Nomeando Objetos de Exceção
        5.2   Declarando e Documentando Variáveis Locais
               5.2.1     Comentários Gerais sobre Declaração

6    Padrões para Parâmetros para Funções-membro
     
6.1    Nomeando Parâmetros
      6.2    Documentando Parâmetros
               6.2.1    Utilize Interfaces para Tipos de Parâmetros

7    Padrões para Classes, Interfaces, Pacotes e Unidades de Compilação
      7.1    Padrões para Classes
                7.1.1    Nomeando Classes
                7.1.2    Documentando uma Classe
                7.1.3    Declarações de Classes
                7.1.4    Minimize a Interface Pública e Protegida
                            7.1.4.1    Defina Primeiro a Interface Pública
      7.2     Padrões para Interfaces
                7.2.1    Nomeando Interfaces
                            7.2.1.1    Alternativa
                7.2.2    Documentando Interfaces
      7.3     Padrões para Pacotes
                7.3.1    Nomeando Pacotes
                7.3.2    Documentando Pacotes
      7.4     Padrões para Unidades de Compilação
                7.4.1    Nomeando Unidades de Compilação
                7.4.2    Documentando Unidades de Compilação

8    Tratamento de Erros e Exceções

9    Padrões e Questões Diversas
      9.1    Reutilizando
      9.2    Importando Classes
      9.3    Otimizando Código Java
      9.4    Gravando Ferramentas de Teste Java

10    Padrões de Sucesso
       10.1    Utilizando esses Padrões com Eficácia
       10.2    Outros Fatores que Levam a Gravar Código com Sucesso

11    Resumo
       
11.1    Convenções de Nomenclatura Java
        11.2    Convenções de Documentação Java
                   11.2.1    Tipos de Comentários Java
                   11.2.2    O que Documentar
        11.3    Convenções de Codificação Java (geral)

12    Referências

13    Glossário


1    Introdução

Este documento descreve a coleção de padrões, convenções e diretrizes para gravar código Java sólido. Elas são baseadas em princípios de engenharia de software comprovados e estáveis que levam a um código de fácil entendimento, manutenção e aprimoramento. Além disso, seguindo esses padrões de codificação, sua produtividade como desenvolvedor Java deve aumentar de maneira notável. A experiência mostra que tomar tempo para gravar código de alta qualidade logo do início, será muito mais fácil modificá-lo durante o processo de desenvolvimento. Finalmente, seguir um conjunto comum de padrões de codificação leva a maior consistência, tornando as equipes de desenvolvedores significativamente mais produtivas.

1.1    A Primeira e Última Diretriz 

Utilize 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 é obrigatório.


2    Padrões de Codificação

Os padrões de codificação para Java são importantes porque levam a maior consistência dentro do código e com o código de seus colegas de trabalho. Maior consistência leva a código que é mais fácil de entender, o que significa ser mais fácil de desenvolver e manter. Isso reduz o custo geral dos aplicativos criados.

Você precisa se lembrar que seu código Java existirá por um longo tempo; bem depois que você tiver se deslocado para outros projetos. Uma meta importante durante o desenvolvimento é assegurar que seja possível transicionar seu trabalho para outro desenvolvedor ou para outra equipe de desenvolvedores, para que eles possam continuar a manter e aprimorar seu trabalho sem ter de investir um esforço excessivo para entender seu código. Um código que é difícil de entender corre o risco de ser jogado fora e reescrito.

2.1    Convenções de Nomenclatura

Discutiremos as convenções de nomenclatura em todos os padrões, portanto vamos montar a cena com alguns conceitos básicos:

  1. Utilize descritores completos em inglês, que descrevam com precisão a variável, o campo e a classe; por exemplo, utilize nomes como firstName, grandTotal ou CorporateCustomer. Embora nomes como x1, y1 ou fn sejam fáceis de digitar porque são curtos, eles não fornecem nenhuma indicação do que representam, resultando em código difícil de entender, manter e aprimorar.
  2. Utilize a terminologia aplicável ao domínio. Se os usuários referirem-se aos clientes como freguês, utilize o termo Customer para a classe, não Client. Muitos desenvolvedores cometem o engano de criar termos genéricos para conceitos, quando termos perfeitamente bons já existem no segmento de mercado ou domínio.
  3. Utilize nomes compostos por letras maiúsculas e minúsculas para torná-los legíveis. Utilize letras minúsculas em geral, mas utilize maiúsculas para a primeira letra dos nomes das classes e interfaces, bem como a primeira letra de qualquer palavra não-inicial. [KAN97]
  4. Utilize abreviações com moderação, mas se o fizer, utilize-as com inteligência. Isso significa que você deve manter uma lista de formas curtas padrão (abreviações), deve escolhê-las com sabedoria e deve utilizá-las de forma consistente. Por exemplo, se deseja utilizar uma forma curta para a palavra "number", utilize nbr, no ou num, documente qual foi a escolhida (na realidade não importa qual delas) e utilize apenas ela.
  5. Evite nomes longos (< 15 caracteres é uma boa idéia). Embora o nome de classe PhysicalOrVirtualProductOrService possa parecer um bom nome de classe no momento, esse nome é simplesmente muito longo e você deve considerar renomeá-lo para algo mais curto, talvez algo como Offering.
  6. Evite nomes semelhantes ou que sejam diferente apenas em maiúsculas e minúsculas. Por exemplo, os nomes de variáveis persistentObject e persistentObjects não devem ser utilizados em conjunto, e nem anSqlDatabase e anSQLDatabase.
  7. Evite sublinhados de início ou encerramento. Nomes com sublinhados de início ou encerramento normalmente são reservados para finalidades do sistema e não devem ser utilizados para quaisquer nomes criados pelo usuário. Ainda mais importante, os sublinhados são maçantes e difíceis de se digitar, portanto tente evitar seu uso sempre que possível.

2.2    Convenções de Documentação

Também discutiremos convenções de documentação, portanto vamos discutir primeiro alguns dos conceitos básicos:

  1. Os comentários devem aumentar a clareza do código. A razão pela qual você documenta seu código é torná-lo mais compreensível para você, seus colegas de trabalho e qualquer outro desenvolvedor que venha depois de você.
  2. Se não valer a pena documentar seu programa, provavelmente não vale a pena executá-lo. [NAG95]
  3. Evite decoração; isto é, não utilize comentários do tipo banner. Nos anos 60 e 70, os programadores COBOL criaram o hábito de desenhar caixas, normalmente com asteriscos, em torno de seus comentários internos. Claro que isso lhes deu uma vazão para seus anseios artísticos, mas francamente era uma grande perda de tempo que adicionava pouco valor ao produto final. Você deseja gravar código limpo, não bonito. Além disso, como muitas das fontes utilizadas para exibir e imprimir seu código são proporcionais e muitas não são, de qualquer modo não é possível alinhar as caixas corretamente.
  4. Mantenha os comentários simples. Alguns dos melhores comentários são notas simples em formato de indicação. Você não precisa escrever um livro; precisa apenas fornecer informações suficientes para que outros possam entender o código.
  5. Escreva a documentação antes de escrever o código. A melhor forma de documentar o código é escrever os comentários antes de escrever o código. Isso lhe dá uma oportunidade de pensar sobre como o código funcionará antes de escrevê-lo e assegurará que a documentação será escrita. De forma alternativa, você deve pelo menos documentar seu código à medida que o escreve. Como a documentação torna o código mais fácil de entender, é possível aproveitar esse fato enquanto o está desenvolvendo. Você investirá o tempo escrevendo a documentação, portanto deverá pelo menos obter algo dela. [AMB98]
  6. Documente porque algo está sendo feito, não apenas o que. Por exemplo, o código no Exemplo 1 a seguir mostra que um desconto de 5% está sendo concedido em pedidos de de US$1.000 ou mais. Por que isso está sendo feito? Existe uma regra de negócios que indique que os pedidos grandes tenham um desconto? Existe alguma promoção por tempo limitado para pedidos grandes ou é um programa permanente? O programador original estava apenas sendo generoso? Você não sabe a menos que esteja documentado em algum lugar, seja no próprio código fonte ou em um documento externo.

Exemplo 1:

if ( grandTotal >= 1000.00)

{

grandTotal = grandTotal * 0.95;

}

2.2.1    Tipos de Comentários Java

Java tem três estilos de comentários: 

O gráfico a seguir é um resumo de um uso sugerido para cada tipo de comentário, bem como diversos exemplos.

Tipo de Comentário Uso Exemplo
Documentação Utilize comentários de documentação imediatamente antes de declarações de interfaces, classes, funções-membro e campos para documentá-los. Os comentários de documentação são processados pelo javadoc (consulte a seguir) para criar documentação externa para uma classe. /**
Customer: A customer is any person or organization that we sell services and products to.
@author S.W. Ambler
*/
Estilo C Utilize comentários em estilo C para documentar linhas de código que não mais são aplicáveis, mas que deseja manter para o caso dos usuários mudarem de idéia, ou porque deseja desativá-las temporariamente durante a depuração. /*
This code was commented out by B. Gustafsson on June 4, 1999 because it was replaced by the preceding code. Delete it after two years if it is still not applicable.
. . . (the source code )
*/
Linha única Utilize os comentários de linha única internamente nas funções-membro para documentar a lógica de negócios, seções de código e declarações de variáveis temporárias. // Apply a 5% discount to all
// Invoices Over $1000 Due To
// generosity campaign started in
// Feb. of 1995.

O fato importante é que sua organização deve definir um padrão de como os comentários em estilo C e comentários de única linha deverão ser utilizados e, posteriormente, seguir esse padrão de forma consistente. Utilize um tipo para documentar a lógica de negócios e o outro para documentar código antigo retirado. Utilize comentários de linha única para lógica de negócios porque pode colocar a documentação na mesma linha que o código (isso é chamado inlining). Utilize comentários do estilo C para documentar código antigo retirado porque isso permite comentar várias linhas de uma só vez. Como o estilo C é muito semelhante a comentários de documentação, para evitar confusão, não os utilize em nenhum outro lugar.

Cuidado com Comentários de Final de Linha-[MCO93] argumenta fortemente contra a utilização de comentários seqüenciais, também conhecidos como comentários de final de linha. McConnell aponta que os comentários devem ser alinhados à direita do código para que não interfiram com a estrutura visual do código. Como resultado, eles tendem a ser difíceis de formatar e "se você utilizar muitos deles, é demorado alinhá-los. Tal tempo não é gasto no aprendizado adicional sobre o código; ele é dedicado somente à tediosa tarefa de pressionar a barra de espaço ou a tecla Tab." Também aponta que os comentários de final de linha são difíceis de se manter porque quando o código na linha cresce, ele empurra o comentário de final de linha e, se você os estiver alinhando, deverá fazer o mesmo para o restante deles.

2.2.2        Uma Visão Geral Rápida do javadoc

No JDK (Java Development Kit) da Sun está incluído um programa denominado javadoc que processa arquivos de código Java e produz documentação externa, no formato de arquivos HTML, para seus programas Java. O Javadoc suporta um número limitado de tags; as palavras reservadas que marcam o início de uma seção de documentação. Consulte a documentação do JDK javadoc para obter detalhes adicionais.

Tag Utilizada para Objetivo
@author name Classes, Interfaces Indica o(s) autor(es) de um determinado trecho de código. Deve ser utilizada uma tag por autor.
@deprecated Classes, Funções-membro Indica que a API para a classe foi desaprovada e, portanto, não mais deve ser utilizada.
@exception name description Funções-membro Descreve as exceções emitidas por uma função-membro. Você deve utilizar uma tag por exceção e fornecer o nome completo da classe à exceção.
@param name description Funções-membro Utilizada para descrever um parâmetro transmitido para uma função-membro, incluindo seu tipo ou classe e seu uso. Utilize uma tag por parâmetro.
@return description Funções-membro Descreve o valor de retorno, se houver, de uma função-membro. Você deve indicar o tipo ou classe e o(s) uso(s) potencial(is) do valor de retorno.
@since Classes, Funções-membro Indica por quanto tempo o item existiu; isto é, desde o JDK 1.1.
@see ClassName Classes, Interfaces, Funções-membro, Campos Gera um link de hipertexto na documentação para a classe especificada. É possível, e provavelmente necessário, utilizar um nome de classe completo.
@see ClassName#member functionName Classes, Interfaces, Funções-membro, Campos Gera um link de hipertexto na documentação para a função-membro especificada. É possível, e provavelmente necessário, utilizar um nome de classe completo.
@version text Classes, Interfaces Indica as informações sobre a versão para um determinado trecho de código.

A forma que o código é documentado tem um grande impacto, tanto em sua própria produtividade quanto na produtividade de qualquer outra pessoa que o mantenha e aprimore posteriormente. Documentando seu código no início do processo de desenvolvimento, você fica mais produtivo porque isso o força a pensar diretamente na lógica antes de confirmá-la no código. Além do mais, quando revisitar o código escrito dias ou semanas antes, poderá facilmente determinar o que você estava pensando quando o escreveu porque isso já está documentado para você.


3    Padrões para Funções-membro

Nunca se esqueça de que o código escrito hoje, ainda poderá ser utilizado muitos anos à frente, e provavelmente será mantido e aprimorado por outra pessoa. Você deve se esforçar para manter o código tão "limpo" e compreensível quanto possível, porque esses fatores tornam mais fácil manter e aprimorá-lo.

3.1    Nomeando Funções-membro

As funções-membro devem ser nomeadas utilizando-se uma descrição completa em inglês, utilizando nomes compostos por letras maiúsculas e minúsculas com a primeira letra de cada palavra não-inicial em maiúsculas. Também é uma prática comum que a primeira palavra do nome de uma função-membro seja um verbo forte e ativo.

Exemplos:

openAccount()

printMailingLabel()

save()

delete()

Essa convenção resulta em funções-membro cujo objetivo pode normalmente ser determinado apenas examinando-se seus nomes. Embora essa convenção resulte em um pouco mais de digitação pelo desenvolvedor porque ela normalmente resulta em nomes longos, isso mais que compensa pela capacidade aumentada de compreensão do código.

3.1.1    Nomeando Funções-membro do Acessador

Discutiremos os acessadores, funções-membro que obtêm e definem os valores dos campos (campos ou propriedades), em maiores detalhes em um próximo capítulo. As convenções de nomenclatura para acessadores, entretanto, são resumidas a seguir.

3.1.1.1    Getters 

Getters são funções-membro que retornam o valor de um campo. Você deve prefixar a palavra "get" no nome do campo, a menos que ele seja um campo booleano, quando você deve prefixar "is" no nome do campo em vez de "get".

Exemplos:

getFirstName()

getAccountNumber()

isPersistent()

isAtEnd()

Seguindo essa convenção de nomenclatura, você torna óbvio que uma função-membro retorna um campo de um objeto, e para getters booleanos, você torna óbvio que ela retorna true ou false. Outra vantagem desse padrão é que ele segue as convenções de nomenclatura utilizadas pelo BDK (Beans Development Kit) para funções-membro getter. [DES97] A principal desvantagem é que "get" é supérfluo e requer digitação adicional.

Convenção de nomenclatura alternativa para Getters: has e can

Uma alternativa viável, com base em convenções corretas em inglês, é utilizar o prefixo "has" ou "can" em vez de "is" para getters Booleanos. Por exemplo, nomes de getter tais como hasDependents() e canPrint() fazem muito sentido ao ler o código. O problema com essa abordagem é que o BDK não encontrará essa estratégia de nomenclatura (ainda). É possível renomear essas funções-membro como isBurdenedWithDependents() e isPrintable().

3.1.1.2    Setters

Os setters, também conhecidos como modificadores, são funções-membro que modificam os valores de um campo. Você deve prefixar a palavra "set" no nome do campo, independentemente do tipo de campo.

Exemplos:

setFirstName(String aName)

setAccountNumber(int anAccountNumber)

setReasonableGoals(Vector newGoals)

setPersistent(boolean isPersistent)

setAtEnd(boolean isAtEnd)

Seguindo essa convenção de nomenclatura, você torna óbvio que uma função-membro define o valor de um campo de um objeto. Outra vantagem desse padrão é que ele segue as convenções de nomenclatura utilizadas pelo BDK para funções-membro setter. [DES97] A principal desvantagem é que "set" é supérfluo e requer digitação adicional.

3.2    Nomeando Construtores

Os construtores são funções-membro que executam quaisquer inicializações necessárias quando um objeto é criado pela primeira vez. Os construtores sempre recebem o mesmo nome que sua classe. Por exemplo, um construtor para a classe Customer seria Customer(). Note que é utilizado o mesmo padrão de maiúsculas e minúsculas.

Exemplos:

Customer()

SavingsAccount()

PersistenceBroker()

Essa convenção de nomenclatura é definida pela Sun Microsystems e deve ser seguida rigidamente.

3.3    Visibilidade da Função-membro

Para um bom design onde o acoplamento entre classes seja minimizado, o método baseado na experiência geral é ser tão restritivo quanto possível ao definir a visibilidade de uma função-membro. Se uma função-membro não precisar ser pública, torne-a protegida e, se não precisar ser protegida, torne-a privada.

Visibilidade Descrição Uso Correto
public Uma função-membro pública pode ser acessada por qualquer outra função-membro em qualquer outro objeto ou classe. Quando a função-membro deve ser acessível por outros objetos e classes fora da hierarquia de classe em que a função-membro está definida.
protected Uma função-membro protegida pode ser chamada por qualquer função-membro na classe em que ela está definida, quaisquer subclasses dessa classe ou quaisquer classes no mesmo pacote. Quando a função-membro fornece o comportamento necessário internamente na hierarquia de classe ou pacote, mas não externamente.
private Uma função-membro privada pode ser chamada apenas por outras funções-membro na classe em que ela está definida, mas não nas subclasses. Quando a função-membro fornece o comportamento específico para a classe. As funções-membro privadas normalmente são o resultado da recriação, também conhecida como reorganização, do comportamento das demais funções-membro dentro da classe para encapsular um comportamento específico.
default Por padrão (nenhuma visibilidade especificada, uma função pode ser chamada apenas por outras funções-membro na classe em que ela está definida ou em quaisquer classes no mesmo pacote. Quando a função-membro fornece o comportamento necessário pelas classes no mesmo pacote, mas não externamente, e não pelas subclasses.

3.4    Documentando Funções-membro

A maneira pela qual você documenta uma função-membro será freqüentemente o fator decisivo para o fato dela ser ou não compreensível e, portanto, se pode ser mantida e estendida.

3.4.1    O Cabeçalho da Função-membro

Toda função-membro Java deve incluir algum tipo de cabeçalho, denominado documentação da função-membro, no início da página do código fonte, que documente todas as informações críticas para compreendê-lo. Essas informações incluem, mas não estão limitadas a, o seguinte:

  1. O que e porque a função-membro faz o que faz. Documentando o que uma função-membro faz, você facilita que outros determinem se podem reutilizar seu código. A documentação do porque ela faz algo facilita que terceiros coloquem seu código no contexto. Também facilita que outros determinem se uma nova alteração deve ou não realmente ser feita em um trecho de código (talvez a razão para a nova alteração entre em conflito com a razão pela qual o código foi escrito a princípio).
  2. Qual função-membro deve ser transmitida como parâmetro. Também é necessário indicar quais parâmetros, se houver, devem ser transmitidos para uma função-membro e como serão utilizados. Essa informação é necessária para que outros programadores saibam quais informações devem ser transmitidas para uma função-membro. A tag @param do javadoc, discutida na seção 2.2.2, Uma Visão Geral Rápida do javadoc, é utilizada para isso.
  3. O que uma função-membro retorna. É necessário documentar o que, se for o caso, uma função-membro retorna, para que outros programadores possam utilizar o valor ou objeto de retorno de forma apropriada. A tag @return do javadoc, discutida na seção 2.2.2, Uma Visão Geral Rápida do javadoc, é utilizada para isso.
  4. Erros conhecidos. Quaisquer problemas importantes com uma função-membro devem ser documentados para que outros desenvolvedores entendam a fragilidade e as dificuldades com a função-membro. Se um determinado erro for aplicável a mais de uma função-membro dentro de uma classe, em vez disso ele deve ser documentado para a classe.
  5. Todas as exceções emitidas por uma função-membro. É necessário documentar todas e quaisquer exceções que uma função-membro emita, para que outros programadores saibam o que seu código precisa capturar. A tag @exception do javadoc, discutida na seção 2.2.2, Uma Visão Geral Rápida do javadoc, é utilizada para isso.
  6. Decisões de visibilidade. Se você acreditar que sua opção de visibilidade para uma função-membro será questionada por outros desenvolvedores - talvez você tenha tornado pública uma função-membro, embora ainda nenhum outro objeto chame a função-membro - deverá documentar sua decisão. Isso ajudará a tornar claro seu pensamento para outros desenvolvedores, para que eles não gastem tempo se preocupando por quê você fez algo questionável.
  7. Como uma função-membro altera o objeto. Se uma função-membro alterar um objeto, por exemplo, a função-membro withdraw() de uma conta bancária modificar o saldo da conta, isso precisa ser indicado. Essa informação é necessária para que outros programadores Java saibam exatamente como uma chamada de função-membro afetará o objeto de destino.
  8. Evite a utilização de cabeçalhos que contenham informações como autor, números de telefone, datas de criação e modificação e local da unidade (ou nome do arquivo), porque essas informações rapidamente se tornam obsoletas. Coloque avisos de direitos autorais de direito à propriedade no final da unidade. Por exemplo, os leitores não desejam precisar rolar por duas ou três páginas de texto que não seja útil para a compreensão do programa, nem desejam rolar por texto que não contenha nenhuma informação do programa, tais 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 uma ferramenta de gerenciamento de configuração para manter o histórico da unidade.
  9. Exemplos de como chamar a função-membro, se apropriado. Uma das formas mais fáceis de determinar como um trecho de código funciona é consultar um exemplo. Considere a inclusão de um exemplo ou dois sobre como chamar uma função-membro.
  10. Condições prévias e posteriores aplicáveis. Uma condição prévia é uma restrição sob a qual uma função-membro funcionará corretamente e uma condição posterior é uma propriedade ou asserção que será verdadeira depois que a execução de uma função-membro for concluída. [MEY88] De muitas formas as condições prévias e posteriores descrevem as premissas feitas ao gravar uma função-membro [AMB98], definindo exatamente os limites de como uma função-membro é utilizada.
  11. Todos os problemas de simultaneidade. A simultaneidade é um conceito novo e complexo para muitos desenvolvedores e, na realidade, no máximo, é um tópico antigo e complexo para programadores de simultaneidade experientes. O resultado final é que se você utilizar os recursos de programação simultânea do Java, deverá documentá-los completamente. O [LEA97] sugere que quando uma classe incluir funções-membro sincronizadas e não-sincronizadas, é necessário documentar o contexto de execução do qual uma função-membro depende, especialmente quando requerer acesso irrestrito, para que outros desenvolvedores possam utilizar suas funções-membro com segurança. Quando um definidor, uma função-membro que atualiza um campo, de uma classe que implementa a interface Runnable não está sincronizado, é necessário documentar a(s) razão(ões) para isso. Finalmente, se você substituir ou sobrecarregar uma função-membro e alterar sua sincronização, também deverá documentar o motivo.
  12. Você deve documentar algo apenas quando isso ajuda a clarificar seu código. Você não documentaria todos os fatores descritos anteriormente para toda e qualquer função-membro, porque nem todos os fatores são aplicáveis a todas as funções-membro. Você deve, entretanto, documentar vários deles para cada função-membro gravada.

3.4.2    Documentação Interna

Além da documentação da função-membro, também é necessário incluir comentários nas funções-membro para descrever seu trabalho. O objetivo é tornar sua função-membro fácil de entender, manter e aprimorar.

Há dois tipos de comentários que devem ser utilizados para documentar o conteúdo do seu código: comentários em estilo C ( /* e */ ) e comentários de linha única ( // ). Como discutido anteriormente, é necessário considerar seriamente a escolha de um estilo de comentários para documentar a lógica de negócios do código e um para comentar código desnecessário. Sugere-se a utilização de comentários de linha única para a lógica de negócios, porque é possível utilizar esse estilo de comentário para linhas de comentário inteiras e comentários seqüenciais no final de uma linha de código. Utilize comentários em estilo C para documentar linhas de código desnecessário porque isso facilita remover várias linhas com apenas um comentário. Além disso, como os comentários em estilo C são muito parecidos com comentários de documentação, seu uso pode ser confuso, o que diminui a capacidade de compreensão do código. Portanto, utilize-os com moderação.

Internamente, você sempre deve documentar o seguinte:

  1. Estruturas de controle. Descreva cada estrutura de controle, como instruções de comparação e loops. Não deve ser necessário ler todo o código em uma estrutura de controle para determinar o que ela faz; em vez disso, deveria apenas ter que olhar um comentário de uma ou duas linhas imediatamente antes dela.
  2. Por que, e também o que, o código faz. Sempre é possível examinar um trecho de código e entender o que ele faz, mas para um código que não seja óbvio, raramente é possível determinar porque ele foi feito daquela maneira. Por exemplo, é possível examinar uma linha de código e facilmente determinar que um desconto de 5% está sendo aplicado ao total de um pedido. Isso é fácil. O que não é fácil é descobrir POR QUE esse desconto está sendo aplicado. Obviamente há algum tipo de regra de negócios que diga que o desconto deve ser aplicado, portanto essa regra de negócios deve pelo menos ser referida no código para que outros desenvolvedores possam entender porque o código faz o que ele faz.
  3. Variáveis locais. Embora iremos discutir isso em maiores detalhes no Capítulo 5, cada variável local definida em uma função-membro deve ser declarada em sua própria linha de código e normalmente deve ter um comentário seqüencial descrevendo seu uso.
  4. Código difícil ou complexo. Se acreditar que não é possível reescrevê-lo ou não tiver tempo, então deverá documentar completamente qualquer código complexo em uma função-membro. Um método baseado na experiência geral é que se o código não for óbvio, será necessário documentá-lo.
  5. A ordem de processamento. Se existirem instruções no código que devem ser executadas em uma ordem predefinida, você deverá assegurar que esse fato seja documentado [AMB98]. Não há nada pior que fazer uma simples modificação em um trecho de código apenas para descobrir que ele não mais funciona e, em seguida, gastar horas procurando pelo problema apenas para descobrir que as coisas foram colocadas fora de ordem.
  6. Documente as chaves de fechamento. Com muita freqüência, você descobrirá que tem estruturas de controle dentro de estruturas de controle dentro de outras estruturas de controle. Embora você deva evitar escrever código dessa forma, às vezes pode achar melhor escrever assim. O problema é que fica confuso saber a qual chave de fechamento o caractere } pertence e a qual estrutura de controle. A boa notícia é que alguns editores de código suportam um recurso que, ao selecionar uma chave de abertura, a chave de fechamento correspondente será automaticamente realçada; a má notícia é que nem todos os editores suportam isso. Notou-se que marcar as chaves de fechamento com um comentário seqüencial, tais como //end if, //end for, //end switch, torna o código mais fácil de entender.

3.5    Técnicas para Gravar Código Limpo

Este seção cobre diversas técnicas que ajudam a separar os desenvolvedores profissionais dos codificadores triviais. Essas técnicas são:

3.5.1    Documente seu Código 

Lembre-se de que se não valer a pena documentar o código, não valerá a pena mantê-lo.[NAG95] Ao aplicar os padrões de documentação e diretrizes propostos neste documento de maneira apropriada, será possível aprimorar em muito a qualidade do código.

3.5.2    Utilize Parágrafo ou Recue seu Código 

Uma forma de aprimorar a legibilidade de uma função-membro é utilizar parágrafos nela ou, em outras palavras, recuar o código dentro do escopo de um bloco de código. Todo o código entre chaves, os caracteres { e }, formam um bloco. A idéia básica é que o código dentro de um bloco deve ser recuado uniformemente em uma unidade.

A convenção Java parece ser que a chave de abertura deve ser colocada na linha seguinte ao proprietário do bloco e que a chave de fechamento deve ser recuada um nível. O fato importante, apontado pelo [LAF97], é que a organização escolhe um estilo de recuo e o mantém. Utilize o mesmo estilo de recuo utilizado por seu ambiente de desenvolvimento Java para o código que ele gera.

3.5.3    Utilize Espaço em Branco em seu Código 

Algumas linhas em branco, chamadas de espaço em branco, incluídas no código Java podem ajudar a torná-lo muito mais legível dividindo-o em seções pequenas e fáceis de entender. O [VIS96] sugere a utilização de uma única linha em branco para separar grupos lógicos de código, tais como estruturas de controle e duas linhas em branco para separar definições de função-membro. Sem espaço em branco, é muito difícil ler e entender.

3.5.4    Siga a Regra dos 30 Segundos 

Outros programadores devem ser capazes de examinar sua função-membro e entender totalmente o que ela faz, porque o faz e como o faz em menos de 30 segundos. Se não for possível, seu código é muito difícil de ser mantido e deve ser aprimorado. Trinta segundos; isso é tudo. Um bom método baseado na experiência é que se uma função-membro tiver mais de uma tela, ela provavelmente é muito longa.

3.5.5    Grave Linhas de Comandos Simples e Curtas 

Seu código deve fazer uma coisa por linha. Nos tempos dos cartões perfurados, fazia sentido tentar obter o máximo de funcionalidade possível em uma única linha de código. Sempre que você tenta fazer mais de uma coisa em uma única linha de código, torna mais difícil de entendê-las. Por que fazer isso? Queremos tornar nosso código mais fácil de entender para que seja mais fácil manter e aprimorá-lo. Assim como uma função-membro deve fazer uma coisa e apenas uma, você deve fazer apenas uma coisa em uma única linha de código.

Além disso, você deve escrever código que permaneça visível na tela [VIS96]. Você não deve precisar rolar a janela de edição para a direita para ler a linha de código inteira, incluindo código que utilize comentários seqüenciais.

3.5.6    Especifique a Ordem das Operações 

Uma forma realmente fácil de aprimorar a capacidade de compreensão do código é utilizar parênteses para especificar a ordem exata das operações no código Java [NAG95] e [AMB98]. Se for necessário saber a ordem das operações para que uma linguagem entenda o código fonte, algo está seriamente errado. Isso é principalmente um problema para comparações lógicas em que AND e OR são feitos em várias outras comparações em conjunto. Note que se você utilizar linhas de comando únicas e curtas, como sugerido anteriormente, isso realmente não se tornaria um problema.


4    Padrões para Campos e Propriedades

O termo campo utilizado aqui refere-se a um campo em que o BDK chama uma propriedade [DES97]. Uma campo é um dado que descreve um objeto ou classe. Os campos podem ser do tipo base de dados, como string ou float, ou podem ser um objeto, como um cliente ou uma conta bancária.

4.1    Nomeando Campos

Você deve utilizar um descritor completo em inglês para nomear os campos, [GOS96] e [AMB98], dessa forma tornando óbvio o que o campo representa. Os campos que são coleções, como matrizes ou vetores, devem receber nomes no plural para indicar que eles representam vários valores.

Exemplos:

firstName

zipCode

unitPrice

discountRate

orderItems

4.1.1    Nomeando Componentes (widgets)

Para nomes de componentes (widgets de interface), você deve utilizar um descritor completo em inglês, acrescentando como sufixo o tipo do widget. Isso facilita a identificação do objetivo do componente, bem como seu tipo, o que facilita a localização de cada componente em uma lista. Muitos ambientes de programação visual fornecem listas de todos os componentes em um applet ou aplicativo e isso pode ser confuso quando tudo está nomeado como button1, button2 e assim por diante.

Exemplos:

okButton

customerList

fileMenu

newFileMenuItem

4.1.1.1    Alternativa para Nomenclatura de Componentes: Notação Húngara 

A "Notação Húngara" [MCO93] tem como base o princípio de que um campo deve ser nomeado utilizando a seguinte abordagem: xEeeeeeEeeeee em que x indica o tipo do componente e EeeeeEeeeee é o descritor completo em inglês.

Exemplos:

pbOk

lbCustomer

mFile

miNewFile

A principal vantagem é que isso é um padrão de mercado comum para código C++, portanto muitas pessoal já o seguem. A partir do nome da variável, os desenvolvedores podem julgar rapidamente seu tipo e como ela é utilizada. As principais desvantagens são que a notação de prefixo se torna

4.1.1.2   Alternativa para Nomenclatura de Componentes: Notação Húngara com Sufixo 

Basicamente, essa é uma combinação das outras duas alternativas e resulta em nomes como okPb, customerLb, fileM e newFileMi. A principal vantagem é que o nome do componente indica o tipo do widget e que widgets do mesmo tipo não são agrupados em uma lista alfabética. A principal desvantagem é que você ainda não está utilizando uma descrição completa em inglês, tornando o padrão mais difícil de lembrar, porque ele se desvia da norma.

4.1.1.3    Definir Padrões de Nome de Componente 

Seja qual for a convenção escolhida, você desejará criar uma lista de nomes de widget "oficiais". Por exemplo, ao nomear botões, você utiliza Button, PushButton, b ou pb? Crie uma lista e disponibilize-a para todos os desenvolvedores Java em sua organização

4.1.2    Nomeando Constantes

Em Java, os valores constantes que não se alteram são normalmente implementados como campos static final de classes. A convenção reconhecida é utilizar palavras completas em inglês, todas em maiúsculas, com sublinhados entre as palavras [GOS96].

Exemplos:

MINIMUM_BALANCE

MAX_VALUE

DEFAULT_START_DATE

A principal vantagem dessa convenção é que ela ajuda a distinguir constantes de variáveis. Veremos posteriormente neste documento que é possível aumentar em muito a flexibilidade e a capacidade de manutenção do código se não definir constantes; em vez disso, você deve definir funções-membro getter que retornam o valor de constantes.

4.1.3    Nomeando Coleções

Uma coleção, como uma matriz ou um vetor, deve receber um nome em plural que represente os tipos de objetos armazenados pela matriz. O nome deve ser um descritor completo em inglês, com a primeira letra de todas as palavras não iniciais em maiúsculas.

Exemplos:

customers

orderItems

aliases

A principal vantagem desta convenção é que ela ajuda a distinguir campos que representam vários valores (coleções) daqueles que representam valores únicos (não-coleções).

4.2    Visibilidade do Campo

Quando os campos forem declarados como protegidos, existe a possibilidade de que as funções-membro em subclasses os acessem diretamente, aumentando eficazmente o acoplamento dentro de uma hierarquia de classe. Isso torna as classes mais difíceis de se manter e aprimorar e, portanto, deve ser evitado. Os campos nunca devem ser acessados diretamente; em vez disso, deve-se utilizar funções-membro do acessador (consulte a seguir).

Visibilidade Descrição Uso Correto
public Um campo público pode ser acessado por qualquer outra função-membro em qualquer outro objeto ou classe. Não torne os campos públicos.
protected Um campo protegido pode ser acessado por qualquer função-membro na classe em que ele está declarado ou por quaisquer funções-membro definidas nas subclasses dessa classe. Não torne os campos protegidos.
private Um campo privado pode ser acessado apenas por funções-membro na classe em que ele está declarado, mas não nas subclasses. Todos os campos devem ser privados e devem ser acessados por funções-membro getter e setter (acessadores).

Para campos que não são persistentes (eles não serão salvos no armazenamento permanente), você deve marcá-los como estáticos ou transientes [DES97]. Isso faz com que eles estejam em conformidade com as convenções do BDK.

4.2.1    Não "Oculte" Nomes 

A ocultação de nomes se refere à prática de nomear uma variável, argumento ou campo local da mesma forma (ou similar) que outro de maior escopo. Por exemplo, se houver um campo denominado firstName não crie uma variável local ou um parâmetro denominado firstName ou algo semelhante como firstNames ou firstName. Isso torna o código difícil de entender e propenso a erros, porque outros desenvolvedores, ou mesmo você, irão enganar-se na leitura do código enquanto o estiverem modificando, o que tornará difícil detectar erros.

4.3    Documentando um Campo

Todo campo deve ser documentado de forma suficiente para que outros desenvolvedores possam entendê-lo. Para que seja eficaz, é necessário documentar:

  1. Sua descrição. Você deve descrevem um campo para que as pessoas saibam como utilizá-lo.
  2. Todas as invariantes aplicáveis. As invariantes de um campo são as condições sempre verdadeiras sobre ele. Por exemplo, uma invariante sobre o campo dayOfMonth pode ser que seu valor esteja entre 1 e 31 (obviamente, você poderia deixar essa invariante muito mais complexa, restringindo o valor do campo com base no mês e no ano). Documentando as restrições sobre o valor de um campo, você ajuda a definir regras de negócios importantes que facilitam a compreensão de como o código funciona.
  3. Exemplos. Para campos que têm regras de negócios complexas associadas a eles, é necessário fornecer diversos valores de exemplo para torná-los mais fáceis de entender. Um exemplo normalmente é como uma boa foto: vale mais que mil palavras.
  4. Problemas de simultaneidade. A simultaneidade é um conceito novo e complexo para muitos desenvolvedores; na realidade, no máximo, é um tópico antigo e complexo para programadores de simultaneidade experientes. O resultado final é que se você utilizar os recursos de programação simultânea do Java, deverá documentá-los completamente.
  5. Decisões de visibilidade. Se você declarou um campo para algo diferente de privado, deverá documentar porque fez isso. A visibilidade do campo foi discutida anteriormente na seção 4.2 Visibilidade do campo e a utilização de funções-membro do acessador para suportar encapsulamento é abrangida a seguir na seção 4.4 Utilização de funções-membro do acessador. O resultado é que é melhor ter uma razão realmente boa para não declarar uma variável como privada.

4.4    Utilizando Funções-membro do Acessador

Além das convenções de nomenclatura, a capacidade de manutenção dos campos é alcançada pela utilização apropriada de funções-membro do acessador - funções-membro que fornecem a funcionalidade para atualizar um campo ou acessar seu valor. As funções-membro do acessador são de dois tipos: setters (também denominados alteradores) e getters. Um setter modifica o valor de uma variável, enquanto um getter o obtém para você.

Embora as funções-membro do acessador sejam utilizadas para gerar código extra em seu código, os compiladores Java agora são otimizados para seu uso, portanto isso não mais é verdadeiro. Os acessadores ajudam a ocultar os detalhes da implementação da classe. Tendo no máximo dois pontos de controle a partir dos quais uma variável é acessada, um definidor e um getter, é possível aumentar a capacidade de manutenção das classes pela minimização dos pontos nos quais alterações precisam ser feitas. A otimização do código Java é discutida na seção 9.3, Otimizando Código Java.

Um dos padrões mais importantes que sua organização pode reforçar é a utilização de acessadores. Alguns desenvolvedores não desejam utilizar funções-membro do acessador porque não desejam digitar os poucos pressionamentos de tecla adicionais requeridos; por exemplo, para um getter, é necessário digitar "get" e "()" above, além do nome do campo. O resultado é que as capacidades de manutenção e extensão aumentadas oriundas da utilização de acessadores mais que justificam seu uso.

Os acessadores são o único local para acessar campos. Um conceito-chave com a utilização apropriada de funções-membro do acessador é que APENAS as funções-membro com permissão para trabalhar diretamente com um campo são as próprias funções-membro do acessador. Sim, é possível acessar diretamente um campo privado dentro das funções-membro da classe na qual o campo está definido, mas você não deseja fazer isso porque aumentaria o acoplamento dentro da classe.

4.4.1    Por que Utilizar Acessadores? 

"Um bom design de programa procura isolar partes de um programa de influências externas desnecessárias, não pretendidas ou de outra forma não desejadas. Os modificadores de acesso (acessadores) fornecem um meio explícito e verificável para que a linguagem controle tais contatos." [KAN97]

As funções-membro do acessador aprimoram a capacidade de manutenção das classes das seguintes maneiras:

  1. Atualizando campos. Você tem pontos únicos de atualização para cada campo, facilitando a modificação e o teste. Em outras palavras, os campos são encapsulados.
  2. Obtendo os valores de campos. Você tem controle completo sobre como os campos são acessados e por quem.
  3. Obtendo os valores de constantes e os nomes das classes. Encapsulando o valor de constantes e nomes de classe em funções-membro getter quando esses valores/nomes forem alterados, é necessário apenas atualizar o valor no getter e não em cada linha de código em que a constante/nome é utilizada.
  4. Inicializando campos. A utilização de inicialização adiada assegura que os campos sempre sejam inicializados e que o sejam apenas se forem necessários.
  5. Reduzindo o acoplamento entre uma subclasse e sua(s) superclasse(s). Quando as subclasses acessarem campos herdados apenas utilizando suas funções-membro de acessador correspondentes, isso possibilita alterar a implementação dos campos na superclasse sem afetar nenhuma de suas subclasses, efetivamente reduzindo o acoplamento entre elas. Os acessadores reduzem o risco da "classe-base frágil" em que as alterações em uma superclasse se estendem pelas suas subclasses.
  6. Encapsulando alterações em campos. Se as regras de negócios pertencentes a um ou mais campos forem alteradas, você pode potencialmente modificar os acessadores para fornecer a mesma habilidade anterior à alteração, facilitando a resposta para as novas regras de negócios.
  7. Simplificando problemas de simultaneidade. O [LEA97] aponta que funções-membro setter fornecem um único local para incluir notifyAll se tiver waits com base no valor desse campo. Isso torna a transição para uma solução simultânea muito mais fácil.
  8. A ocultação de nomes se torna menos que um problema. Embora você deva evitar a ocultação de nomes, dar às variáveis locais o mesmo nome que os campos, a utilização de acessadores para sempre acessar campos significa que é possível dar às variáveis locais qualquer nome desejado. Não é necessário se preocupar sobre a ocultação de nomes de campos porque de qualquer maneira nunca as acessará diretamente.

4.4.1.1    Quando Não Utilizar Acessadores 

O único momento em que você pode desejar não utilizar acessadores é quando o tempo de execução for de máxima importância. No entanto, é um caso muito raro em que o aumento de acoplamentos em seu aplicativo justifica essa ação.

4.4.2    Nomeando Acessadores

As funções-membro getter devem receber o nome "get" + nome do campo, a menos que o campo represente um Booleano (verdadeiro ou falso), caso no qual o getter receberá o nome "is" + nome do campo. As funções-membro setter devem receber o nome "set" + nome do campo, independentemente do tipo do campo ([GOS96] e [DES97]). Note que o nome do campo é sempre composto por letras maiúsculas e minúsculas com a primeira letra de todas as palavras em maiúsculas. Essa convenção de nomenclatura é utilizada de forma consistente no JDK e é requerida para o desenvolvimento de beans.

Exemplos:

Campo Tipo Nome do Getter Nome do Setter
firstName
string
getFirstName()
setFirstName()
address
Address 
object
getAddress()
setAddress()
persistent
boolean
isPersistent()
setPersistent()
customerNo
int
getCustomerNo()
setCustomerNo()
orderItems
 
Array of 
OrderItem 
objects
getOrderItems()
 
setOrderItems()
 

 

4.4.3    Técnicas Avançadas para Acessadores

Os acessadores podem ser utilizados para mais que apenas obter e definir os valores de campos da instância. Esta seção discute como aumentar a flexibilidade do seu código pela utilização de acessadores para:

4.4.3.1    Inicialização Adiada 

As variáveis precisam ser inicializadas antes de serem acessadas. Existem duas escolas de pensamento para a inicialização: Inicializar todas as variáveis no momento em que o objeto é criado (a abordagem tradicional) ou inicializar no momento de sua primeira utilização. 

A primeira abordagem utiliza funções-membro especiais que são chamadas quando o objeto é criado pela primeira vez, chamadas construtores. Embora isso funcione, normalmente prova ser propenso a erros. Ao incluir uma nova variável, você pode facilmente esquecer de atualizar os construtores.

Uma abordagem alternativa é denominada inicialização adiada, em que os campos são inicializados por suas funções-membro getter, como mostrado a seguir. Note como uma função-membro setter é utilizada dentro da função-membro getter. Note que a função-membro verifica se o número da filial é zero; ser for, ela a define para o valor padrão apropriado.

/** Answers the branch number, which is the leftmost

four digits of the full account number.

Account numbers are in the format BBBBAAAAAA.

*/

protected int getBranchNumber()

{

if ( branchNumber == 0)

{

// The default branch number is 1000, which

// is the main branch in downtown Bedrock.

setBranchNumber(1000);

}

return branchNumber;

}

É bastante comum utilizar a inicialização adiada para campos que na realidade são outros objetos armazenados no banco de dados. Por exemplo, ao criar um novo item do inventário, não é necessário buscar qualquer tipo de item do inventário no banco de dados definido como o padrão. Em vez disso, utilize a inicialização adiada para definir esse valor pela primeira vez que ele for acessado, para que seja necessário ler o objeto do tipo de item do inventário do banco de dados quando e se ele for necessário. 

Essa abordagem é vantajosa para objetos cujos campos não são acessador regularmente. Por que ficar sujeito ao código extra de recuperar algo do armazenamento persistente se isso não será utilizado?

Sempre que a inicialização adiada for utilizada em uma função-membro getter, você deve documentar porque o valor padrão é o que é, como vimos no exemplo anterior. Quando fizer isso, removerá o mistério de como os campos são utilizados no código, o que aprimora sua capacidade de manutenção e de extensão.

4.4.3.2    Acessadores para Constantes

A idiomática Java comum é implementar valores constantes como campos finais estáticos. Essa abordagem faz sentido para "constantes" com garantia de estabilidade. Por exemplo, a classe Boolean implementa dois campos static final denominados TRUE e FALSE, que representam as duas instâncias dessa classe. Também faria sentido para uma constante DAYS_IN_A_WEEK cujo valor provavelmente nunca será alterado.

No entanto, muitas "constantes" de negócios assim denominadas são alteradas com o passar do tempo porque as regras de negócios se alteram. Considere o seguinte exemplo: O Archon Bank of Cardassia (ABC) sempre insistiu que uma conta tivesse um saldo mínimo de US$500 se ela devesse merecer juros. Para implementar isso, poderíamos incluir um campo estático denominado MINIMUM_BALANCE na classe Account que seria utilizado nas funções-membro que calculam juros. Embora isso possa funcionar, não é flexível. O que acontece se as regras de negócios forem alteradas e diferentes tipos de contas tiverem saldos mínimos diferentes, talvez US$500 para constas de poupança mas apenas US$200 para contas correntes? O que aconteceria se as regras de negócios fossem alteradas para um saldo mínimo de US$500 no primeiro ano, US$400 no segundo, US$300 no terceiro e assim por diante? Talvez a regra será alterada para US$500 no verão, mas para apenas US$250 no inverno? Talvez uma combinação de todas essas regras precise ser implementada no futuro.

O ponto levantado aqui é que a implementação de constantes como campos não é flexível. Uma solução muito melhor é implementar constantes como funções-membro getter. Em nosso exemplo acima, uma função-membro estática (classe) denominada getMinimumBalance() é muito mais flexível que um campo estático denominado MINIMUM_BALANCE porque podemos implementar as diversas regras de negócios nessa função-membro e colocá-la em subclasse apropriadamente para os diversos tipos de contas.

/** Get the value of the account number. Account numbers are in the following 
    format: BBBBAAAAAA, where BBBB is the branch number and 
    AAAAAA is the branch account number.
*/
public long getAccountNumber()
{
	return ( ( getBranchNumber() * 100000 ) + getBranchAccountNumber() );
}
/**
	Set the account number. Account numbers are in the following 
	format: BBBBAAAAAA where BBBB is the branch number and
	AAAAAA is the branch account number.
*/
public void setAccountNumber(int newNumber)
{
	setBranchAccountNumber( newNumber % 1000000 );
	setBranchNumber( newNumber / 1000000 );
}

Outra vantagem dos getters de constantes é que eles ajudam a aumentar a consistência do código. Considere o código mostrado acima: ele não funciona corretamente. Um número de conta é a concatenação do número da filial e o número da conta da filial. Testando nosso código, descobrimos que a função-membro setter, setAccountNumber() não atualiza corretamente os números de conta de filial; ela obtém os três dígitos mais à esquerda, não quatro. Isso ocorre porque utilizamos 1.000.000 em vez de 100.000 para extrair o campo branchAccountNumber. Se tivéssemos utilizado uma única origem para esse valor, o getter de constante getAccountNumberDivisor() como vemos a seguir, nosso código seria mais consistente e teria funcionado.

/**
	Returns the divisor needed to separate the branch account number from the 
	branch number within the full account number. 
	Full account numbers are in the format BBBBAAAAAA.
*/
public int getAccountNumberDivisor()
{
	return ( (long) 1000000);
}
/**
	Get the value of the account number. Account numbers are in the following 
	format: BBBBAAAAAA, where BBBB is the branch number and 
	AAAAAA is the branch account number.
*/
public long getAccountNumber()
{
	return ( ( getBranchNumber() * getAccountNumberDivisor() ) + getBranchAccountNumber() );
}
/**
	Set the account number. Account numbers are in the following 
	format: BBBBAAAAAA where BBBB is the branch number and
	AAAAAA is the branch account number.
*/
public void setAccountNumber(int newNumber)
{
	setBranchAccountNumber( newNumber % getAccountNumberDivisor() );

	setBranchNumber( newNumber / getAccountNumberDivisor() );
}

Utilizando acessadores para constantes, diminuímos a chance de erros e, ao mesmo tempo, aumentamos a capacidade de manutenção do sistema. Quando o layout de um número de conta for alterado, e sabemos que isso eventualmente ocorrerá, muito provavelmente o nosso código será mais fácil de alterar porque ocultamos e centralizamos as informações necessárias para construir ou dividir números de conta.

4.4.3.3    Acessadores para Coleções

O objetivo principal dos acessadores é encapsular o acesso aos campos para reduzir o acoplamento no código. As coleções, tais como matrizes e vetores, como são mais complexas que campos de valor único, naturalmente precisam ter mais do que apenas as funções-membro getter e setter padrão implementadas para elas. Especificamente, como é possível incluir e remover de coleções, as funções-membro de acessador precisam ser incluídas para fazê-lo. Inclua as seguintes funções-membro do acessador onde apropriado para um campo que seja uma coleção:

Tipo da Função-membro Convenção de nomenclatura Exemplo
Getter para a coleção
getCollection()
getOrderItems()
Setter para a coleção
setCollection() 
setOrderItems()
Inserir um objeto na coleção
insertObject()
insertOrderItem()
Excluir um objeto da coleção
deleteObject()
deleteOrderItem()
Criar e incluir um novo objeto na coleção
newObject()
newOrderItem()

A vantagem dessa abordagem é que a coleção é totalmente encapsulada, permitindo substituí-la posteriormente por outra estrutura, talvez uma lista vinculada ou uma árvore-B.

4.4.3.4   Acessando Vários Campos Simultaneamente

Uma das forças das funções-membro do acessador é que elas permitem reforçar as regras de negócios de forma eficaz. Considere, por exemplo, uma hierarquia de classe de shapes. Cada subclasse de Shape sabe sua posição pela utilização de dois campos, "xPosition e yPosition" e pode ser movida na tela em um plano bidimensional chamando a função-membro move(Float xMovement, Float yMovement). Para nossos objetivos, não faz sentido mover um shape de cada vez por um eixo; em vez disso, moveremos os eixos x e y simultaneamente (é aceitável transmitir o valor 0.0 para qualquer um dos parâmetros da função-membro move()). A implicação é que a função-membro move() deve ser pública, mas as funções-membro setXPosition() e setYPosition() devem ser privadas, sendo chamadas pela função-membro move() de forma apropriada.

Uma implementação alternativa seria introduzir uma função-membro setter que atualiza os dois campos de uma vez, conforme mostrado a seguir. A funções-membro setXPosition() e setYPosition() ainda seriam privadas, de forma que elas não podem ser chamadas diretamente por classes ou subclasses externas (você poderia desejar incluir alguma documentação, mostrada a seguir, indicando que elas não devem ser chamadas diretamente).

/** Set the position of the shape */
protected void setPosition(Float x, Float y)
{
	setXPosition(x);
	setYPosition(y);
}
/** Set the x position.  Important: Invoke setPosition(), not this member function. */
private void setXPosition(Float x)
{
	xPosition = x;
}
/** Set the y position of the shape
    Important: Invoke setPosition(), not this member function.
*/
private void setYPosition(Float y)
{
	yPosition = y;
}

4.5    Visibilidade dos Acessadores

Sempre se esforce em tornar os acessadores protegidos, para que apenas as subclasses possam acessar os campos. Apenas quando uma "classe externa" precisar acessar um campo, você deverá tornar público o getter ou setter correspondente. Note que é comum que a função-membro getter seja pública e a setter protegida.

Às vezes é necessário tornar setters privados para garantir que certas invariantes não se alterem. Por exemplo, uma classe Order pode ter um campo que represente uma coleção de instâncias de OrderItem e um segundo campo denominado orderTotal, que é o total do pedido inteiro. orderTotal é um campo de conveniência que é a soma ou todos os subtotais dos itens ordenados. As únicas funções-membro que devem atualizar o valor de orderTotal são aquelas que manipulam a coleção de itens de ordem. Assumindo que essas funções-membro são todas implementadas em Order, você deveria tornar setOrderTotal() privada, muito embora getOrderTotal() seja mais do que provavelmente pública.

4.6    Sempre Inicialize Campos Estáticos

Os campos estáticos, também conhecidos como campos de classe, devem receber valores válidos, porque não é possível assumir que instâncias de uma classe serão criadas antes que um campo estático seja acessado.

 


5    Padrões para Variáveis Locais

Uma variável local é um objeto ou item de dados que está definido no escopo de um bloco, normalmente uma função-membro. O escopo de uma variável local é o bloco no qual ela está definida. Os padrões de codificação importantes para variáveis locais focam em:

5.1    Nomeando Variáveis Locais

Em geral, as variáveis locais são nomeadas seguindo as mesmas convenções utilizadas para campos; em outras palavras, utilize descritores completos em inglês com a primeira letra de qualquer palavra não-inicial em maiúsculas.

Pelo bem da conveniência, entretanto, essa convenção de nomenclatura é relaxada para vários tipos específicos de variáveis locais:

5.1.1    Nomeando Fluxos

Quando houver um único fluxo de entrada e/ou saída sendo aberto, utilizado e, em seguida, fechado em uma função-membro, a convenção comum é utilizar In e Out para os nomes desses fluxos, respectivamente [GOS96]. Para um fluxo utilizado para entrada e saída, a implicação é utilizar o nome inOut.

Uma alternativa comum a essa convenção de nomenclatura, embora esteja em conflito com as recomendações da Sun, é utilizar os nomes inputStream, outputStream e ioStream no lugar de In, Out e inOut, respectivamente.

5.1.2    Nomeando Contadores de Loop

Como os contadores de loop são uma utilização muito comum para variáveis locais, e porque isso era aceitável em C/C++, na programação Java, a utilização de i, j ou k é aceitável para contadores de loop [GOS96]. Se você utilizar esses nomes para contadores de loop, utilize-os de forma consistente.

Uma alternativa comum é utilizar nomes como loopCounter ou simplesmente counter, mas o problema com esta abordagem é que você freqüentemente localizará nomes como counter1 e counter2 em funções-membro que requerem mais de um contador. O resultado é que i, j e k funcionam como contadores; eles são rápidos de se digitar e são normalmente aceitos.

5.1.3    Nomeando Objetos de Exceção

Como a manipulação de exceção também é muito comum na codificação Java, a utilização da letra e para uma exceção genérica é considerada aceitável [GOS96].

5.2    Declarando e Documentando Variáveis Locais

Há várias convenções relativas à declaração e à documentação de variáveis locais em Java. Essas convenções são:

  1. Declare uma variável local por linha de código. Isso é consistente com uma instrução por linha de código e possibilita documentar cada variável com um comentário seqüencial.
  2. Documente variáveis locais com um comentário seqüencial. O comentário seqüencial é um estilo em que um comentário de linha única, denotado por //, segue imediatamente um comando na mesma linha de código (isso é denominado um comentário de final de linha). Você deve documentar para que uma variável local é utilizada, onde seu uso é apropriado e porque ela é utilizada. Isso torna seu código mais fácil de entender.
  3. Utilize variáveis locais apenas para uma coisa. Sempre que utilizar uma variável local para mais de uma razão, você diminui efetivamente sua coesão e a torna difícil de entender. Você também aumenta as chances de introduzir erros no código a partir dos efeitos colaterais inesperados dos valores anteriores de uma variável local anterior no código. Sim, reutilizar variáveis locais é mais eficiente porque menos memória precisa ser alocada, mas a reutilização de variáveis locais diminui a capacidade de manutenção do código e o torna mais frágil. Isso normalmente não compensa as pequenas economias obtidas de não precisar alocar mais memória.

5.2.1    Comentários Gerais sobre Declaração

As variáveis locais declaradas entre linhas de código, por exemplo, no escopo de uma instrução if, podem não ser facilmente localizadas por pessoas que não estejam familiarizadas com o código.

Uma alternativa à declaração de variáveis locais imediatamente antes de sua primeira utilização é declará-las no início da página do código. Como de qualquer forma as funções-membro devem ser curtas (consulte a seção 3.5.5 Grave linhas de comando simples e curtas), não deve ser tão ruim ir para o início da página do código para determinar a que se refere a variável local.


6    Padrões para Parâmetros para Funções-membro

Os padrões importantes para parâmetros e argumentos de funções-membro estão focados em como eles são nomeados e como são documentados. O termo parâmetro é utilizado para referir ao argumento de uma função-membro.

6.1    Nomeando Parâmetros

Os parâmetros devem ser nomeados seguindo as mesmas convenções utilizadas para variáveis locais. Da mesma forma que as variáveis locais, a ocultação do nome é um problema.

Exemplos:

customer

inventoryItem

photonTorpedo

e

Uma alternativa viável, obtida do Smalltalk, é utilizar as convenções de nomenclatura para variáveis locais, com a inclusão de "a" ou "an" na frente do nome. Incluir "a" ou "an" ajuda a tornar o parâmetro destacado de campos e variáveis locais, evitando o problema de ocultação de nome. Essa é a abordagem preferida.

Exemplos:

aCustomer

anInventoryItem

aPhotonTorpedo

anInputStream

anException

6.2    Documentando Parâmetros

Os parâmetros de uma função-membro são documentados na documentação de cabeçalho da função-membro utilizando a tag @param do javadoc. Você deve descrever o seguinte:

  1. Para que ele deve ser utilizado. É necessário documentar para que um parâmetro é utilizado, a fim de que outros desenvolvedores entendam o contexto completo de como o parâmetro será utilizado.
  2. Quaisquer restrições ou condições prévias. Se o intervalo completo de valores de um parâmetro não for aceitável para uma função-membro, o responsável pela chamada dessa função-membro precisa saber. Talvez uma função-membro aceite apenas números positivos ou cadeias com menos de cinco caracteres.
  3. Exemplos. Se não for totalmente óbvio o que deve ser um parâmetro, será necessário fornecer um ou mais exemplos na documentação.

6.2.1    Utilize Interfaces para Tipos de Parâmetros

Em vez de especificar uma classe, como um Object, para o tipo de um parâmetro, se apropriado especifique uma interface, tal como Runnable. A vantagem é que essa abordagem, dependendo da situação, pode ser mais específica (Runnable é mais específico que Object) ou potencialmente pode ser uma melhor forma de suportar polimorfismo. Em vez de insistir que um parâmetro seja uma instância de uma classe em uma hierarquia de classes específica, especifique que ela suporta uma interface específica implicando que ela precisa apenas estar em conformidade de modo polimórfico ao que você precisa.


7    Padrões para Classes, Interfaces, Pacotes e Unidades de Compilação

Este capítulo se concentra em padrões e diretrizes para classes, interfaces, pacotes e unidades de compilação. Uma classe é um gabarito a partir do qual os objetos são instanciados (criados). As classes contém a declaração dos campos e funções-membro. As interfaces são a definição de uma assinatura comum, incluindo funções-membro e campos, que uma classe que implementa uma interface deve suportar. Um pacote é uma coleção de classes relacionadas. Finalmente, uma unidade de compilação é um arquivo de código fonte no qual as classes e interfaces são declaradas. Como o Java permite que as unidades de compilação sejam armazenadas em um banco de dados, uma unidade de compilação individual pode não estar diretamente relacionada a um arquivo de código fonte físico.

7.1    Padrões para Classes

Os padrões importantes para classes têm como base:

7.1.1    Nomeando Classes

A convenção padrão do Java utiliza um descritor completo em inglês, iniciando com a primeira letra em maiúscula e utilizando um nome composto por letras maiúsculas e minúsculas para o resto. ([GOS96] e [AMB98])

Os nomes de classes devem estar na forma singular.

Exemplos:

Customer

Employee

Order

OrderItem

FileStream

Cadeia

7.1.2    Documentando uma Classe

As seguintes informações devem aparecer nos comentários da documentação imediatamente antes da definição de uma classe:

  1. O objetivo da classe. Os desenvolvedores precisam saber o objetivo geral de uma classe para que possam determinar se ela atende ou não suas necessidades. Torne um hábito documentar todas as coisas boas a saber sobre uma classe; por exemplo, ela faz parte de um padrão ou existe alguma limitação interessante em seu uso [AMB98]?
  2. Erros conhecidos. Se existirem problemas importantes com uma classe, eles devem ser documentados para que outros desenvolvedores entendam a fragilidade e as dificuldades com a classe. Além disso, a razão da não correção do erro também precisa ser documentada. Note que se um erro for específico para uma única função-membro, em vez disso ele deverá ser diretamente associado à função-membro.
  3. O histórico de desenvolvimento ou manutenção da classe. É uma prática comum incluir uma tabela de históricos que lista datas, autores e resumos das alterações feitas em uma classe. Isso fornece aos programadores de manutenção a percepção para quaisquer modificações feitas em uma classe no passado e documenta quem fez o que em uma classe.
  4. Documentar invariantes aplicáveis. Uma invariante é um conjunto de asserções sobre uma instância ou classe que devem ser verdadeiras em todos os momentos "estáveis", em que um momento estável é definido como o período antes de uma função-membro ser chamada no objeto ou classe e imediatamente depois que uma função-membro é chamada [MEY88]. Documentando as invariantes de uma classe, você fornece percepção valiosa para outros desenvolvedores sobre como uma classe pode ser utilizada.
  5. A estratégia de simultaneidade. Qualquer classe que implemente a interface Runnable deve ter sua estratégia de simultaneidade totalmente descrita. A programação simultânea é um tópico complexo que é novo para muitos programadores, portanto é necessário investir o tempo extra para assegurar que as pessoas possam entender seu funcionamento. É importante documentar sua estratégia de simultaneidade e porque escolheu essa estratégia em detrimento de outras. As estratégias comuns de simultaneidade [LEA97] incluem o seguinte:
  • objetos sincronizados
  • objetos de campo
  • objetos cautelosos
  • objetos de versão
  • controladores de política de simultaneidade
  • aceitantes

7.1.3    Declarações de Classes

Uma forma de facilitar o entendimento das classes é declará-las de forma consistente. A abordagem comum em Java é declarar uma classe na seguinte ordem:

  • funções-membro públicas
  • campos públicos
  • funções-membro protegidas
  • campos protegidos
  • funções-membro privadas
  • campos privados

[LAF97] aponta que os construtores e finalize() devem ser listados em primeiro lugar, provavelmente porque essas são as primeiras funções-membro que outro desenvolvedor irá procurar para entender como utilizar a classe. Além disso, como temos um padrão para declarar todos os campos como privados, a ordem da declaração verdadeiramente é reduzida para:

construtores

finalize()

funções-membro públicas

funções-membro protegidas

funções-membro privadas

campos privados

Em cada agrupamento de funções-membro, é comum listá-as em ordem alfabética. Muitos desenvolvedores optam por listar primeiro as funções-membro estáticas dentro de cada agrupamento, seguido pelas funções-membro de instância; em seguida, dentro de cada um desses dois sub-agrupamentos, listam as funções-membro alfabeticamente. Essas duas abordagens são válidas; você precisa apenas escolher uma e mantê-la.

7.1.4    Minimize a Interface Pública e Protegida

Um dos princípios do design orientado a objetos é minimizar a interface pública de uma classe. Há várias razões para isso:

  1. Facilidade de aprendizado. Para aprender como utilizar uma classe, você deve entender apenas sua interface pública. Quanto menor for a interface pública, mais fácil será aprender uma classe.
  2. Acoplamento reduzido. Sempre que a instância de uma classe enviar uma mensagem para uma instância de outra classe, ou diretamente para a própria classe, as duas classes se tornam acopladas. Minimizar a interface pública implica na minimização das oportunidades de acoplamento.
  3. Maior flexibilidade. Isso está diretamente relacionado ao acoplamento. Sempre que desejar alterar a forma que uma função-membro em sua interface pública é implementada - talvez você queira modificar o que a função-membro retorna - provavelmente terá que modificar todos os códigos que chamam a função-membro. Quanto menor for a interface pública, maior o encapsulamento e, portanto, maior a flexibilidade.

É claro que vale a pena minimizar a interface pública, mas freqüentemente o que não é tão claro é que você também deseja minimizar a interface protegida. A idéia básica é que, do ponto de vista de uma subclasse, as interfaces protegidas de todas as suas superclasses são efetivamente públicas. Qualquer função-membro na interface protegida pode ser chamada por uma subclasse. Portanto, você deseja minimizar a interface protegida de uma classe pelas mesmas razões que deseja minimizar a interface pública.

7.1.4.1    Defina Primeiro a Interface Pública

Os desenvolvedores mais experientes definem a interface pública de uma classe antes de começar a codificá-la. 

7.2    Padrões para Interfaces

Os padrões importantes para interfaces têm como base:

7.2.1    Nomeando Interfaces

A convenção Java é nomear interfaces utilizando composição de letras maiúsculas e minúsculas, com a primeira letra de cada palavra em maiúsculas. A convenção Java preferida para o nome de uma interface é utilizar um adjetivo descritivo, tais como Runnable ou Cloneable, embora substantivos descritos, tais como Singleton ou DataInput, também sejam comuns [GOS96].

7.2.1.1    Alternativa

Coloque a letra "I" como prefixo do nome da interface. Como [COA97] sugere, anexar a letra "I" na frente do nome de uma interface resulta em nomes como ISingleton ou IRunnable. Essa abordagem ajuda a distinguir nomes de interfaces de nomes de classes e pacotes. Essa convenção de nomenclatura potencial é boa pelo simples fato de que torna seus diagramas de classe, às vezes referidos como modelos de objeto, fáceis de se ler. A principal desvantagem é que as interfaces existentes, tal como Runnable, não são nomeadas utilizando essa abordagem. A convenção de nomenclatura de interfaces também é popular para a arquitetura COM/DCOM da Microsoft.

7.2.2    Documentando Interfaces

As seguintes informações devem aparecer nos comentários da documentação imediatamente antes da definição de uma interface:

  1. Indique o objetivo. Antes que outros desenvolvedores utilizem uma interface, eles precisam entender o conceito que ela encapsula. Em outras palavras, precisam saber seu objetivo. Um teste realmente bom para determinar se é ou não necessário definir uma interface é se seu objetivo pode ou não ser facilmente descrito. Se tiver dificuldades em descrevê-lo, existem grandes chances de que a interface não é necessária para iniciar. Como o conceito de interfaces é nova para o Java, as pessoas ainda não estão experientes em seu uso apropriado e provavelmente irão utilizá-las em demasia porque são novas.
  2. Como as interfaces devem e não devem ser utilizadas. Os desenvolvedores precisam saber como uma interface deve ser utilizada e também como não o deve ser [COA97].

Como a assinatura das funções-membro é definida em uma interface, para cada assinatura de função-membro é necessário seguir as convenções de documentação da função-membro discutidas no Capítulo 3.

7.3    Padrões para Pacotes

Os padrões importantes para pacotes têm como base:

  • convenções de nomenclatura
  • convenções de documentação

7.3.1    Nomeando Pacotes

Há várias regras associadas à nomenclatura de pacotes. Em ordem, essas regras são:

  1. Os identificadores são separados por pontos. Para tornar os nomes de pacotes mais legíveis, a Sun sugere que os identificadores em nomes de pacotes sejam separados por pontos. Por exemplo, o nome de pacote java.awt é composto de dois identificadores, java e awt.
  2. Os pacotes de distribuição Java padrão da Sun começam com o identificador "java". A Sun reservou esse direito para que os pacotes Java padrão sejam nomeados de maneira consistente, independentemente do fornecedor do seu ambiente de desenvolvimento Java.
  3. Os nomes de pacotes locais começam com um identificador que não tem todas as letras maiúsculas. Os pacotes locais são utilizados internamente na organização e não serão distribuídos a outras organizações. Exemplos desses nomes de pacotes incluem persistence.mapping.relational e interface.screens.
  4. Nomes de pacotes globais começam com o nome de domínio da Internet reverso de sua organização. Um pacote que será distribuído para várias organizações deve incluir o nome do domínio da organização de origem, com o tipo do domínio de nível superior em maiúsculas. Por exemplo, para distribuir os pacotes anteriores, eles deveriam ser denominados com.rational.www.persistence.mapping.relational e com.rational.www.interface.screens.0

7.3.2    Documentando Pacotes

Você deve manter um ou mais documentos externos que descrevam o objetivo dos pacotes desenvolvidos por sua organização. Para cada pacote, você deve documentar:

  1. O fundamento lógico do pacote. Outros desenvolvedores precisam saber do que se trata um pacote para que possam determinar se desejam ou não utilizá-lo e, se for um pacote compartilhado, se desejam ou não aprimorar ou estendê-lo.
  2. As classes no pacote. Inclua uma lista das classes e interfaces no pacote com uma descrição breve de uma linha sobre cada uma, para que os demais desenvolvedores saibam o que o pacote contém.

Dica: Crie um arquivo HTML utilizando o nome do pacote e coloque-o no diretório apropriado do pacote. O arquivo deve ter o sufixo .html.

7.4    Padrões para Unidades de Compilação

Os padrões e diretrizes para unidades de compilação têm como base:

7.4.1    Nomeando Unidades de Compilação

Uma unidade de compilação, neste caso um arquivo de código fonte, deve receber o nome da classe primária ou interface que está declarada dentro dela. Utilize o mesmo nome do pacote ou classe para o nome do arquivo, utilizando o mesmo padrão de maiúsculas e minúsculas. A extensão .java deve ser o sufixo do nome do arquivo.

Exemplos:

Customer.java

Singleton.java

SavingsAccount.java

7.4.2    Documentando Unidades de Compilação

Embora você deva se esforçar em ter apenas uma declaração de classe ou interface por arquivo, às vezes faz sentido definir várias classes (ou interfaces) no mesmo arquivo. Um método baseado na experiência geral é que se o único objetivo da classe B for encapsular a funcionalidade necessária apenas pela classe A, faz sentido que a classe B apareça no mesmo arquivo de código fonte que a classe A. Como resultado, as seguintes convenções de documentação são aplicadas a um arquivo de código fonte e não especificamente a uma classe:

  1. Para arquivos com várias classes, liste cada classe. Se um arquivo contiver mais de uma classe, você deve fornecer uma lista das classes e uma breve descrição de cada uma.
  2. O nome do arquivo e/ou as informações de identificação. O nome do arquivo deve ser incluído em seu início. A vantagem é que se o código for impresso, você saberá qual é o arquivo de origem do código.
  3. Informações de direitos autorais. Se aplicável, você deve indicar todas as informações de direitos autorais para o arquivo. É comum indicar o ano dos direitos autorais e o nome do indivíduo ou organização que detém os direitos autorais. Note que o autor do código pode não ser o portador dos direitos autorais.

8    Tratamento de Erros e Exceções

A filosofia geral é utilizar exceções apenas para erros: erros lógicos e de programação, erros de configuração, dados corrompidos, exaustão de recursos e assim por diante. A regra geral é que os sistemas, em condições normais e na ausência de sobrecarga ou falha de hardware, não devem emitir nenhuma exceção.

  1. Utilize exceções para manipular erros lógicos e de programação, erros de configuração, dados corrompidos e exaustão de recursos

Relate as exceções pelo mecanismo de log apropriado o mais cedo possível, incluindo o ponto em que elas foram emitidas.

  1. Minimize o número de exceções exportadas de uma determinada abstração.

Em sistemas grandes, a manipulação de um grande número de exceções em cada nível torna o código difícil de ler e manter. Às vezes, o processamento da exceção atrapalha o processamento normal.

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

  • Exporte apenas umas poucas exceções, mas forneça primitivas de "diagnóstico" que permitam a consulta da abstração com falha ou o objeto inválido para obter informações mais detalhadas sobre a natureza do problema ocorrido.
  • Inclua estados "excepcionais" nos objetos e forneça primitivas para verificar explicitamente a validade dos objetos.
  1. Não utilize exceções para eventos antecipados e freqüentes.

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

  • É confuso.
  • Isso geralmente força alguma interrupção no fluxo de controle que é mais difícil de ser entendido e mantido.
  • Isso torna a depuração do código mais complicada, uma vez que a maioria dos depuradores de nível de origem sinaliza todas as exceções, por padrão.

Por exemplo, não utilize uma exceção como alguma forma de valor adicional retornado por uma função (tal como Value_Not_Found em uma procura); utilize um procedimento com um parâmetro de "saída", introduza um valor especial que signifique Not_Found, ou ainda empacote o tipo retornado em um registro com um Not_Found característico.

  1. Não utilize exceções para implementar 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".

  1. Certifique-se de que os códigos de status tenham um valor apropriado.

Ao utilizar código de status retornado por subprogramas como um parâmetro de "saída", sempre certifique-se de que um valor seja designado ao parâmetro de "saída", tornando-o 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.

  1. Execute localmente as verificações de segurança; não espere que o cliente o fará.

Se um subprograma puder produzir saída errônea se não receber uma entrada correta, instale um código no subprograma para detectar e relatar entrada inválida de uma maneira controlada. Não confie em um comentário que informe ao cliente para transmitir os valores apropriados. É praticamente garantido que cedo ou tarde esse comentário será ignorado, resultando em erros difíceis de se depurar se os parâmetros inválidos não forem detectados.


9    Padrões e Questões Diversas

Este capítulo descreve diversos padrões e orientações importantes que são gerais o bastante para ter seu próprio capítulo.

9.1    Reutilizando

Qualquer pacote ou biblioteca de classes Java adquirido ou reutilizado de uma fonte externa deve ser certificado como Java 100% puro [SUN97]. Aplicando esse padrão, você garante que esteja o que está sendo reutilizado funcionará em todas as plataformas escolhidas para implantá-lo. É possível obter classes, pacotes ou applets Java de diversas fontes, seja uma empresa de desenvolvimento de terceiros especializada em bibliotecas Java, ou outra divisão ou equipe de projeto em sua organização.

9.2    Importando Classes

A instrução import permite a utilização de curingas ao indicar os nomes de classes. Por exemplo, a instrução

import java.awt.*;

traz todas as classes no pacote java.awt. Na realidade, isso não é totalmente verdadeiro. O que realmente acontece é que toda classe utilizada do pacote java.awt será trazida para dentro do seu código quando ele for compilado; as classes não utilizadas não o serão. Embora isso pareça um bom recurso, ele reduz a legibilidade do código. Uma melhor abordagem é nomear completamente as classes utilizadas pelo código [LAF97]; [VIS96]. Uma melhor forma de importar classes é mostrada no exemplo a seguir:

import java.awt.Color
import java.awt.Button
import java.awt.Container

9.3    Otimizando Código Java

A otimização do código é uma das últimas coisas em que os programadores devem pensar, não uma das primeiras. Deixe a otimização para o final, porque você deseja otimizar apenas o código que a necessita. Muito freqüentemente uma pequena porcentagem do código resulta na grande maior parte do tempo de processamento, e esse é o código que deverá ser otimizado. Um erro clássico feito por programadores inexperientes é tentar otimizar todo o código, mesmo o código que já seja executado rápido o bastante.

  1. Não perca seu tempo otimizando código com o qual ninguém se importa!

O que você deve procurar ao otimizar o código? Como [KOE97] aponta, os fatores mais importantes são código extra fixo e o desempenho em grandes entradas. A razão para isso é simples: o código extra fixo domina a velocidade do tempo de execução para pequenas entradas e o algoritmo domina para grandes entradas. O método baseado na experiência de Koenig é que um programa que funcione bem para entradas pequenas e grandes provavelmente funcionará bem para entradas de médio porte.

Os desenvolvedores que precisam escrever software que funcione em diversas plataformas de hardware e/ou sistemas operacionais, precisam estar cientes das peculiaridades nas diversas plataformas. Operações que possam aparentar tomar uma quantidade de tempo específica, como a forma em que a memória e os buffers são manipulados, freqüentemente mostram variações substanciais entre as plataformas. É comum descobrir que você precisa otimizar o código de forma diferente, dependendo da plataforma.

Outra questão que se deve estar ciente ao otimizar código são as prioridades dos usuários, porque as pessoas serão sensíveis a retardos específicos, dependendo do contexto. Por exemplo, os usuários provavelmente ficariam mais felizes com uma tela que é apresentada imediatamente e que, em seguida, leva oito segundos para carregar dados, em vez de uma tela que é apresentada após demorar cinco segundos para carregar dados. Em outras palavras, a maior parte dos usuários deseja aguardar um pouco mais, desde que recebam feedback imediato - conhecimento importante ao se otimizar o código.

  1. Nem sempre é necessário fazer com que o código seja executado mais rapidamente para otimizá-lo aos olhos dos usuários.

Embora a otimização possa significar a diferença entre o êxito e a falha do aplicativo, nunca se esqueça de que é muito mais importante fazer com que o código funcione corretamente. Nunca se esqueça de que um software lento que funciona é sempre preferível a um software rápido que não funciona.

9.4    Gravando Ferramentas de Teste Java

O teste orientado a objetos é um tópico crítico que de forma alguma foi ignorado pela comunidade de desenvolvimento de objetos. A realidade é que você ou outra pessoa terá que testar o software que você escreveu, independentemente da linguagem escolhida para trabalhar. Uma ferramenta de teste é a coleção de funções-membro, algumas incorporadas nas próprias classes (denominadas testes internos) e algumas em classes de teste especializadas, utilizada para testar seu aplicativo.

  1. Prefixe todos os nomes de função-membro de teste com "test". Isso permite localizar rapidamente todas as funções-membro de teste no código. A vantagem de prefixar o nome das funções-membro de teste com "test" é que isso permite facilmente remover as funções-membro de teste do código fonte antes de compilar sua versão de produção.
  2. Nomeie todas as funções-membro de teste de função-membro de forma consistente. O teste de método é o ato de verificar se uma única função-membro é executada como foi definida. Todas as funções-membro de teste de função-membro devem ser nomeadas seguindo o formato "testMemberFunctionNameForTestName". Por exemplo, as funções-membro de ferramentas de teste para testar withdrawFunds() incluiriam testWithdrawFundsForInsufficientFunds() e testWithdrawFundsForSmallWithdrawal(). Se tiver uma série de testes para withdrawFunds(), poderá escrever uma função-membro denominada testWithdrawFunds() que chama todas elas.
  3. Nomeie todas as funções-membro de teste de classe de forma consistente. O teste de classe é o ato de verificar se uma única classe é executada como foi definida. Todas as funções-membro de teste de classe devem ser nomeadas seguindo o formato "testSelfForTestName". Por exemplo, as funções-membro de ferramentas de teste para testar a classe Account seriam testSelfForSimultaneousAccess() e testSelfForReporting().
  4. Crie um único ponto de chamada para os testes de uma classe. Desenvolva uma função-membro estática denominada testSelf() que chama todas as funções-membro de teste de método ou de teste de classe.
  5. Documente suas funções-membro de ferramentas de teste. Documente suas funções-membro de ferramentas de teste. A documentação deve incluir uma descrição do teste, bem como os resultados esperados do teste.

10    Padrões de Sucesso

Ter um documento de padrões em sua posse não o torna automaticamente mais produtivo como um desenvolvedor. Para obter êxito, é necessário optar por ser mais produtivo, o que significa que você deve aplicar esses padrões de forma efetiva.

10.1    Utilizando esses Padrões com Eficácia

As recomendações a seguir ajudarão você a utilizar as diretrizes e padrões de codificação Java descritos neste documento mais efetivamente.

  1. Entenda os padrões. Tome tempo para entender porque cada padrão e diretriz leva a maior produtividade. Por exemplo, não declare cada variável local em sua própria linha porque essas diretrizes o dizem para fazê-lo. Faça-o porque você entende que isso melhora a capacidade de compreensão do código.
  2. Acredite neles. Entender cada padrão é um início, mas também é necessário acreditar neles. Seguir padrões não deve ser algo que você faz quando tem tempo; deve ser algo que você sempre faz porque acredita que essa é a melhor forma de codificar.
  3. Siga-os enquanto está codificando, não como uma reflexão tardia. Um código documentado é mais fácil de entender quando você o está escrevendo e depois que ele está escrito. Campos e funções-membro nomeadas de forma consistente são mais fáceis de se trabalhar durante o desenvolvimento e a manutenção. Um código limpo é mais fácil de se trabalhar durante o desenvolvimento e a manutenção. O resultado é que seguindo padrões, você aumentará sua produtividade, enquanto estiver desenvolvendo, e tornará seu código mais fácil de manter (assim tornando os desenvolvedores de manutenção mais produtivos também). Se escrever código limpo a partir do início, poderá se beneficiar disso enquanto o está criando.
  4. Faça-os parte de seu processo de garantia de qualidade. Parte de uma inspeção de código deve ser assegurar que o código fonte segue os padrões adotados pela organização. Utilize padrões como a base a partir da qual os desenvolvedores são treinados e orientados para ficarem mais eficazes.

10.2    Outros Fatores que Levam a Gravar Código com Sucesso

  1. Programe para pessoas, não para a máquina. O principal objetivo de seus esforços de desenvolvimento deve ser que seu código seja facilmente entendido por outras pessoas. Se mais ninguém puder entender, ele não é bom. Utilize convenções de nomenclatura. Documente seu código. Utilize parágrafos.
  2. Faça o design primeiro, depois codifique. Você já esteve em uma situação em que parte do código do qual seu programa depende precise ser alterado? Talvez um novo parâmetro precise ser transmitido para uma função-membro, ou talvez uma classe precise ser dividida em várias classes. Quanto trabalho extra você teve para certificar-se de que seu código funciona com a versão reconfigurada do código modificado? Isso o deixou alegre? Você se perguntou porque alguém não parou e pensou sobre isso primeiro, quando ele ou ela escreveu originalmente o código, para que isso não precisasse acontecer ou que eles deveriam ter feito seu DESIGN primeiro? É evidente que sim. Se você separar um tempo para compreender como irá escrever o código antes de realmente começar a codificar, provavelmente gastará menos tempo escrevendo-o. Além disso, há uma grande chance de reduzir o impacto de futuras alterações no código, simplesmente pensando nelas com antecedência.
  3. Desenvolva em pequenas etapas. Desenvolver em pequenas etapas - escrever algumas funções-membro, testá-las e, em seguida, escrever algumas mais - normalmente é muito mais eficaz que escrever uma grande quantidade de código de uma vez e, em seguida, tentar corrigi-lo. É muito mais fácil testar e corrigir dez linhas de código do que 100; de fato, é seguro dizer que você poderia programar, testar e corrigir 100 linhas de código em dez incrementos de dez linhas em menos da metade do tempo que poderia escrever um único bloco de cem linhas de código que faria o mesmo trabalho. 
    A razão para isso é simples. Sempre que estiver testando seu código e encontrar um erro, quase sempre localizará o erro no novo código que acabou de escrever (assumindo, é claro, que o resto do código estava razoavelmente sólido a princípio). É possível caçar um erro muito mais rapidamente em uma seção pequena de código do que em uma grande. Desenvolvendo em pequenas etapas incrementais, você reduz o tempo médio que se demora para localizar um erro, o que por sua vez reduz o tempo de desenvolvimento geral.
  4. Mantenha o código simples. Escrever um código complexo pode ser intelectualmente satisfatório, mas se outras pessoas não puderem entendê-lo, ele não é realmente bom. A primeira vez que alguém, talvez mesmo você, precisar modificar um trecho de código complexo para corrigir um erro ou aprimorá-lo, existem grandes chances de que o código será reescrito. De fato, você provavelmente já teve que reescrever o código de alguma outra pessoa porque ele era muito difícil de se entender. O que você pensa sobre o desenvolvedor original quando você reescreveu seu código? Você considera essa pessoa um gênio ou um idiota? Escrever código que precisará ser reescrito posteriormente não é nenhum motivo de orgulho, portanto siga a regra para mantê-lo simples.
  5. Aprenda padrões comuns, antipadrões e idiomáticas. Existe uma abundância de padrões e antipadrões de análise, design e processo, bem como idiomáticas de programação, disponíveis para guiá-lo no aprimoramento da produtividade de desenvolvimento.

11    Resumo

Este capítulo resume as diretrizes fornecidas aqui para sua conveniência e está organizado em vários resumos de uma página de nossos padrões de codificação Java, unidos por tópico. Esses tópicos são:

  • Convenções de nomenclatura Java
  • Convenções de documentação Java
  • Convenções de codificação Java

Antes de resumir o restante dos padrões e diretrizes descritos neste white paper, é importante reiterar a diretiva principal:

Quando não seguir um padrão, documente isso. Todos os padrões, exceto este, podem ser transgredidos. Se o fizer, deverá documentar porque transgrediu o padrão, as implicações potenciais dessa quebra e quaisquer condições que podem/devem ocorrer antes que o padrão possa ser aplicado a essa situação.

11.1    Convenções de Nomenclatura Java

Com algumas exceções discutidas a seguir, você deve sempre utilizar descritores completos em inglês ao nomear coisas. Além disso, você deve utilizar letras minúsculas em geral, mas utilize maiúsculas para a primeira letra dos nomes das classes e interfaces, bem como a primeira letra de qualquer palavra não-inicial.

Conceitos Gerais:

  • Utilize descritores completos em inglês.
  • Utilize a terminologia aplicável ao domínio.
  • Utilize nomes compostos por letras maiúsculas e minúsculas para torná-los legíveis.
  • Utilize formas curtas com moderação, mas se o fizer, utilize-as com inteligência.
  • Evite nomes longos (menos de 15 caracteres é uma boa idéia).
  • Evite nomes semelhantes ou que sejam diferentes apenas em maiúsculas e minúsculas.
  • Evite sublinhados.
Item Convenção de Nomenclatura Exemplo
Argumentos/
parâmetros
Utilize uma descrição completa em inglês do valor/objeto que está sendo transmitido, possivelmente prefixando o nome com "a" ou "an". O fato importante é escolher uma abordagem e continuar com ela.
customer, account,
               - ou -
aCustomer, anAccount
Campos/
campos/
propriedades
Utilize uma descrição completa em inglês do campo, com a primeira letra em minúsculas e a primeira letra de qualquer palavra não-inicial em maiúsculas.
firstName, lastName, 
warpSpeed
 
Funções-membro getter booleanas Todos os getters booleanos devem ter como prefixo a palavra "is". Se você seguir o padrão de nomenclatura para campos booleanos descrito acima, simplesmente dê a ele o nome do campo.
isPersistent(), isString(), 
isCharacter() 
Classes Utilize uma descrição completa em inglês, com as primeiras letras de todas as palavras em maiúsculas.
Customer, SavingsAccount
Arquivos de unidade de compilação Utilize o nome da classe ou interface ou, se houver mais de uma classe no arquivo, a classe primária, finalizada com ".java" para indicar que ele é um arquivo de código fonte.
Customer.java, 
SavingsAccount.java,
Singleton.java
Componentes/
widgets
Utilize uma descrição completa em inglês que descreva para que o componente é utilizado, com o tipo do componente concatenado no final.
okButton, customerList, 
fileMenu
Construtores Utilize o nome da classe.
Customer(), SavingsAccount()
Destrutores Java não tem destrutores, mas em vez disso chamará a função-membro finalize() antes que um objeto tenha o lixo coletado.
finalize()
Exceções Geralmente é aceito utilizar a letra "e" para representar exceções.
e
Campos estáticos finais (constantes) Utilize todas as letras maiúsculas com as palavras separadas por sublinhados. Uma melhor abordagem é utilizar funções-membro getter estáticas finais porque elas aumentam em muito a flexibilidade .
MIN_BALANCE, DEFAULT_DATE
Funções-membro getter Prefixe o nome do campo que está sendo acessado com "get".
getFirstName(), getLastName(), 
getWarpSpeeed()
Interfaces Utilize uma descrição completa em inglês que descreva o conceito encapsulado pela interface, com as primeiras letras de todas as palavras em maiúsculas. É comum sufixar o nome com "able", "ible" ou "er", mas isso não é requerido.
Runnable, Contactable, 
Prompter, Singleton
Variáveis locais Utilize descrições completas em inglês com a primeira letra em minúsculas, mas não oculte campos/campos existentes. Por exemplo, se tiver um campo denominado "firstName", não tenha uma variável local denominada "firstName".
grandTotal, customer, 
newAccount
Contadores de loop É geralmente aceito utilizar as letras i, j ou k ou o nome counter.
i, j, k, counter
Pacotes Utilize descrições completas em inglês, utilizando composição de letras maiúsculas e minúsculas com a primeira letra de cada palavra em maiúsculas, e todo o restante em minúsculas. Para pacotes globais, reverta o nome do domínio da Internet e concatene o nome do pacote a isso.
java.awt,
com.ambysoft.www.
persistence.mapping
Funções-membro Utilize uma descrição completa em inglês sobre o que a função-membro faz, iniciando com um verbo ativo sempre que possível, com a primeira letra em minúsculas.
openFile(), addAccount()
Funções-membro setter Prefixe o nome do campo que está sendo acessado com "set".
setFirstName(), setLastName(), 
setWarpSpeed()

11.2    Convenções de Documentação Java

Um método baseado na experiência muito bom para seguir a documentação relativa é perguntar-se, caso nunca tenha visto o código anteriormente, "quais informações seriam necessárias para entender efetivamente o código em uma quantidade de tempo razoável?"

Conceitos Gerais:

11.2.1    Tipos de Comentário Java

O gráfico a seguir descreve os três tipos de comentários Java e usos sugeridos para eles.

Tipo de Comentário Uso Exemplo
Documentação Utilize comentários de documentação imediatamente antes de declarações de interfaces, classes, funções-membro e campos para documentá-los. Os comentários de documentação são processados pelo javadoc (consulte a seguir) para criar documentação externa para uma classe. /**
Customer: A customer is any person or organization that we sell services and products to.
@author S.W. Ambler
*/
Estilo C Utilize comentários em estilo C para documentar linhas de código que não mais são aplicáveis, mas que deseja manter para o caso dos usuários mudarem de idéia, ou porque deseja desativá-las temporariamente durante a depuração. /*
This code was commented out by B.Gustafsson, June 4 1999 because it was replaced by the preceding code. Delete it after two years if it is still not applicable.
. . . (the source code )
*/
Linha única Utilize os comentários de linha única internamente nas funções-membro para documentar a lógica de negócios, seções de código e declarações de variáveis temporárias. // Apply a 5% discount to all invoices
// over $1000 as defined by the Sarek
// generosity campaign started in
// Feb. of 1995.

11.2.2    O que Documentar

O gráfico a seguir resume o que documentar relativo a cada parte do código Java gravado.

Item O que Documentar
Argumentos/
parâmetros
O tipo do parâmetro

Para que ele deve ser utilizado

Quaisquer restrições ou condições prévias

Exemplos

Campos/
campos/propriedades
Sua descrição

Documentar todas as invariantes aplicáveis

Exemplos

Problemas de simultaneidade

Decisões de visibilidade

Classes O objetivo da classe

Erros conhecidos

O histórico de desenvolvimento e manutenção da classe

Documentar invariantes aplicáveis

A estratégia de simultaneidade

Unidades de Compilação Cada classe ou interface definida na classe, incluindo uma descrição breve

O nome do arquivo e/ou as informações de identificação

Informações de direitos autorais

Função-membro getter Documentar porque a inicialização foi utilizada, se aplicável
Interfaces O objetivo

Como devem e não devem ser utilizadas

Variáveis locais Seu uso ou objetivo
Funções-membro: Documentação O que e porque a função-membro faz o que faz

Qual função-membro deve ser transmitida como parâmetro

O que uma função-membro retorna

Erros conhecidos

Todas as exceções emitidas por uma função-membro

Decisões de visibilidade

Como uma função-membro altera o objeto

Inclua um histórico de todas as alterações de código

Exemplos de como chamar a função-membro, se apropriado

Condições prévias e posteriores aplicáveis

Funções-membro: Comentários internos Estruturas de controle

Por que, e também o que, o código faz

Variáveis locais

Código difícil ou complexo

A ordem de processamento

Pacote O fundamento lógico do pacote

As classes no pacote

11.3    Convenções de Codificação Java (geral)

Existem muitas convenções e padrões que são críticos à capacidade de manutenção e aprimoramento do seu código Java. 99,9% do tempo é mais importante programar para pessoas, seus colegas desenvolvedores, do que programar para a máquina. Tornar seu código compreensível por outras pessoas é de máxima importância.

Destino de Convenção Convenção
Funções-membro do Acessador Considere a utilização de inicialização adiada para campos no banco de dados

Utilize acessadores para obter e modificar todos os campos

Utilize acessadores para "constantes"

Para coleções, inclua funções-membro para inserir e remover itens

Sempre que possível, torne os acessadores protegidos, não públicos

Campos Os campos devem sempre ser declarados privados

Não acesse diretamente os campos; em vez disso, utilize funções-membro do acessador

Não utilize campos estáticos finais (constantes); em vez disso, utilize funções-membro do acessador

Não oculte nomes

Sempre inicialize campos estáticos

Classes Minimize as interfaces públicas e protegidas

Defina a interface pública para uma classe antes de começar a codificá-la

Declare os campos e funções-membro de uma classe na seguinte ordem:

  • construtores
  • finalize()
  • funções-membro públicas
  • funções-membro protegidas
  • funções-membro privadas
  • campo privado
Variáveis locais Não oculte nomes

Declare uma variável local por linha de código

Documente variáveis locais com um comentário seqüencial

Declare variáveis locais imediatamente antes de seu uso

Utilize variáveis locais apenas para uma coisa

Funções-membro Documente seu código

Utilize parágrafo em seu código

Utilize espaço em branco, uma linha antes das estruturas de controle e duas antes de declarações de função-membro

Uma função-membro deve ser compreensível em menos de trinta segundos

Grave linhas de comando curtas e simples

Restrinja a visibilidade de uma função-membro o máximo possível

Especifique a ordem das operações


12    Referências

Código de Referência Informações Relacionadas
[AMB98] Ambler, S.W. (1998). Building Object Applications That Work: Your Step-By-Step Handbook for Developing Robust Systems with Object Technology. New York: SIGS Books/Cambridge University Press.
[COA97] Coad, P. and Mayfield, M. (1997). Java Design: Building Better Apps & Applets. Upper Saddle River, NJ: Prentice Hall Inc.
[DES97] DeSoto, A. (1997). Using the Beans Development Kit 1.0 February 1997: A Tutorial. Sun Microsystems.
[GOS96] Gosling, J., Joy, B., Steele, G. (1996). The Java Language Specification. Reading, MA: Addison Wesley Longman Inc.
[GRA97] Grand, M. (1997). Java Language Reference. Sebastopol, CA: O. Reilly & Associates, Inc.
[KAN97] Kanerva, J. (1997). The Java FAQ. Reading, MA: Addison Wesley Longman Inc.
[KOE97] Koenig, A. (1997). The Importance--and Hazards--of Performance Measurement. New York: SIGS Publications, Journal of Object-Oriented Programming, January, 1997, 9(8), pp. 58-60.
[LAF97] Laffra, C. (1997). Advanced Java: Idioms, Pitfalls, Styles and Programming Tips. Upper Saddle River, NJ: Prentice Hall Inc.
[LEA97] Lea, D. (1997). Concurrent Programming in Java: Design Principles and Patterns. Reading, MA: Addison Wesley Longman Inc.
[MCO93] McConnell, S. (1993). Code Complete: A Practical Handbook of Software Construction. Redmond, WA: Microsoft Press.
[MEY88] Meyer, B. (1988). Object-Oriented Software Construction. Upper Saddle River, NJ: Prentice Hall Inc.
[NAG95] Nagler, J. (1995). Coding Style and Good Computing Practices. http://wizard.ucr.edu/~nagler/coding_style.html
[SUN96] Sun Microsystems (1996). javadoc - The Java API Documentation Generator. Sun Microsystems.
[SUN97] Sun Microsystems (1997). 100% Pure Java Cookbook for Java Developers: Rules and Hints for Maximizing the Portability of Java Programs. Sun Microsystems.
[VIS96] Vision 2000 CCS Package and Application Team (1996). Coding Standards for C, C++, and Java. http://v2ma09.gsfc.nasa.gov/coding_standards.html

13    Glossário

100% puro: Efetivamente um "selo de aprovação" da Sun que diz que um applet, aplicativo ou pacote Java será executado em QUALQUER plataforma que suporte a Java VM.

Acessador: Uma função-membro que modifica ou retorna o valor de um campo. Também conhecido como um modificador de acesso. Consulte Getter e Setter.

Padrão de análise: Um padrão de modelagem que descreve uma solução para um problema de domínio ou negócio.

Antipadrão: Uma abordagem para resolver um problema comum, que com o tempo prova ser incorreto ou altamente ineficaz.

Argumento: Consulte parâmetro.

BDK: Beans Development Kit

Bloco: Uma coleção de zero ou mais instruções contidas entre parênteses.

Parênteses: Os caracteres { e }, conhecidos respectivamente como parêntese de abertura e parêntese de fechamento, são utilizados para definir o início e o fim de um bloco.

Classe: Uma definição, ou gabarito, a partir do qual os objetos são instanciados.

Teste de classe: O ato de assegurar que uma classe e suas instâncias (objetos) são executados conforme definido.

CMVC: Configuration Management and Version Control

Unidade de compilação: Um arquivo de código fonte, seja físico no disco ou "virtual" armazenado em um banco de dados, no qual classes e interfaces são declaradas.

Componente: Um widget de interface, tal como uma lista, botão ou janela.

Getter de constante: Uma função-membro getter que retorna o valor de uma "constante", que pode, por sua vez, estar em código permanente ou ser calculada se necessário.

Construtor: Uma função-membro que executa quaisquer inicializações necessárias quando um objeto é criado.

Contenção: Um objeto contém outros objetos com os quais ele colabora para desempenhar seus comportamentos. Isso pode ser realizado com a utilização de classes internas (JDK 1.1+) ou a agregação de instâncias de outras classes dentro de um objeto (JDK 1.0+).

CPU: Unidade de Processamento Central

Comentários em estilo C: Um formato de comentário Java, /* e */, adotado a partir da linguagem C/C++, que pode ser utilizado para criar comentários de várias linhas. Normalmente utilizados para "documentar" linhas de código desnecessárias ou não desejadas durante o teste.

Padrão de design: Um padrão de modelagem que descreve uma solução para um problema de design.

Destrutor: Uma função-membro de classe C++ utilizada para remover um objeto da memória quando ele não mais for necessário. Como o Java gerencia sua própria memória, esse tipo de função-membro não é necessário. O Java suporta, entretanto, uma função-membro semelhante em conceito, denominada finalize().

Comentários de documentação: Um formato de comentário Java, /** e */, que pode ser processado pelo javadoc para fornecer documentação externa para um arquivo de classe. A documentação principal para interfaces, classes, funções-membro e campos deve ser escrita com comentários de documentação.

Campo: Uma variável, do tipo de dados literal ou outro objeto, que descreve uma classe ou uma instância de uma classe. Os campos de instância descrevem objetos (instâncias) e os campos estáticos descrevem classes. Os campos também são referidos como campos, variáveis de campo e propriedades.

finalize(): Uma função-membro chamada automaticamente durante a coleta de lixo, antes que um objeto seja removido da memória. O objetivo dessa função-membro é fazer qualquer limpeza necessária, tal como fechar arquivos abertos.

Coleta de lixo: O gerenciamento automático de memória, em que os objetos que não mais são referidos são removidos automaticamente da memória.

Getter: Um tipo de função-membro do acessador que retorna o valor de um campo. Um getter pode ser utilizado para responder o valor de uma constante, o que normalmente é preferível a implementar a constante como um campo estático, pois isso é uma abordagem mais flexível.

HTML: Linguagem de marcação de hipertexto, um formato padrão de mercado para criar páginas da Web.

Recuo: Consulte utilização de parágrafo.

Comentários seqüenciais: A utilização de um comentário de linha para documentar uma linha de código fonte, em que o comentário segue imediatamente o código, na mesma linha que este. Os comentários de linha única normalmente são utilizados para isso, embora os comentários em estilo C também possam ser empregados.

Interface: A definição de uma assinatura comum, incluindo funções-membro e campos, que uma classe que implementa uma interface deve suportar. As interfaces promovem o polimorfismo por composição.

E/S: Entrada/Saída

Invariante: Um conjunto de asserções sobre uma instância ou classe que devem ser verdadeiras em todos os momentos "estáveis", tais como os períodos antes e depois da chamada de uma função-membro no objeto ou classe.

Java: Uma linguagem de desenvolvimento padrão de mercado orientada a objetos que é bastante adequada para o desenvolvimento de aplicativos para a Internet e aplicativos que devem operar em uma variedade de plataformas de computação.

javadoc: Um utilitário incluído no JDK que processa um arquivo de código fonte Java e produz um documento externo, em formato HTML, descrevendo o conteúdo do arquivo de código fonte com base nos comentários de documentação no arquivo de código.

JDK: Java Development Kit

Inicialização adiada: Uma técnica na qual um campo é inicializado em sua função-membro getter correspondente na primeira vez em que for necessário. A inicialização adiada é utilizada quando um campo não é normalmente necessário e requer uma grande quantidade de memória para armazenamento ou precisa ser lido a partir de um armazenamento permanente.

Variável local: Uma variável definida no escopo de um bloco, normalmente uma função-membro. O escopo de uma variável local é o bloco no qual ela está definida.

Função-membro: Um trecho de código executável associado a uma classe ou a instâncias de uma classe. Pense na função-membro como o equivalente orientado a objetos de uma função.

Assinatura da função-membro: Consulte assinatura.

Teste de método: O ato de assegurar que uma função-membro é executada conforme definido.

Ocultação de nome: A prática de utilizar o mesmo nome, ou pelo menos um semelhante, para um campo, variável ou argumento que o de um com escopo superior. O abuso mais comum da ocultação de nome é nomear uma variável local da mesma forma que um campo da instância. A ocultação de nome deve ser evitada pois torna o código mais difícil de entender e propenso a erros.

Sobrecarregar: Uma função-membro é dita sobrecarregada quando está definida mais de uma vez na mesma classe (ou em uma subclasse); a única diferença é a assinatura de cada definição.

Substituir: Uma função-membro é dita substituída quando é redefinida em uma subclasse e tem a mesma assinatura que a definição original.

Pacote: Uma coleção de classes relacionadas.

Utilização de parágrafo: Uma técnica onde você recua o código dentro do escopo de um bloco de código por uma unidade, normalmente uma tabulação horizontal, de forma a distingui-lo do código fora do bloco de código. A utilização de parágrafo ajuda a aumentar a legibilidade do código.

Parâmetro: Um argumento transmitido para uma função-membro, um parâmetro pode ser um tipo definido, como string ou int, ou um objeto.

Condição posterior: Uma propriedade ou asserção que será verdadeira depois que a execução de uma função-membro for concluída.

Condição prévia: Uma restrição sob a qual uma função-membro funcionará corretamente.

Propriedade: Consulte campo.

Setter: Uma função-membro do acessador que define o valor de um campo.

Assinatura: A combinação do tipo de parâmetros, se houver, e sua ordem que deve ser transmitida para uma função-membro. Também é chamada de assinatura da função-membro.

Comentários de linha única: Um formato de comentário Java, // , adotado a partir da linguagem C/C++ que é normalmente utilizado para a documentação interna de função-membro da lógica de negócios.

Tags: Uma convenção para marcar seções especificadas dos comentários de documentação que serão processadas pelo javadoc para produzir comentários com aparência profissional. Exemplos de tags incluem @see e @author.

Ferramentas de teste: Uma coleção de funções-membro para testar seu código.

UML: Idioma unificado de modelagem, que é uma notação de modelagem padrão de mercado.

Visibilidade: Uma técnica utilizada para indicar o nível de encapsulamento de uma classe, função-membro ou campo. As palavras-chave - public, protected e private - podem ser utilizada para definir a visibilidade.

Espaço em branco: Linhas em branco, espaços e tabulações incluídas no código para aumentar sua legibilidade.

Widget: Consulte componente.