Uma Questão de Estilo Elementos de Estilo Java Vasco. T. Vasconcelos 9 Setembro 2004 Escrevemos programas para serem lidos por humanos. Pelos outros, mas também por nós, daqui a uma semana. As linguagens de programação não vêm equipadas com regras de estilo. De facto, nada nos impede de escrever um programa completo em apenas uma linha de código. No entanto, a comunidade de programadores de uma dada linguagem desenvolve um certo estilo. Um estilo comum favorece a leitura do nosso código pelos outros programadores, mas, talvez mais importante, torná-lo-á mais robusto, de mais fácil manutenção, e tenderá a conter menos bugs. As regras que abaixo enuncio são baseadas no livro de bolso The Elements of Java Style [1], cuja leitura recomendo a todos os estudantes da linguagem Java. Consulte também as convenções da Sun em http://java.sun.com/docs/codeconv/. 1 Formatação de texto Reflectir no texto do programa a estrutura lógica de blocos do programa. parte do código Java tem a forma X { bloco que deverá ser escrito X { bloco Exemplos: class UmaClasse { void f ( i n t n ) { i f ( n > 0) { for ( i n t i = 0; i > n ; i ++) { Grande i f ( j < 0) { i f ( j < 0) { { 1
Partir linhas demasiado longas. Linhas demasiado longas podem parecer bem no terminal, mas não quando impressas. Vamos limitar-nos a 80 caracteres por linha, partindo linhas demasiado longas. Exemplos: double l e n g t h = Math. s q r t ( Math. pow( x, 2. 0 ), Math. pow( y, 2. 0 ) ; return year < other. year year = = other. year && month < other. month ( year = = other. year && month = = other. month && day < other. day ) ; Incluir espaços. Utilizar um espaço para separar um parêntesis ) ou chaveta direita de uma palavra reservada; e uma palavra reservada de um parêntesis ( ou chaveta esquerda {. Exemplos: while. ( ). { i f. ( ). {. i f. ( ). {. { Utilizar um espaço antes e depois de qualquer operador binário, excepto o ponto.. return ( dayssince ( FIRST DATE) + 5) % 7; return Date. isleapyear ( year )? 365 : 366; Utilizar uma linha em branco para separar cada membro de uma classe. public class Date { / Ano. p r i v a t e i n t year ; / O ano desta data. p u b l i c i n t getmonth ( ) { r e t u r n month ; Evitar utilizar caracteres de tabulação. O número de espaços que correspondem a um tab varia de ambiente para ambiente. Um texto correctamente alinhado num ambiente de programação pode aparecer completamente desalinhado noutro. 2 Identificadores Utilizar nomes sugestivos. y = 1 5 8 2 ; Em vez de 2
utilizamos year = FIRST YEAR ; com a declaração / O ano da p r i m e i r a data. p u b l i c s t a t i c f i n a l i n t FIRST YEAR = 1 5 8 2 ; Não comer vogais. boolean islpyr ( ) {... boolean isleapyear ( ) {... Qual dos dois é mais fácil de ler? Nomes de classes ou interfaces começam por maiúscula. public class PrintStream extends FilterOutputStream { Utilizar substantivos para nomes de classes. ser identificados por substantivos. public class Date { Classes definem objectos que devem Utilizar substantivos ou adjectivos para nomes de interfaces. para nomear interfaces que descrevem serviços: public interface A c t i o n L i s t e n e r { e adjectivos para descrever competências: public interface Runnable { Utilizar substantivos Nomes de métodos começam por letra minuscula. public void advancetoweekday ( i n t weekday ) { Nomes de variáveis, de argumentos e de atributos começam por letra minúscula. private i n t year ; public s t a t i c i n t daysinmonth ( i n t year, i n t month ) { i n t r e s u l t ; 3
Todas as variáveis (ver result acima) têm o propósito de nos auxiliar em cálculos mais complexos. Chamar aux a uma variável não ajuda a compreensão do código. Todas as variáveis são temporárias: vivem até ao fim do bloco onde foram declaradas. Chamar temp a uma variável não nos ajuda em nada. Fazer coincidir o nome de um parâmetro com o nome atributo correspondente. public Date ( i n t year, i n t month, i n t day ) { this. year = year ; this. month = month ; this. day = day ; public void setyear ( i n t year ) { this. year = year ; Nomes de constantes escrevem-se apenas com maiúsculas; as várias palavras são separadas por underscore. public s t a t i c f i n a l i n t FIRST YEAR = 1 5 8 2 ; 3 Documentação Escrever documentação para quem utiliza o código; Escrever documentação para quem mantém o código; Manter o código e a documentação em sincronia; Utilizar a voz activa; Ser sucinto. Utilizar cabeçalhos para descrever classes, interfaces e seus membros. / Datas do c a l e n d á r i o Gregoriano, representadas por t r ê s i n t e i r o s : ano, mês e dia. p u b l i c class Date { / C o n s t r u i r uma data i g u a l à p r i m e i r a data v á l i d a. p u b l i c Date ( ) { 4
Utilizar comentários longos para esconder partes de código, sem o apagar. Porque os comentários não podem ser anichados, comentários longos devem ser utilizados apenas para esconder código temporariamente. / Escondi este código temporariamente. i f ( day < daysthismonth ( ) ) day ++; Utilizar comentários de linha para explicar detalhes de implementação. public i n t weekday ( ) { / / Sabemos que a p r i m e i r a data f o i uma sexta f e i r a r e t u r n ( dayssince ( FIRST DATE ) + 5 ) % 7 ; 4 Programação Definir métodos pequenos. Se um método tiver mais de 10 linhas, provavelmente contém em si mais do que uma rotina. Descubra quais e declare-as separadamente. Em vez de: public void add ( i n t numberofdays ) { for ( i n t i = 0 ; i < numberofdays ; i ++) i f ( day < daysthismonth ( ) ) day ++; { day = 1 ; i f ( month < 12) month ++; { month = 1 ; year ++; escrevemos uma rotina que permite avançar um dia: public void f o r t h ( ) { i f ( day < daysthismonth ( ) ) day ++; { / / Último dia do mês day = 1 ; i f ( month < 12) month + + ; / / Último dia dum mês que não Dezembro { / / 3 1 de Dezembro month = 1 ; year ++; e outra que permite avançar um dado número de dias public void add ( i n t numberofdays ) { for ( i n t i = 0 ; i < numberofdays ; i ++) f o r t h ( ) ; 5
Não declarar sem inicializar. Evitamos declarar de variáveis sem as inicializar. Para isso declaramos as variáveis onde precisamos delas, e não no início dos métodos. Quando declaramos, inicializamos logo. Em vez de declarar agora e mais tarde atribuir um valor inicial: i n t a r a b i c ;... a r a b i c = scanner. n e x t I n t ( ) ; declaramos e inicializamos mais tarde:... i n t a r a b i c = scanner. n e x t I n t ( ) ; Utilizar um único ponto de saída numa rotina. i f ( Date. isleapyear ( year ) ) return 3 6 5 ; return 3 6 6 ; escrevemos: return Date. isleapyear ( year )? 3 6 5 : 3 6 6 ; Em vez de quatro return: Em vez de dois return: public s t a t i c i n t daysinmonth ( i n t year, i n t month ) { switch ( month ) { case 4 : case 6 : case 9 : case 1 1 : return = 3 0 ; case 2 : i f ( Date. isleapyear ( year ) ) return 2 9 ; return 2 8 ; default : return = 3 1 ; declaramos, no início da função, uma variável que representa o seu valor. No fim devolvemos o valor desta variável: public s t a t i c i n t daysinmonth ( i n t year, i n t month ) { i n t r e s u l t ; switch ( month ) { case 4 : case 6 : case 9 : case 1 1 : r e s u l t = 3 0 ; break ; case 2 : r e s u l t = Date. isleapyear ( year )? 2 9 : 2 8 ; break ; default : r e s u l t = 3 1 ; break ; return res ult ; 6
Evitar Se sim, sim; senão, não As expressões são avaliadas antes de serem utilizadas. Em vez de dizer Se a expressão é verdadeira, retorno verdade; se a expressão é falsa, retorno falso : i f ( other. e a r l i e r ( this ) ) return true ; return false ; dizemos retorno (o valor d)a expressão : return other. e a r l i e r ( this ) ; De modo semelhante, em vez de i f ( other. e a r l i e r ( this ) ) b = true ; b = false ; escrevemos b = other. e a r l i e r ( this ) ; Evitar exp == true. i f ( i s V a l i d ( ) = = true ) { A expressão exp == true: tem exactamente o mesmo valor de verdade do que exp: i f ( i s V a l i d ( ) ) { E se for exp == false? e exp!= true? Evitar return (exp). A palavra return é reservada da linguagem, não se trata do nome de uma função. Os parêntesis mais exteriores dificultam a leitura. Em vez de return ( other. e a r l i e r ( this ) ) ; escrevemos return other. e a r l i e r ( this ) ; Não esbanjar variáveis. A excessiva declaração de variáveis dificulta a leitura do código. Se a variável for utilizada apenas uma vez, substitutimos essa utilização pela expressão de inicialização. Em vez de: i n t a r a b i c = scanner. n e x t I n t ( ) ; RomanNumber roman = new RomanNumber( a rab ic ) ; escrevemos RomanNumber roman = new RomanNumber( scanner. n e x t I n t ( ) ) ; 7
Não utilizar importações anónimas. import java. u t i l. ; Em vez de seleccionamos as classes (ou interfaces) que queremos importar: import java. u t i l. Comparable ; import java. U t i l. Map ; Deste modo ajudamos o leitor dizendo exactamente quais os tipos em que estamos interessados. References [1] A. Vermeulen, S. Ambler, et al. The elements of Java style. Cambridge University Press, 2000. ISBN 0 521 77768 2. 8