Uma Quest˜ao de Estilo
Elementos de Estilo Java
Vasco. T. Vasconcelos
9 Setembro 2004
Escrevemos programas para serem lidos por humanos. Pelos outros, mas tamb´em por n´os, daqui a uma semana. As linguagens de programac¸˜ao n˜ao vˆem equipadas com regras de estilo. De facto, nada nos impede de escrever um programa completo em apenas uma linha de c´odigo.
No entanto, a comunidade de programadores de uma dada linguagem desenvolve um certo estilo. Um estilo comum favorece a leitura do nosso c´odigo pelos outros programadores, mas, talvez mais importante, torn´a-lo-´a mais robusto, de mais f´acil manutenc¸˜ao, e tender´a a conter menos bugs.
As regras que abaixo enuncio s˜ao baseadas no livro de bolso “The Elements of Java Style” [1], cuja leitura recomendo a todos os estudantes da linguagem Java. Consulte tamb´em as convenc¸˜oes da Sun emhttp://java.sun.com/docs/codeconv/.
1
Formatac¸˜ao de texto
Reflectir no texto do programa a estrutura l´ogica de blocos do programa. Grande parte do c´odigo Java tem a formaX{bloco}que dever´a ser escrito
X { b l o c o } Exemplos: class UmaClasse { void f ( i n t n ) { i f ( n > 0) { f o r ( i n t i = 0 ; i > n ; i ++) { xxx } } } } i f ( j < 0) { xxx } else i f ( j < 0) { xxx } else { xxx }
Partir linhas demasiado longas. Linhas demasiado longas podem parecer bem no terminal, mas n˜ao 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 ) ;
r e t u r n year < o t h e r . year | |
year = = o t h e r . year & & month < o t h e r . month | |
( year = = o t h e r . year & & month = = o t h e r . month && day < o t h e r . day ) ;
Incluir espac¸os. Utilizar um espac¸o para separar um parˆentesis ‘)’ ou chaveta direita ‘}’ de uma palavra reservada; e uma palavra reservada de um parˆentesis ‘(’ ou chaveta esquerda ‘{’. Exemplos: while . ( ) .{ xxx } i f . ( ) .{ xxx } else . i f . ( ) .{ xxx } else .{ xxx }
Utilizar um espac¸o antes e depois de qualquer operador bin´ario, excepto o ponto ‘.’.
r e t u r n ( daysSince ( FIRST DATE ) + 5 ) % 7 ; r e t u r n Date . isLeapYear ( year ) ? 3 6 5 : 3 6 6 ;
Utilizar uma linha em branco para separar cada membro de uma classe.
p u b l i c class Date { /∗∗ ∗ Ano . ∗/ p r i v a t e i n t year ; /∗ ∗
∗ O ano d est a data .
∗/
p u b l i c i n t getMonth ( ) { r e t u r n month ; }
Evitar utilizar caracteres de tabulac¸˜ao. O n´umero de espac¸os que correspondem a um tab varia de ambiente para ambiente. Um texto correctamente alinhado num ambiente de programac¸˜ao pode aparecer completamente desalinhado noutro.
2
Identificadores
Utilizar nomes sugestivos. Em vez de
utilizamos
year = FIRST YEAR ;
com a declarac¸˜ao
/∗∗
∗ 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˜ao comer vogais. Qual dos dois ´e mais f´acil de ler?
boolean i s L p Y r ( ) { . . . }
boolean isLeapYear ( ) { . . . }
Nomes de classes ou interfaces comec¸am por mai ´uscula.
p u b l i c class P r i n t S t r e a m extends F i l t e r O u t p u t S t r e a m {
xxx
}
Utilizar substantivos para nomes de classes. Classes definem objectos que devem ser identificados por substantivos.
p u b l i c class Date {
xxx
}
Utilizar substantivos ou adjectivos para nomes de interfaces. Utilizar substantivos para nomear interfaces que descrevem servic¸os:
p u b l i c i n t e r f a c e A c t i o n L i s t e n e r {
xxx
}
e adjectivos para descrever competˆencias:
p u b l i c i n t e r f a c e Runnable {
xxx
}
Nomes de m´etodos comec¸am por letra minuscula.
p u b l i c void advanceToWeekDay ( i n t weekDay ) {
xxx
}
Nomes de vari´aveis, de argumentos e de atributos comec¸am por letra min ´uscula.
p r i v a t e i n t year ;
p u b l i c 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 ;
xxx
Todas as vari´aveis (verresultacima) tˆem o prop´osito de nos auxiliar em c´alculos mais complexos. Chamar aux a uma vari´avel n˜ao ajuda a compreens˜ao do c´odigo. Todas as vari´aveis s˜ao tempor´arias: vivem at´e ao fim do bloco onde foram declaradas. Chamartempa uma vari´avel n˜ao nos ajuda em nada.
Fazer coincidir o nome de um parˆametro com o nome atributo correspondente.
p u b l i c Date ( i n t year , i n t month , i n t day ) {
t h i s . year = year ; t h i s . month = month ; t h i s . day = day ; } p u b l i c void s e t Y e a r ( i n t year ) { t h i s . year = year ; }
Nomes de constantes escrevem-se apenas com mai ´usculas; as v´arias palavras s˜ao separadas por underscore.
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 ;
3
Documentac¸˜ao
• Escrever documentac¸˜ao para quem utiliza o c´odigo; • Escrever documentac¸˜ao para quem mant´em o c´odigo; • Manter o c´odigo e a documentac¸˜ao em sincronia; • Utilizar a voz activa;
• Ser sucinto.
Utilizar cabec¸alhos para descrever classes, interfaces e seus membros.
/∗∗
∗ Datas do c a l e n d ´a r i o Gregoriano ,
∗ r e p r e s e n t a d a s por t r ˆe s i n t e i r o s : ano , mˆes e d i a .
∗/
p u b l i c c l a s s Date {
/∗ ∗
∗ C o n s t r u i r uma data i g u a l `a p r i m e i r a data v ´a l i d a .
∗/ p u b l i c Date ( ) { xxx } xxx }
Utilizar coment´arios longos para esconder partes de c´odigo, sem o apagar. Porque os coment´arios n˜ao podem ser anichados, coment´arios longos devem ser utilizados ape-nas para esconder c´odigo temporariamente.
/∗
Escondi e s t e c ´o d i g o temporariamente . i f ( day < daysThisMonth ( ) )
day ++;
∗/
Utilizar coment´arios de linha para explicar detalhes de implementac¸˜ao.
p u b l i c 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
Programac¸˜ao
Definir m´etodos pequenos. Se um m´etodo tiver mais de 10 linhas, provavelmente cont´em em si mais do que uma rotina. Descubra quais e declare-as separadamente. Em vez de:
p u b l i c void add ( i n t numberOfDays ) {
f o r ( i n t i = 0 ; i <numberOfDays ; i ++) i f ( day < daysThisMonth ( ) ) day ++; else { day = 1 ; i f ( month <1 2 ) month ++; else { month = 1 ; year ++; } } }
escrevemos uma rotina que permite avanc¸ar um dia:
p u b l i c void f o r t h ( ) { i f ( day < daysThisMonth ( ) ) day ++; else { / / U l t i m o d i a do mˆes´ day = 1 ; i f ( month < 1 2 )
month + + ; / / U l t i m o d i a dum mˆes que n ˜ao Dezembro´ e l s e { / / 3 1 de Dezembro month = 1 ; year ++; } } }
e outra que permite avanc¸ar um dado n´umero de dias
p u b l i c void add ( i n t numberOfDays ) {
f o r ( i n t i = 0 ; i <numberOfDays ; i ++) f o r t h ( ) ;
N˜ao declarar sem inicializar. Evitamos declarar de vari´aveis sem as inicializar. Para isso declaramos as vari´aveis onde precisamos delas, e n˜ao no in´ıcio dos m´etodos. 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 ´unico ponto de sa´ıda numa rotina. Em vez de doisreturn:
i f ( Date . isLeapYear ( year ) ) r e t u r n 3 6 5 ;
else
r e t u r n 3 6 6 ;
escrevemos:
r e t u r n Date . isLeapYear ( year ) ? 3 6 5 : 3 6 6 ;
Em vez de quatroreturn:
p u b l i c 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 : r e t u r n = 3 0 ;
case 2 :
i f ( Date . isLeapYear ( year ) ) r e t u r n 2 9 ; else r e t u r n 2 8 ; d e f a u l t : r e t u r n = 3 1 ; } }
declaramos, no in´ıcio da func¸˜ao, uma vari´avel que representa o seu valor. No fim devolvemos o valor desta vari´avel:
p u b l i c 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 ; d e f a u l t : r e s u l t = 3 1 ; break ; } r e t u r n r e s u l t ; }
Evitar “Se sim, sim; sen˜ao, n˜ao” As express˜oes s˜ao avaliadas antes de serem uti-lizadas. Em vez de dizer “Se a express˜ao ´e verdadeira, retorno verdade; se a express˜ao ´e falsa, retorno falso”:
i f ( o t h e r . e a r l i e r ( t h i s ) ) r e t u r n t r u e ;
else
r e t u r n f a l s e ;
dizemos “retorno (o valor d)a express˜ao”:
r e t u r n o t h e r . e a r l i e r ( t h i s ) ;
De modo semelhante, em vez de
i f ( o t h e r . e a r l i e r ( t h i s ) ) b = t r u e ; else b = f a l s e ; escrevemos b = o t h e r . e a r l i e r ( t h i s ) ;
Evitarexp == true. A express˜aoexp == true:
i f ( i s V a l i d ( ) = = t r u e ) {
xxx
}
tem exactamente o mesmo valor de verdade do queexp:
i f ( i s V a l i d ( ) ) {
xxx
}
E se forexp == false? eexp != true?
Evitarreturn (exp). A palavrareturn´e reservada da linguagem, n˜ao se trata do nome de uma func¸˜ao. Os parˆentesis mais exteriores dificultam a leitura. Em vez de
r e t u r n ( o t h e r . e a r l i e r ( t h i s ) ) ;
escrevemos
r e t u r n o t h e r . e a r l i e r ( t h i s ) ;
N˜ao esbanjar vari´aveis. A excessiva declarac¸˜ao de vari´aveis dificulta a leitura do c´odigo. Se a vari´avel for utilizada apenas uma vez, substitutimos essa utilizac¸˜ao pela express˜ao de inicializac¸˜ao. 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 r a b i c ) ;
escrevemos
N˜ao utilizar importac¸˜oes an´onimas. Em vez de
import j a v a . u t i l .∗;
seleccionamos as classes (ou interfaces) que queremos importar:
import j a v a . u t i l . Comparable ; import j a v a . 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.