Codigo Limpo - Completo PT.pdf

wellintondepauloRoma 94 views 136 slides Jul 04, 2023
Slide 1
Slide 1 of 136
Slide 1
1
Slide 2
2
Slide 3
3
Slide 4
4
Slide 5
5
Slide 6
6
Slide 7
7
Slide 8
8
Slide 9
9
Slide 10
10
Slide 11
11
Slide 12
12
Slide 13
13
Slide 14
14
Slide 15
15
Slide 16
16
Slide 17
17
Slide 18
18
Slide 19
19
Slide 20
20
Slide 21
21
Slide 22
22
Slide 23
23
Slide 24
24
Slide 25
25
Slide 26
26
Slide 27
27
Slide 28
28
Slide 29
29
Slide 30
30
Slide 31
31
Slide 32
32
Slide 33
33
Slide 34
34
Slide 35
35
Slide 36
36
Slide 37
37
Slide 38
38
Slide 39
39
Slide 40
40
Slide 41
41
Slide 42
42
Slide 43
43
Slide 44
44
Slide 45
45
Slide 46
46
Slide 47
47
Slide 48
48
Slide 49
49
Slide 50
50
Slide 51
51
Slide 52
52
Slide 53
53
Slide 54
54
Slide 55
55
Slide 56
56
Slide 57
57
Slide 58
58
Slide 59
59
Slide 60
60
Slide 61
61
Slide 62
62
Slide 63
63
Slide 64
64
Slide 65
65
Slide 66
66
Slide 67
67
Slide 68
68
Slide 69
69
Slide 70
70
Slide 71
71
Slide 72
72
Slide 73
73
Slide 74
74
Slide 75
75
Slide 76
76
Slide 77
77
Slide 78
78
Slide 79
79
Slide 80
80
Slide 81
81
Slide 82
82
Slide 83
83
Slide 84
84
Slide 85
85
Slide 86
86
Slide 87
87
Slide 88
88
Slide 89
89
Slide 90
90
Slide 91
91
Slide 92
92
Slide 93
93
Slide 94
94
Slide 95
95
Slide 96
96
Slide 97
97
Slide 98
98
Slide 99
99
Slide 100
100
Slide 101
101
Slide 102
102
Slide 103
103
Slide 104
104
Slide 105
105
Slide 106
106
Slide 107
107
Slide 108
108
Slide 109
109
Slide 110
110
Slide 111
111
Slide 112
112
Slide 113
113
Slide 114
114
Slide 115
115
Slide 116
116
Slide 117
117
Slide 118
118
Slide 119
119
Slide 120
120
Slide 121
121
Slide 122
122
Slide 123
123
Slide 124
124
Slide 125
125
Slide 126
126
Slide 127
127
Slide 128
128
Slide 129
129
Slide 130
130
Slide 131
131
Slide 132
132
Slide 133
133
Slide 134
134
Slide 135
135
Slide 136
136

About This Presentation

livro sobre programção, excelente escolha para quem está iniciando na area de programação de computadores


Slide Content

Série de Robert C. Martin =

Codigo Limpo

Habilidades Praticas do Agile Software

RR

Prefacio

Um de nossos doces favoritos aqui na Dinamarca € o Ga-Jol, cujos fortes vapores de licorice
so um complemento perfeito para nosso clima úmido c, geralmente, fo. Parte do charme do
Ga-Jol, para nós dinamarqueses, sdo os dizeres sábios impressos em cada caixa. Comprei dois
pacotes dessa iguaria essa manhi e nela velo este amigo ditado dinamarqués:

Ærlighed i sma ting er ikke nogen lille ting.

“Honestidade em pequenas coisas nâo € uma coisa pequena”. Era um bom prességio para
com o que eu já desejava dizer aqui. Pequenas coisas sio importantes. Este € um livro sobre
preocupaçües modestas cujos valores estio longe de ser pequenos.

Deus está nos detalhes, disse o arquiteto Ludwig Mies van der Rohe. Essa citagdo retoma,
argumentos com contemporáneos sobre o papel da arquitetura no desenvolvimento de software,
especialmente no mundo Agile. Bob e eu, as vezes, acabávamos engajados entusiasmadamente
debatendo sobre este assunto. E sim, Mies van der Rohe atentava para os utlitärios € as formas
¡memorias de construgáo que fundamentam uma ótima arguitetura. Por outro lado, ele também
selecionava pessoalmente cada maganeta para cada casa que ele projetava, Por qué? Por que
pequenas coisas sio importantes.

‘Em nosso “debate” sobre Desenvolvimento dirigido a testes (TDD, sigla em inglés), Bob
€ eu descobrimos que concordamos que a arquitetura do software possuir um lugar importante
no desenvolvimento, embora provavelmente tenhamos perspectivas diferentes do significado
exato disso. Essas diferenças so relativamente irelevantes contudo, pois podemos admitir que
profissionais responsáveis dedicam algum tempo para pensar e planejar o inicio de um projeto. As
nogdes de desenvolvimento dirigido apenas por testes e por códigos do final da década de 1990
Já no existem mais. Mesmo assim, a atençäo aos detalhes € um fundamento de profissionalismo
ainda mais crítico do que qualquer visio maior. Primeiro, € por meio da prática em pequenos.
trabalhos que profissionais adquirem proficiéncia e confianga para se aventurar nos maiores.
Segundo, a menor parte de uma construgáo desleixada, a porta que nao fecha dircito ou o azulejo.
levemente torio do chäo, ou mesmo uma mesa desarrumada, retiram completamente o charme
do todo. E sobre isso que se trata 0 código limpo,

‘Ainda assim, a arquitetura é apenas uma metáfora para o desenvolvimento de software,
especialmente para a parte que entrega o produto inicial no mesmo sentido que um arquiteto
entrega uma construçäo imaculada. Nessa época do Scrum e do Agile, o foco está em colocar 0

produto rapidamente no mercado, Desejamos que a indústria funcione em velocidade máxima
na produgäo de software. Essas fábricas humanas: programadores que pensam e sentem que
trabalham a partir das pendéncias de um produto ou do user story para criar o produto, À
metáfora da fabricagäo está mais forte do que nunca no pensamento. Os aspectos da producto da
manufatura japonesa de automóveis, de um mundo voltado para a linha de montage, inspiraram
‘grande parte do Scrum.

‘Ainda assim, mesmo na indústria automobilistica, a maior parte do trabalho no está na
fabricagdo, mas na manutençlo — ou na prevençäo. Em software, 80% ou mais do que fazemos
€ chamado de “manutençäo”: o ato de reparar. Em vez de abracar o típico foco ocidental
sobre a produçäo de bons softwares, deveriamos pensar mais como um pedreiro que conserta
‘casas na indústria de construçäo, ou um mecánico de automóveis na área automotiva. O que 0
gerenciamento japonés tem a dizer sobre isso?

Por volta de 1951, uma abordagem qualitativa chamada Manutençäo Produtiva Total (TPM)
surgiu no cenärio japonés. Seu foco era na manutengdo em vez da produçäo. Um dos maiores
fundamentos da TPM éo conjunto dos chamados SS principios. 5S é uma série de disciplinas - uso
‘aqui o termo “disciplina” para fins educativos. Os 5S principios, na verdade, s3o os fundamentos
do Lean - outro jargdo no cenärio ocidental, e cada vez mais conhecida no mundo dos softwares.
Esses princípios näo so uma opgdo. Assim como Uncle Bob diz em suas preliminares, a prática
de um bom software requer tal disciplina: foco, presenga de espirito e pensamento. Nem sempre
sobre fazer, sobre pressionar os equipamentos da fábrica para produzir em velocidade máxima.
A filosofia dos 5S inclui os seguintes conceitos:

Sciri, ou organizagdo (pense em “ordenar”). Saber onde estáo as coisas — usar abordagens
‘como nomes adequados - é crucial. Acha que dar nome a identificadores ndo € importante? Leia
próximos capítulos

Sciton, ou arrumaçäo (pense em “sistematizar”). Há um antigo ditado americano que diz:
“Um lugar para tudo, e tudo em seu lugar”. Um pedago de código deve estar onde vocé espera
encontrá-lo — caso ndo esteja, refatore e o coloque lá.

‘Seiso, ou limpeza (pensem em “polir”): manter o local de trabalho livre de fos pendurados,
gordura, migalhas e lixo. O que os autores falam aqui sobre encher seu código com comentários
€ linhas de códigos como comentários que informa o passado ou os desejos para o futuro? Livre-
se deles.

‘Seiketsu, ou padronizagdo: a equipe concorda em manter o local de trabalho limpo.

Vocé acha que este livro fala algo sobre ter um estilo de programagdo consistente e uma série
de práticas dentro da equipe? De onde vém tais padrôes? Continue a leitura.

‘Shutsuke, ou disciplina (sutodisciplina). Iso significa ter disciplina para seguir as práticas e
refletir frequentemente isso no trabalho e estar disposto a mudar.

Se aceitar o desaño - isso, o desafío — de ler e aplicar o que € aconselhado neste livro,
vocé entenderá e apreciará o último item. Aqui estamos finalmente indo em direçäo as raizes
do profissionalismo responsävel numa profissio que deve se preocupar com o ciclo de vida de
um produto. Conforme façamos a manutençäo de automóveis e outras máquinas na TPM, a
manutençäo corretiva ~ esperar que bugs aparepam — é a excegäo. Em vez disso, subimos um
nivel: inspecionamos as máquinas todos os dias e consertamos as partes desgastadas antes de
quebrarem, ou percorremos o equivalente aos famosos 16km antes da primeira troca de óleo para
testar o desgaste. No código, a refatoraçäo € impiedosa. Vocé ainda pode melhorar um nivel a
mais com o advento do movimento da TPM hä 50 anos; construa máquinas que sejam passiveis
de manutengdo. Tornar seu código legivel é tdo importante quanto torná-lo exceutävel. A última
prática, adicionada à TPM em torno de 1960, é focar na incluso de máquinas inteiramente novas
ou substituir as antigas. Como nos adverte Fred Brooks, provavelmente devemos refazer partes

principais do software a partir do zero a cada sete anos ou entäo se livrar dos entulhos. Talvez
devéssemos atualizar a constante de tempo de Fred para semanas, dias ou horas, em vez de anos.

‘onde ficam os detalhes.

Há um grande poder nos detalhes, mesmo assim existe algo simples e profundo sobre essa
abordagem para a vida, como talvez esperemos de qualquer abordagem que afirme ter origem
japonesa. Mas essa no € apenas uma visio ocidental de mundo sobre a vida; a sabedoria de
ingleses e americanos também está cheia dessas adverténcias. A citaçäo de Seiton mais acima.
veio da ponta da caneta de um ministro em Ohio que visualizou literalmente a organizado
“como um remédio para todos os niveis de mal”.

E Seiso? Deus ama a limpeza. Por mais bonita que uma casa seja, uma mesa desarrumada
retira seu esplendor. E Shutsuke nessas pequenas questdes? Quem é fiel no pouco também &
‘no muito. E que tal ficar ansioso para refatorar na hora certa, fortalecendo sua posicdo para
as “grandes” decisdes subsequentes, em vez de descarti-las? Um homem prevenido vale por
dois. Deus ajuda a quem cedo madruga, Näo deixe para amanhá o que se pode fazer hoje. (Esse
era 0 sentido original da frase “o último momento de responsabilidade", em Lean, até car nas
mdos dos consultores de software). E que tal aplicar o local de esforgos pequenos e individuais
num grande todo? De gräo em gräo a galinha enche o papo. Ou que tal integrar um trabalho de
prevençäo no dia a dia? Antes prevenir do que remediar. O código limpo honra as profundas
raizes do conhecimento sob nossa cultura mais ampla, ou como ela fora um dia, ou deve ser, e
poderd vir a ser com a atençäo correta aos detalhes.

Mesmo na grande literatura na área de arquitetura encontramos visdes que remetem a esses
supostos detalhes. Pense nas maçanetas de Mies van der Rohe. Aquilo € seiri. É ficar atento a
‘cada nome de varidvel. Deve-se escolher o nome de uma variävel com cuidado, como se fosse
‘eu primeiro flho.

‘Como todo proprietério de uma casa sabe, tal cuidado e constante refinamento jamais
acaba.

O arquiteto Christopher Alexander - pai dos padröes e das linguagens de padrdes ~ enxerga +
cada o próprio design como um conserto pequeno e local. E ele enxerga a habilidade de uma boa
estrutura como 0 único objetivo do arquiteto; pode-se deixar as formas maiores para os padres
€ seus aplicativos para os moradores. O design € constante nio só ao adicionarmos um novo.
cómodo a uma casa, mas ao nos atentarmos a repintura, a substituiçäo de carpetes gastos ou a
melhoria da pia da cozinha. A maioria das artes reflete relagdes análogas. Em nossa busca por
outras pessons que dizem que a casa de Deus fi feta nos mínimos detalhes, encontramo-nos em.
boa companhia do autor francés Gustav Flaubert, do século XIX. O poeta francés Paul Valery
nos informa que um poema nunca fica pronto e requer trabalho continuo, e parar de trabalhar
ele seria abandoné-lo,

Tal preocupaçäo com os detalhes é comum a todos os encargos de exceléncia. Portanto,
talvez haja pouca coisa nova aqui, mas ao ler este livro vocé será desafiado a retomar a disciplina.
{que vocé há muito largou para a apatia ou um desejo pela espontaneidade e apenas “respondia
As mudangas”.

Infelizmente, nño costumamos enxergar essas questdes como pegas fundamentais da arte
de programar. Abandonamos nosso código antecipadamente, ndo porque cle já esteja pronto,
mas porque nosso sistema de valores se foca mais na aparéncia externa do que no contetido que
entregamos.

No final, essa falta de atengo nos custa: Dinheiro ruim sempre reaparece. Pesquisas, nem no
‘mercado e nem nas universidades, sño humildes o bastante para se rebaixar e manter o código
limpo. Na época em que trabalbei na empresa Bell Labs Software Production Research (produç20,
de fato!), a0 ficarmos mexendo aqui e ali, nos deparamos com descobertas que sugeriam que ©

estilo consistente de endentaçäo era um dos indicadores mais significantes estatisticamente da
idéncia de bugs.

‘Queriamos que a qualidade fosse produzida por essa ou aquela estrutura ou linguagem de
programaco ou outra nogäo de alto nivel; conforme as pessoas cujo suposto profisionalismo
se dá ao dominio de ferramentas e métodos de design grandioso, sentimo-nos ofendidos pelo
valor que aquelas máquinas de fábricas, os codificadores, recebem devido a simples aplicagio
consistente em um estilo de endentagdo, Para citar meu proprio livro de 17 anos atrás, tal estilo
faz a distinçäo entre exceléncia e mera competéncia. A visto de mundo japonesa entende o valor
crucial do trabalhador dirio e, alé do mais, dos Sistemas de desenvolvimento voltados para
as açües simples e dirias dessestrabalhadores. A qualidade é 0 resultado de um milhäo de tos
altruistas de importar-se ~ ndo apenas um grande método qualquer que desça dos céus. No €
porque esses atos sio simples que eles sejam simplistas, e muito menos que sejam fäceis. Eles
io, no obstante, a fbrica de magnitude e, também, de beleza em qualquer esforgo humano,
Ignori-los 6 nto ser ainda completamente humano.

E claro que ainda defendo o conceito de um escopo mais amplo, e, especialmente, o do
valor de abordagens arquitetónicas arraigadas profundamente no conhecimento do dominio e de
usabilidade do sofware.

Este livro no é sobre isso - ou, pelo menos, nio de modo direto, Mas cle passa uma
mensagem mais sutil cuja esséncia ndo deve ser subestimada. Ele se encaixa à máxima atual
das pessoas que realmente se preocupam com o código, como Peter Sommerlad, Kevlin Henney
+ Giovanni. “O código € o projeto” e “Código simples” sño seus mantras. Enquanto devamos
tentar nos lembrar de que a interface € o programa. e que suas estruturas dizem bastante sobre a
estrutur de nosso programa, é crucial adotar a humilde postura de que projeto vive no código.
E enquanto o retrabalho na metáfora da manufarura leva ao custo, o no projeto leva ao valor.
Devemos ver nosso código como a bela articulagáo dos nobres esforços do projeto ~ projeto
como um processo, e no uma meta esátic. É no código que ocorrem as medidas estuturais
fe acoplamento e coesdo. Se vocé vir a deserigäo de Larry Constantine sobre eses dois fatores,
le os conceitua em termos de código — e ndo em conceitos de alto nivel como se pode encontrar
‘em UML, Richard Gabriel nos informa em seu artigo Abstraction Descant que a abstragdo €
maligna. O código € antimaligno, e talvez o código limpo seja divino,

Voltando à minha pequena embalagem de Ga-Jol, acho importante notar que a sabedoria
dinamarquesa nos aconselha a náo só prestar atengdo a pequenas coisas, mas também a ser
honesto em pequenas coisas. Isso significa ser honesto com o código e tanto com nossos colegas
¢,acima de tudo, com nós mesmos sobre o estado de nosso código. Fizemos o Melhor para “deixar
© local mais limpo do que como o encontramos”? Refatoramos nosso código antes de veriici-
10? Essas ndo slo preocupagdes externas, mas preocupagdes que estio no centro dos valores
do Agile. Que a refatoragdo seja pate do conceito de “Pronto”, & uma prática recomendada no
Serum. Nem a arquitetura e nem o código limpo exigem perfeigdo, apenas honestidade e que
fagamos o melhor de nés. Errar é humano; perdoar é divino. No Serum, tomamos as coisas
visivis. Arejamos nossa roupa suja. Somos honestos sobre o estado de nosso código porque
+ código nunca € perfeito. Tomamo-nos mais completamente humanos, mais merecedores do
divino e mais próximos da magnitude dos detalhes.

Em nossa profissio, precisamos desesperadamente de toda ajuda que conseguirmos. Seo piso
de uma loja reduz os acidentes e suas ferramentas bem organizadas aumentam a produtividade,
ent sou totalmente a favor. E relaçäo a este iro, ele € a melhor aplicaçäo pragmática dos
principios de Lean ao software que já vi. Eu ndo esperava nada mais deste pequeno grupo prätico
de individuos que se esforgaram juntos por anos nâo só para se aperfeigoarem, mas também
‘ara presentear com seus conhecimentos o mercado com obras como esta em suas mos agora

Isso deixa o mundo um pouco melhor do que quando o encontrei antes de Uncle Bob me enviar
‘6 manuscrito. Após ter finalizado estes exercicios com conhecimentos to sublimes, agora vou

limpar minha mesa.

James O. Coplien
Mordrup, Dinamarca.

Introdugáo

he omy VAcid meagugenent
OF Code Quaciry: WTFs/nivure

fn,
wre 8
a3,

wrr

Good code. Bad code

Reproduzido com a gentil autorizagño de Thom Holwerda.
up /Awww.osnews.comvstory/19266/WTEs_m
(©) 2008 Focus Shift

Que porta representa seu código? Que porta representa sua equipe ou sua companhia?
Por que estamos naquela sala? É apenas uma revisäo normal de código ou encontramos uma série
‘de problemas terriveis logo após iniciarmos a apresentaçäo? Estamos depurando em pánico, lendo
meticulosamente um código que pensávamos que funcionava? Os clientes estáo indo embora aos
bandos e estamos com os gerentes em nossos pescogos? Como podemos garantir que cheguemos
atrás da porta certa quando o camino fica dificil? A resposta €: habilidade profissional.

"Há duas vertentes para se obter habilidade profissional: conhecimento € trabalho. Vocé deve
adquirir o conbecimento dos principios, padröes, priticas e heuristicas que um profissional

habilidoso sabe, também esmiugar esse conhecimento com seus dedos, olhos e corpo por meio
do trabalho érduo e da prática

Posso Ihe ensinar a mecánica para se andar de bicicleta. Na verdade, a matemática clásica
relativamente direta. Gravidade, atrto, momento angular, centro de massa, e assim por diante,
podem ser demonstrados com menos de uma página cheia de equagdes. Dada essas fórmulas,
eu poderia provar para vocé que € prático andar de bicicleta e Ihe dar todo o conhecimento
necessério para que vocé consiga, E mesmo assim vocé cairá na primeira vez que tentar.

Programar no diferente, Poderimos pr no papel odos os principios neces para um
código limpo e, entño, confiar que vocé fará as tarefas (isto €, deixar vocé cair quando subir na
PR) ps sit ds pls Japos ho ein dente?

‘io. Essa náo € forma que este livro seguirá.

Aprender a criar códigos limpos € uma tarefa árdua e requer mais do que o simples
conhecimento dos principios e padröes. Vocé deve suar a camisa: praticar sozinho e ver que
‘cometeu erros; assistir a outros praticarem e errarem; vé-los tropegar refazer seus passos; Vé-
Jos agonizar para tomar decisöes e o prego que pagario por as terem tomado da maneira errada,

Esteja preparado para trabalhar duro enquanto ler este livro. Esse no € um livro fácil e
simples que vocé pode ler num avido € terminar antes de aterisar. Este livro the fará trabalhar,
+ trabalhar duro. Que tipo de trabalho vocé fará? Vocé lerá códigos aqui, muitos cödig«

E vocé deverá descobrir o que cstä correto e errado nos códigos. Vocé terá de seguir o
raciocinio conforme dividirmos módulos e os unirmos novamente. Iso levará tempo € esforgo,
mas achamos que valerá a pena.

Dividimos este livro em trés partes, Na primeira há diversos capítulos que descrevem os
principios, padröcs € práticas para criar um código limpo. Há um tanto de códigos nessa parte. €
será desañiadorlé-os. Eles Ihe preparardo para a segdo seguinte. Se vocé deixar o livro de lado
apös essa primeira parte. Bem, boa sorte!

Na segunda parte entra o trabalho pesado que consiste em diversos estudos de caso de
complexidade cada vez maior. Cada um é um exercicio para limpar um código — resolver alguns.
problemas dele. O detalhamento nesta seçao é intenso. Vocé terá de ire voltar por entre as folhas.
detextos e códigos, canalisar e entender o código com o qual estamos trabalhando e captar nosso
raciocinio para cada alteragdo que fizermos. Reserve um tempo para essa parte, pois deverá levar
dias.

‘Aterceira parte éa compensaçäo. É um único capítulo com uma lista de heuristicas e “odores”
reunidos durante a criagdo dos estudos de caso. Conforme progredirmos e limparmos os códigos
nos estudos de caso, documentaremos cada motivo para nossas agdes como uma heuristica ou um
“odor”. Tentaremos entender nossas próprias reaçôes em relaçäo ao código quando estivermos
Jendo ou alterando-o, e trabalharemos duro para captar por que nos sentimos de tal forma e por
que fizemos isso ou aqui. O resultado será um conhecimento base que desereve a forma como
pensamos quando criamos, lemos e limpamos um código.

Este conhecimento base possui um valor limitado se vocé nao ler com atençäo ao longo dos
casos de estudo na segunda parte deste livro. Neles, anotamos cuidadosamente cada alteracdo
que fizemos com referéncias posteriores ás heuristicas. Tas referéncias aparecem em colchetes,
assim: [H22]. Isso Ihe permite ver o contexto no qual sto aplicadas e escritas aquelas heuistcas!
Essas, em si, näo slo táo valiosas, mas, sim, a relaçäo entre elas e as diferentes decisöes que
tomamos a0 limpar o código nos casos de estudo.

A fim de the ajudar ainda mais com essasrelagdes, colocamos no final do livro uma referencia
cruzada que mostra o número da página para cada referéncia. Vocé pode usi-la com um guia
rápido de consulta para saber onde uma determinada heurística foi aplicada.

Mesmo se vocé ler a primeira € a terceira segdes e pular para os casos de estudo, ainda

assim ter lido um livro que satisfaz sobre a criaçäo de um bom software. Mas se ndo liver
pressa e explorar os casos de estudo, seguindo cada pequeno passo € cada minuto de decisäo, se
colocando em nosso lugar e se forgando a seguir a mesma linha de raciocinio que usamos, ento
vocé adquiriu um entendimento muito mais rico de todos aqueles principios, padrdes, práticas
+ heuristicas, Todo esse conhecimento ficará em cada fibra de seu corpo. Ele se tornará parte de
vocé da mesma forma que ao aprender a andar de bicicleta, ela se torna uma parte de sua vontade
de pedalá-la

Agradecimentos

Tlustragdes
Meus agradeeimentos a minhas dus aristas, Jenifer Kohnke © Angela Brooks, Jennifer € a
Fexponeavel pels maravillosas € eitvas figuras no inicio de cada capitulo < também pelos
fetus de Kent Beck, Ward Cunningham, Bjame Stroustrup, Ron levies, Grady Booch, Dave
Thomas, Michael Feathers e de mim mesmo.

An esponsivel pelas gu engenhosas qu enfeitam os capítulos Ao longo dos anos,
cla ori batanes imagens para mim, incuindo mus ds que eso no lv Agile Software
Develpmen Principles Pates, and Practices, Angela amber € minha primogénita ede quem

Sobre a capa

A imagem da capa se é a MIOS — a Galáxia Sombrero -, que fica na constelagäo de Virgem
+ está a pouco menos de 30 milhdes de anos-luz de nós. Em seu centro há um buraco negro
Supermassivo cujo peso equivale um bilhäo de vezes a massa do sol

A imagem Ihe faz lembrar da explosäo da lua Praxis, de Klingon? Eu me recordo vividamente
a cena de Jornada nas Estrelas VI que mostrava um anel equatorial de destrogos voando devido à
explosäo. Desde essa cena, o anel equatorial se tornou um componente comum as explosdes em
filmes de ficçäo científica. Ele até foi adicionado à explosäo de Alderaan nas últimas versöes do
filme Jomada nas Estrela.

‘© que causou a formaçäo desse anel em tomo de M104? Por que ele possui um bojo to
amplo e um núcleo tio minúsculo ¢ brilhoso? Parece-me como se o buraco negro central perde
sua graga e langou um buraco de 30.000 anos-luz no meio da galáxia, devastando quaisquer
civilizagdes que estivessem no caminho daquele distürbio cósmico,

Buracos negros supermassivos engolem estrelas inteiras no_almogo, convertendo uma
consideravel frago de sua massa cm energia. E = MC2 já.

é bastante poténcia, mas quando M € uma massa estelar:

Cuidado! Quantas estrelas caíram impetuosamente
naquelas presas antes de o monstro ficar saciado?

© tamanho do vio central poderia ser uma dica?

‘A imagem de M104 da capa € uma combinaçäo da
famosa fotografia de luz visivel do Hubble (foto superior)
com a recente imagem infravermelha do observatório
espacial Spitzer (foto inferior). É essa segunda imagem
que nos mostra claramente a natureza do anel da galáxia
Na luz visivel, vemos apenas a extremidade frontal do anel
como uma silhueta. O bojo central ofusca o resto do anel.

Mas no infravermelho, as partículas “quentes” — isto
6, altamente radioativas ~ no anel brilham através do bojo
central. A combinagáo de ambas as imagens nos mostra
uma visio que no haviamos visto antes e indica que, há
‘muito tempo atrás, lá havia um inferno enfurecido de atividades.

Cédigo Limpo

2 Capitulo 1: Código Limpo

Este livro fala sobre programago e está repleto de códigos que examinaremos a partir
de diferentes perspectivas: de baixo para cima, de cima para baixo e de dentro para fora. Ao
terminarmos,teremos um amplo conhecimento sobre códigos e seremos capazes de distinguir
entre um código bom € um &

tomar um ruim em um bom.

O Código

Podem dizer que um livro sobre códigos é, de certa forma, algo ultrapassado, que a
programagio deixou de ser uma preocupaçäo e que devemos nos preocupar com modelos
+ requisitos. Outros até mesmo alegam que o fim do código, ou seja, da programado, está
próximo; que logo todo código será gerado, e ndo mais escrito. E que ndo precisardo mais de
programadores. pois as pessoas eriaräo programas a partir de especificagdes.

Bobagens! Nunca nos livraremos dos códigos, pois eles representam os detalhes dos
requisitos. Em certo nivel, nio há como ignorar ou abstrair esses detalhes; eles precisam
ser especificados. E especificar requisitos detalhadamente de modo que uma mägui
executá-los é programar — «tal especificaçäo é o código.

Espero que o nivel de de nossas linguagens continue a aumentar e que o número de
linguagens específicas a um dominio continue crescendo. Isso será bom, mas náo acabará com
aprogramagdo, De fato, todas as especificagdes escritas nessas linguagens de niveis mais altos
+ específicas a um dominio sero códigos! Eles precisardo ser minuciosos, exatos e bastante
formais e detalhados para que uma máquina possa entendé-los e cxccutá-los

As pessoas que pensam que o código um dia desaparecerá sio como matemáticos que
esperam algum dia descobrir uma matemática que ndo precise ser formal. Elas esperam que
um dia descubramos uma forma de criar máquinas que possam fazer o que desejamos em vez
do que mandamos. Tais máquinas teräo de ser capazes de nos entender lo bem de modo que

possam traduzir exigéncias vagamente especificadas em programas executáveis perfeitos para
satisfazer nossas necessidades.

Isso jamais acontecerá. Nem mesmo os seres humanos, com toda sua intuiçäo ecriatividade,
tém sido capazes de criar sistemas bem-sucedidos a partir das caréncias confusas de seus
clientes. Na verdade, se a matéria sobre especificaçäo de requisitos ndo nos ensinou nada, €
porque os requisitos bem especificados so to formais quanto os códigos e podem agir como
testes executáveis de tais códigos!

Lembre-se de que o código é a linguagem na qual expressamos nossos requisitos. Podemos
eriar linguagens que sejam mais próximas a eles. Podemos criar ferramentas que nos ajudem
a analisar a sintaxe e unir tais requisitos em estruturas formais. Mas jamais eliminaremos a.
precisäo necessária — portanto, sempre haverá um código.

Código Ruim 3

Código ruim

Recentemente li o prefäcio do livro Implementation
Patterns! de Kent Beck, no qual ele diz“... ste livro
baseia-se numa premissa frágil de que um bom código
import... Uma premissa frágil? No concordo! Acho
que essa premissa € uma das mais robustas, apoiadas
© plenas do que todas as outras em nossa área (e sei
que Kent sabe disso). Estamos cientes de que um bom
‘digo importa, pois tivemos de lidar com a falta dele
por muito tempo.

Lembro que no final da década de 1980 uma empresa
esiow um aplicativo extraordináio que se tomou muito
popular e muitos profissionais o compraram e usaram. 2
Mas, entäo, o intervalo entre os lançamentos das novas”
disrbuigdes comegou a aumentar. Os bugs ndo cram
consertados na distribuieao seguinte. E 0 tempo de
carregamento do aplicativo € o número de travamentos
aumentaram. Lembro-me do dia em que, frustrado,
fechei o programa e nunca mais 0 usci. A empresa añu
do mercado logo depois.

Duas décadas depois encontrei um dos funcionários detal empresa na época € o perguntei o
que havia acontecido, e o que eu temia fora confirmado. Elestiveram de apressar 0 langamento
do produto e, devido a isso, o código ficou uma zona. Entäo, conforme foram adicionando mais
€ mais recursos, o código piorava cada vez mais até que simplesmente no era mais possivel *
gerenciá-lo. Foi o código ruim que acabou com a empresa.

Alguma vez um código ruim j Ihe atrasou consideravelmente? Se voc? for um programador,
independente de sua experiénca, ento já se deparou vrias vezes com esse obstáculo, Ali, €
como se caminhässemos penosamente por um lamagal de arbustos emaranhados com armadilhas
cultas.sso & o que fazemos num código ruim. Pelejamos para encontrar nosso caminho,
esperando avistar alguma dica, alguma indicagáo do que está acontecendo; mas tudo o que
vemos é um código cada vez mais sem sentido.

É claro que um código ruim já Ihe trasou. Mas, ent¥o, por que vocé o escreveu dessa forma?
Estava tentando ser rápido? Estava com pressa? Provavelmente. Talvez vocé pensou que nfo
sse tempo para fazer um bom trabalho; que seu chefe ficaria com raiva se vocé demorasse
um pouco mais para limpar seu código. Talvez vocé estava apenas cansado de trabalhar neste
programa e queria termind-lo logo. Ou verificou a lista de coisas que havia prometido fazer e
percebeu que precisava finalizar este módulo de uma vez, de modo que pudesse passar para ©
próximo. Todos já fizemos isso, já vimos a bagunga que fizemos c, ento, optamos por amımd-
las outro din. Todos já nos sentimos aliviados ao vermos nosso programa confuso funcionar e
decidimos que uma bagunça que funciona € melhor do que nada. Todos nös já dissemos que
revisariamos e limparíamos o código depois, É claro que naquela época náo conhecíamos a lei
de LeBlanc: Nunca é arde.

O Custo de Ter um Cédigo Confuso

Se vocé é programador a mais de dois ou très anos, provavelmente o código confuso de outra
‘pessoa já fez com que vocé trabalhasse mais lentamente e provavelmente seu proprio código já
Ihe trouxe problemas.

O nivel de retardo pode ser significativo. Ao longo de um ou dois anos, as equipes que
trabalharam rapidamente no inicio de um projeto podem perceber mais tarde que estáo indo a
passos de tartaruga. Cada alteragäo feita no código causa uma falha em outras duas ou trös partes.
do mesmo código. Mudanga alguma é trivial. Cada adigäo ou modificagäo ao sistema exige
que restauraçües, amarragdes e remendos sejam “entendidas” de modo que outras possam ser
incluídas. Com o tempo, a bagunga se toma lo grande e profunda que ro dá para arrumá-la.
¡Nao há absolutamente soluçäo alguma.

Conforme a confuso aumenta, a produtividade da equipe diminui, assintoticamente
aproximando-se de zero. Com a redugdo da produtividade, a geréncia faz a única coisa que ela
pode; adiciona mais membros ao projeto na esperanga de aumentar a produtividade, Mas esses
novos membros nao conhecem o projeto do sistema, nao sabem a diferença entre uma mudanga
que altera o propósito do projeto e aquela que o atrapalha. Ademais, eles, e todo o resto da
equipe, estdo sobre tremenda pressio para aumentar a produtividade. Com isso todos eriam mais
e mais confus es, levando a produtividade mais perto ainda de zero (veja a Figura 1.1).

o8 8888

Time

Figura 1.1
Produtividade v. tempo

(© Custo de Ter um Código Confuso 5

O Grande Replanejamento

No final, a equipe se rebela. Todos informam à geréncia que nfo conseguem mais trabalhar neste
irritante código-fonte e exigem um replanejamento do projeto. Apesar de a geréncia nfo querer
gastar recursos em uma nova remodelaçäo, ela näo pode negar que a produtividade está péssima.
No final das contas, ela acaba cedendo ás exigencias dos desenvolvedores € autoriza o grande
replanejamento desejado.

É, entdo, formada uma nova equipe especializada. Por ser um projeto inteiramente novo,
todos querem fazer parte dessa equipe. Eles desejam comegar do zero e criar algo belo de verdade.
Mas apenas os melhores e mais brilhantes säo selecionados e os outros deverdo continuar na
manutençäo do sistema atual.

‘Agora ambos os times esto numa corrida. A nova equipe precisa construir um novo sistema.
que faga o mesmo que o antigo, além de ter de se manter atualizada em relaçäo ás mudangas
feitas constantemente no sistema antigo. Este, a geréncia ndo substituirá até que o novo possa
fazer tudo também.

Essa corrida pode durar um bom tempo. Já vi umas levarem 10 anos. E, quando ela termina,
‘9s membros originais da nova equipe já foram embora há muito tempo, e 08 atuais exigem 0
replanejamento de um novo sistema, pois está tudo uma zona novamente.

Se vocé já vivencion pelo menos um pouco dessa situagdo, entdo sabe que dedicar
tempo para limpar seu código ndo € apenas eficaz em termos de custo, mas uma questäo de
sobrevivencia profissional.

Atitude

‘Vocé já teve de trabalhar penosamente por uma confusdo to grave que levou semanas o que
deveria ter levado horas? Vocé já presenciou o que deveria ser uma alteraçäo única e direta, mas
que em vez disso foi feta em diversos módulos distintos? Todos esses sintomas sdo bastante
comuns

Por que isso ocorre em um código? Por que um código bom se decompüe to rápido em
‘um ruim? Temos diversas explicagdes para isso. Reclamamos que os requisitos mudaram de
tal forma que estragaram o projeto original. Criticamos os prazos por serem curtos demais para
fazermos as coisas certas. Resmungamos sobre gerentes tolos e clientes intolerantes e tipos de
marketing inúteis e técnicos de telefone. Mas o padräo, querido Dilbert”, ndo está cm nossas
estrelas, mas sim em nös mesmos. Somos profissionais.

Isso pode ser algo dificil de engolir. Mas como poderia essa zona ser nossa culpa? E os
requisitos? E o prazo? E os tolos gerentes e tipos de marketing imiteis? Eles ndo carregam
alguma parcela da culpa?

Nao. Os gerentes e marketeiros buscam em nós as informagdes que precisam para fazer promessas
€ firmarem compromissos; e mesmo quando ndo nos procurar, ndo devemos dar uma de tímidos ao
dizer-lhes nossa opinido, Os usuirios esperam que validemos as maneiras pelas quais 0s requisitos se
encaixardo no sistema. Os gerentes esperam que os ajudemos a cumpri o prazo. Nossa cumplicidade
no planejamento do projet € tamanha que compartilhamos de uma grande parcela da responsabilidade
‘em caso de falhas especialmente se estas forem em relaçäo a um código ruim.

6 Capitulo 1: Código Limpo

“Mas, esperel”, vocé diz. “E se eu no fizer o que meu gerente quer, serei demitido™. É
provável que nio.

"A maioria dos gerentes quer a verdade, mesmo que demonstrem o conträio. A maioria deles
quer um código bom, mesmo estourando o prazo. Eles podem proteger com paixto o prazo eos
requisitos, mas essa € a fungio deles. A sua € proteger o código com essa mesma paixdo.

Para finaliza essa questo, e se vocé fosse médico e um paciente exigisse que vocé parasse
com toda aquela lavago das maos na preparagáo para aciungia só porque iso leva muito tempo?”
É dbvio que o chefe neste caso € o paciente; mas, mesmo assim, o médico deverá totalmente se
recusar obedecé-o, Por qué? Porque o médico sabe mais do que o paciente sobre os riscos de
ddoengase infecçôes. Ndo seria profisional (sem mencionar criminoso) que o médico obedecesse
‘ao paciente neste cenário. Da mesma forma que ndo € profissional que programadores cedam à
vontade dos gerentes que no entendem os riscos de se gerar códigos confusos.

O Principal Dilema

Os programadores se deparam com um dilema de valores básicos, Todos os desenvolvedores
‘com alguns anos a mais de experiéncia sabem que bagungas antigas reduzem o rendimento.
Mesmo assim todos eles se sentem pressionados a cometer essas bagungas para cumprir os
prazos. Resumindo, eles náo se esforgam para.

(Os profissionais sérios sabem que a segunda parte do dilema está errada. Vocé ndo cumprirá
o prazo se fizer bagunga no código. De fato, tal desorganizagao reduzirá instantancamente sun
velocidade de trabalho, e vocé perderá o prazo. A zinica maneira de isso ndo acontecer - a única
mancira de ir mais rápido ~ é sempre manter o código limpo.

A Arte do Código Limpo?

Digamos que vocé acredite que um código confuso seja um obstáculo relevante. Digamos que
vocé aceite que u única forma de trabalhar mais rápido € manter seu código limpo. Entlo, vocé
deve se perguntar: “Como escrever um código limpo?” Nao vale de nada tentar esrever um
código limpo se vocé ná souber o que isso significa.

As más notícias so que eserever um código limpo é como pintar um quadro. A maria de
ns sabe quando a figura foi bem ou mal pintada. Mas ser capaz de distinguir uma boa arte de
‘uma ruim no significa que vocé saiba pintar. Assim como saber distinguir um código limpo de
‘um ruim ndo quer dizer que saibamos escrever um código limpo.

screver um código limpo exige o uso disciplinado de uma miriade de pequenas técnicas
aplicadas por meio de uma sensibilidade meticulosamente adquirida sobre *limpeza". A
*sensibilidade ao código" € o segredo. Alguns de nés já nascemos com cla. Outros precisam se
esforgar para adquirila. Ela no só nos permite perceber se o código €bom ou ruim, como também
‘os mostra a estatégia e disciplina de como transformar um código ruim em um limpo.

Um programador sem “sensibilidade ao código” pode visualizar um módulo confuso e
reconhecer a bagunga, mas no saberá o que fazer a respeit del. Já um com essa sensibiidade
olharé um mödulo confuso e verá altemativas. A “sensibilidade ao código” ajudará a esse

AAN II IN

(© Casto de Ter um Código Confuso. 1

programador a escolher a melhor alternativa € o orientarä na criaçäo de uma sequéncia de
comportamentos para proteger as alteragdes feitas aqui e li.

Em suma, um programador que escreve um código limpo & um artista que pode pegar
‘uma tela em branco e submeté-la a uma série de transfommaçües até que se tome um
graciosamente programado.

O que é um Código Limpo?

Provavelmente existem tantas definigdes como existem programadores. Portanto, perguntei a
alguns programadores bem conhecidos e com muita experiéncia o que achavam.

Bjarne Stroustrup, criador do C++
e autor do livro A linguagem de
programagáo C++

Gasto do meu código elegant e en. A lógica
leve sr are para dficultar encobrimento de bugs
‘a dependéncian minimas paro falta a momen.
© tatamento de erm complet de acordo com uma
esmuégla clara € 0 desemponho próximo de mais
ciel de modo and ncaa pessoas otornarem o
bat conso com orimizahs sorters. O cádigo
{imp fa em apenas uma cols

Bjame usa a palavra “elegante” — uma
palavra e tanto! O dicionário possui as seguintes
definigóes: que se caracteriza pela naturalidade
de harmonia, leveza, simplicidade; naturalidade
no modo se dispor: requintado, fino, estiloso.
Observe a énfase dada à palavra “naturalidade”.
Aparentemente, Bjame acha que um código limpo proporciona uma Icitura natural; €
ser belo como ouvir uma música num radio ou visualizar um carro de design magnifico.

Bjame também menciona duas vezes “eficiéncia”. Talvez isso ndo devesse nos surpreender
vindo do criador do C++, mas acho que ele quis dizer mais do que um simples desejo por
agilidade. A repetigäo de ciclos náo é elegante, no € belo. E repare que Bjame usa a palavra
“incitar” para descrever a consequéncia de tal deselegäncia. A verdade aqui € que um código
im incita o crescimento do caos num código. Quando outras pessoas alteram um código ruim,
elas tendem a pioré-lo.

Pragmáticos, Dave Thomas € Andy Hunt expressam isso de outra forma. Eles usam a metáfora.
das janclas quebradas.* Uma constru¿áo com janelas quebradas parece que ninguém cuida dela.
Dessa forma, outras pessoas deixam de se preocupar com ela também. Elas permitem que as
outras janelas se quebrem também. No final das contas, as próprias pessoas as quebram. Elas
cstragam a fachada com pichagdes e deixam acumular lixo. Uma única janela inicia 0 processo
de degradaçäo.

8 Capitulo 1: Código Limpo

Bjarne também menciona que o tratamento de erro deva ser completo. Isso significa prestar
atençäo nos detalhes. Um tratamento de erro reduzido é apenas uma das maneiras pela qual
os programadores deixam de notar os detalhes. Perdas de memória e condigöes de corrida sdo
outras. Nomenclaturas inconsistentes so ainda outras. A conclusio é que um código limpo
requer bastante atengilo aos detalhes.

Bjame conclui com a asseveraçäo de que um código limpo faz bem apenas uma coisa. No €
por acaso que hä inúmeros princípios de desenvolvimento de software que podem ser resumidos
à essa simples afırmagäo, Varios escritores já tentaram passar essa ideia. Um código ruim
tenta fazer coisas demais, ele está cheio de propósitos obscuros e ambiguos. O código limpo é
«centralizado. Cada fango, cada classe, cada módulo expe uma única tarefa que munca sofre
interferéncia de outros detalhes ou fica rodeada por eles.

Grady Booch, autor do livro Object
Oriented Analysis and Design with
Applications

Um co impo é simples e dito, Ele & th bem lgivel
ramo uma prosa bem escri. Ele jamais torna cofiso 0
Gba de desemolvedor. em vez disso, cle end replete de
abstragdes claras e linker de controle obesas

Grady fala de alguns dos mesmos pontos que Bjame,
voltando-se mais para questäo da legibilidade. Eu,
particularmente, gosto desse ponto de vista de que ler um;
código limpo deve ser como ler uma prosa bem escrita.
Pense num livro muito bom que vocé já leu. Lembre-se de como as palavras eram substituidas
por imagens! Era como assistir a um filme, no era? Melhor ainda, vocé via os personagens,
‘ouvia os sons, envolvia-se nas emogdes e no humor.

Ler um código limpo jamais será como ler O senhor dos anéis. Mesmo assim, a analogía
‘coma literatura náo € ruim. Como um bom romance, um código limpo deve expor claramente
as questôes do problema a ser solucionado, Ele deve desenvolvé-las até um clímax e, entio, dar
ao leitor aquele “Ahá! Mas é claro!”, como as questdes € os suspenses que so solucionados na
revelagio de uma solugdo 6bvia.

“Acho que o uso que Grady faz da frase “abstragdes claras” € um paradoxo fascinante!
{Apesar de tudo, a palavra “clara” é praticamente um sinónimo para “explícito”. Meu dicionério
MacBook tem a seguinte definigäo para “claro(a)”: direto, decisivo, sem devaneios ou detalhes
desnecessärios. Apesar desta justaposiçäo de significados, as palavras carregam uma mensagem
poderosa. Nosso código deve ser decisivo, sem especulagdes. Ele deve conter apenas o necessário,
Nossos leitores devem assumir que fomos decisivos

(© Custo de Ter um Código Confuso 9

O “grande” Dave Thomas, fundador da
OTI, o pai da estratégia Eclipse

Am de seu criador um desemolvedor pode ler e melhorar
tim código lingo Ele om ete de unie e de acen
om ca be arc paras um mad, aks
‘ras, de se fazer uma tarea. past pouces dependencias
as ques 530 explitamente declaradas e ofrecen um
API minimo e cloro. O cio deve ser mel. que
depend da Inguagem, nem odo formato ncesária
ode express no cig em si

Dave compartilha do mesmo desejo de Grady pela
legibilidade, mas com uma diferenga relevante. Dave
afirma que um código limpo facilita para que outras
pessoas o melhorem. Pode parecer dbvio, mas näo se deve
enfatizar muito isso. Há, afinal de contas, uma diferenga
entre um código fácil de ler. um fácil de alterar

Dave associa limpeza a testes! Dez anos atrás, isso levantaria um ar de desconfianga. Mas 0
estudo do Desenvolvimento Dirigido a Testes teve grande impacto em nossa indústria e se tornou
uma de nossos campos de estudo mais essenciais. Dave está certo. Um código, sem testes, ndo
está impo. Näo importa o quai elegante, legível ou acessível esteja que, se ele nio possuir testes,
elo näo élimpo.

Dave usa a palavra mínima duas vezes, Aparentemente ele dä preferéncia a um código
pequeno. De fato, esse tem sido uma citagdo comum na literatura computacional. Quando menor,
‘melhor. Dave também diz que o código deve ser inteligivel - referéncia esta à programacdo
inteligivef (do livro Literate Programming) de Donald Knuth. A conclusäo € que o código deve *
ser escrito de uma forma que seja ineligivel aos seres humanos.

Michael Feathers, autor de Working
Effectively with Legacy Code

Eu podri ia todas as qualidade que veo em um cco
lampe, mas há uma predominante que ev a odas as otras.
Un código lmpo sempre parce que fo sert por alguón
que se morons No há nada de vio no ques pode fer
ra orná melho Tudo fs pensado pelo ato do código,
Ese tomar pensar em algunas mars, vocé volar 0
inicio, ow sea, recado o código deat para voc por

algun ue se impor basan com esa tar

One word: care. É esse o assunto deste livro. Talvez,
um subtítulo apropriado seria Como se importar ||:
com o código.

Michael bate na testa; um código limpo é um código.
que foi cuidado por alguém. Alguém que calmamente

re

10 Capitulo 1: Código Limpo

o manteve simples e organizado; algı
que se importou.

que prestou a atençao necessária sos detalhes; alguém

Ron Jeffries, autor de Extreme
Programming Installed and Extreme
Programming Adventures in C#

Ron iniciou sua carreira de programador em Fortran, no.
Strategic Air Command, e eriou códigos em quase todas
as linguagens € máquinas. Vale a pena considerar suas
palavras:

Nestes anos recente, comece, e quas finale, com as regres
‘de Beck sobre código simples, Em nde de prordade, so

+ Bre odos os ets:
Sem dplcoçdo de dig:

2 Express todas axis proto qe esto no sistema:
Minima & número de entdades, como clases, métodos
umge outras do po.

¿sil de que
endo, exprestor

Dessasquaro,focome mas na de duplcazto. Quando mesma coisa eta repetidos
Jima adela em suo cabega nests bem rpresetado no código, ento descobri oque
quel idea com mais ch

preise para mim 5do nomes significatives cotume mudar o nome das cosas wärs vezes ant de
finalizar Com ferramentas de programcdo modern, como Eclipse. renomear € bastante fl, or suo
Meme incomodo em facer sso. Fmretato, a espresiidade va al de ones Tambo vrifc se um método
objetos mals de uma arf. Se for um objeto. provaveiment le precisar ser dividido em dis ou ais. Se
for um meta, sempre uso a reftoragdo do Método de Estracdo el. result em un método que expresa
mas claramente ua fundo € em outros métodos que diem como el fea

Duplsad e exresividae me fevam an que considero um código impo. e melhorar um cdg rum com
panas eses dls concis na mente pode fr uma grande difeenga Ha. porém, una ora corsa de qual
Cow cinto quandı program, que 61m pouco mas ic de explicar

“Gots enon de trabalho, parce me que todos us programadores penton tuo igual. Un exemplo “encontrar
‘ona mana colecdo”. Tenhamos uma base de dados cam registros de funconáris ou uma tbe hash de
‘haves valores où um ver de lens de olga tipo, geralnente procuramos um lem expect naguela
Colega. Quando percebo Iso, cosmo implementar ess fun em um método ou clase mais altrato — 0
que me proporciona aguas vantagens ineressanies

‘so Implementar a incionalidade aora com algo mas simples digamos uma abe hash, mas como agora
nda as rerencosdquela busca exdo na minha clase ou méodo abat. poss alerar Implementado
Sempre que desir. Paso ainda prosegur rapidamente enquanto presero à apache de aleracio
Jura

“lim cso. de oleesgeraimente chama minha teng parao que realmente está acontecendo, e impede
que u implement funcionalidades arbre coles quando tudo que cu preciso sb simples maneras de
"coman que dej. Ro de duplicado de código, ala xprssvidado ciao mo nici de ahragder
Simples. Esso que torna para mim un código limpo.

Aqui, em alguns breves parágrafos, Ron resumiu o conteúdo deste livro. Sem duplicaçäo, uma
tarefa, expressividade, pequenas abstragdes. Tudo foi mencionado.

© Custo de Ter um Código Confuso. u

Ward Cunningham, criador do conceito
de “WikiWiki”, criador do Fit, co-criador
da Programagäo Extrema (eXtreme
Programming). Incentivador dos Padrôes
de Projeto. Lider da Smalltalk e da OO.
Pai de todos aqueles que se importam
com o código.

Yo sabe que está criando um cio lingo quando cada ora, 7
que sod ela e mostra com que voce espera. Vee pode

‘hamar de código elo quando cle tambn fz parecer que a
inguagom fl ea pora o problema.

Declaragdes como essa sto características de Ward. Vocé a
Ig, coloca na sua cabeça e segue para a próxima. Parece tio racional, tio Sbvio que raramente €
memorizada como algo profundo. Vocé acha que basicamente já pensava assim, Mas observemos
mais atentamente.

=... 0 que voce esperava”, Qual foi a última vez que vocé viu um módulo como vocé o
esperava que fosse? No € mais comum eles serem complexos, complicados, emaranhados?
Interpretá-lo erroneamente ndo é a regra? Vocé ndo está acostumado a se descontrolar ao tentar
entender o raciocinio que gerou todo o sistema e associá-lo ao módulo que estás lendo? Quando
foi última vez que vocé leu um código para o qual vocé assentiu com a cabega da mesma forma
que fez com a declarado de Ward?

Este espera que ao ler um código limpo nada Ihe surpreenda. De fato, näo será nem preciso
muito esforgo. Vocé ir l-lo e será basicamente o que voce já esperava. O código € öbvio,“
simples e convincente. Cada módulo prepara o terreno para o seguinte, Cada um Ihe diz como o
próximo estará escrito, Os programas que sño to limpos e claros assim foram tdo bem escritos
que vocé nem perceberä. O programador o faz parecer super simples, como o é todo projeto
extraordinäro.

Ea noçäo de beleza do Ward? Todos já reclamamos do fato de nossas linguagens nao tiverem
sido desenvolvidas para os nossos problemas. Mas a declaragäo de Ward coloca novamente
‘© peso sobre nés. Ele diz que um código belo faz parecer que a linguagem foi feita para o
problema! Portanto, € nossa responsabilidade fazer a linguagem parecer simples! Há brigas
por causa das linguagens em todo lugar, cuidado! Nao é a linguagem que faz os programas
parecerem simples, € o programador!

12 Capitulo 1: Código Limpo

Escolas de Pensamento

E eu (Tio Bob)? O que é um código limpo para mim? É
isso 0 que este livro Ihe dirá em detalhes, o que eu e meus
compatriotas consideramos um código limpo. Diremo-the o
que consideramos como nomes limpos de variáveis, fungdes
limpas, classes limpas etc. Apresentaremos nossos conceitos
‘como verdades absolutas, e no nos desculparemos por nossa
austeridade. Para nós, a essa altura de nossa carreira, tas
‘conceitos sdo absolutos. Sio nossa escola de pensamento
acerca do que seja um código limpo.

Nem todos os mestres de artes marciais concordam
com qual seria a melhor arte marcial de todas ou a melhor
Técnica dentro de uma arte marcial específica. Geralmente,
eles criam suas próprias escolas de pensamento e recrutam
alunos para serem ensinados. Dessa forma, temos © Jiu
Jitsu dos Gracie, criado e ensinado pela familia Gracie, no Brasil; o Jiu Jitsu de Hakkoryu,
criado e ensinado por Okuyama Ryuho, em Töquio; e 0 Jeer Kune Do, criado e ensinado por
Bruce Lee, nos EUA.

Os estudantes se dedicam à doutrina ensinada por aquela determinada arte, e aprendem 0
que seus mestres ensinam, geralmente ignorando os ensinamentos de outros mestres. Depois,
conforme os estudantes progridem, costuma-se mudar o mestre de modo que possam expandir
seus conhecimentos e práticas. Alguns, no final, continuam a fim de refinar suas habilidades,
descobrem novas técnicas e criam suas próprias escolas.

Nenhuma dessas escolas está 100% certa. Mesmo assim dentro de uma determinada escola.
‘agimos como os ensinamentos e técnicas fossem os certos. Apesar de tudo, há uma forma correta
de praticar o Ju Jitsu de Hakkoryu, ou Jeet Kune Do. Mas essa retidho dentro de uma escola náo
invalida as técnicas de outra.

Considere este livro como uma descricäo da Escola de Código Limpo da Object Mentor. As.
técnicas e ensinamentos sáo a maneira pela qual praticamos nossa arte. Estamos dispostos alegar
que se voct seguir esses ensinamentos, desfrutará dos beneficios que também aproveitamos e
aprenderá escrever códigos limpos e profissionais. Mas ndo pense vocé que nós estamos 100%
“certos”. Provavelmente hä outras escolas e mestres que tém tanto para oferecer quanto nés. O
<corretoseria que vocé aprendesse com clas também.

De fato, muitas das recomendagdes neste livro sto contraditórias. Provavelmente vocé nio
‘concordari com todas e poderá até mesmo discordar intensivamente com algumas. Tudo bem.
Näo podemos querer ter a palavra final. Por outro lado, pensamos bastante e por muito tempo
sobre as recomendagdes neste livro, As aprendemos a0 longo de décadas de experiéncia €
repetidos testes e erros. Portanto, concorde vocé ou nfo, seria uma pena se vocé náo conseguisse
enxergar nosso ponto de vista

Somos Autores 5

Somos Autores

(© campo Gauthor de um Javadoc nos diz quem somos. Nós somos autores, e todo autor tem
Ieitores, com os quais uma boa comunicaçäo € de responsabilidade dos autores. Na próxima vez
em que vocé eserever uma linha de código, lembre-se de que vocé é um autor, eserevendo para
leitores que julgardo seus esforgos,

Vocè talvez se pergunte: o quanto realmente se 18 de um código? A maioria do trabalho ndo.

Voc já reproduziu uma sessdo de edigio? Nas décadas de 1980 e 1990, tinhamos editores,
como o Emaes, que mantinham um registro de cada tecla pressionada. Vocé podia trabalhar
por horas e, ento, reproduzir toda a sua sessäo de edisäo como um filme passando em alta
velocidade. Quando fiz iso, os resultados foram fascinantes,

{A grande maioria da reprodugäo era rolamento de tela e navegaçäo para outros módulos!

Bab entra no modulo.
Ele descia até a fungdo que precisava ser alterada.

Ele para e pondera suas opgdes.

Oh, ele sobe para o inicio do módulo a fim de verificar a inicializaçäo da variável.

Agora ele desce novamente e comega a digitar.

Opa, ele está apagando o que digitou!

Ele digita novamente.

Ele apaga novamenie.

Ele digita a metade de algo e apaga! à

Ele desce até outra fungäo que chama a que ele está modificando para ver como ela é,
chamada.

Ele sobe novamente e digita o mesmo código que acabara de digitar

Ele para.

Ele apaga novamente!

Ele abre uma outra janela e analisa uma subclasse. A funcao é anulada?

Bem, vocé entendeu! Na verdade, a taxa de tempo gasto na leitura v. na escrita
Constantemente lemos um código antigo quando estamos criando um novo.
Devido à tamanha diferenga, desejamos que a leitura do código seja fácil, mesmo se sua

eriaçäo for árdua. É claro que näo há como escrever um código sem lé-lo, portanto tomá-lo de

fácil letura realmente facilita a escrita.

‘Nilo há como escapar desta lógica. Vocé ndo pode escrever um código se näo quiser ler as
‘outras partes dele. O código que voc tenta escrever hoje será de dificil ou fácil leitura dependendo
‘a facilidade de leitura da outra parte do código. Se quiser ser rápido, se quiser acabar logo, se
quiser que seu código seja de fácil escrita, torne-o de fácil Iitura.

1 Capitulo 1: Código Limpo

A Regra de Escoteiro

Nao basta escrever um código bom. Ele precisa ser mantido sempre limpo. Todos já vimos
códigos estragarem e degradarem com o tempo. Portanto, precisamos assumir um papel ativo na
prevençao da degradagao,

À Boy Scouts of America (maior organizaçäo de jovens escoteiros dos EUA) tem uma regra
simples que podemos aplicar à nossa profissäo.

Deixe a área do acampamento mais limpa do que como vocé a encontrou.*

‘Setodos deixässemos nosso código mais limpo do que quando o comegamos, ele simplesmente
ndo degradaria. A limpeza no precisa ser algo grande. Troque o nome de uma variável por um
‘melhor, divida uma funcio que esteja um pouco grande demais, elimine um pouco de repetigáo
de código, reduza uma instruçäo if aninhada.

‘Consegue se imaginar trabalhando num projeto no qual o código simplesmente melhorou com
‘o tempo? Vocé acredita que qualquer alternativa sejaprofissional? Na verdade, aperfeigoamento,
continuo ndo é inerente ao profissionslismo?

Prequela e Princípios

Em muitas maneira est ivro € uma “prequela” de outro que escrevi em 2002, chamado Agile
Software Development: Principles. Patterns, and Practices (PPP). Ele fala sobre os principios
{do projeto orientado a objeto e muitas das prticas utilizadas por desenvolvedores profssionais.
Se vocé ainda ndo leu o PPP, talvez ache que é a continuagdo deste livro. Se já o leu, entäo
perceberá que ele é bastante parecido a esse em relagäo aos códigos.

Neste livro ha referencias esporádicas a diversos principios de projeto, dentre os quais est:
Principio da Responsabilidade Unica (SRP. sigla em inglés), Principio de Aberto-Fechado (OCP,
sigla eminglés), Principio da Inversäo da Independencia (DIP, sigla em inglés) dentre outros.
Esses principios slo descritos detalhadamente no PPP.

Conclusáo

Livros sobre arte mio prometem Ihe tornar um artista. Tudo o que podem fazer € Ihe oferecer
algumas das ferramentas, técnicas e linhas de pensamento que outros artistas usaram. Portanto,
este livro nfo pode prometer Ihe tomar um bom programador. Ou Ihe dar a “sensibilidade ao
código”. Tudo o que ele pode fazer é Ihe mostrara linha de pensamento de bons programadores
€ 08 truques, técnicas e ferramentas que cles usam.

"Assim como um livro sobre arte, este está cheio de detalhes. Há muitos códigos. Vocé verá
códigos bons e mins; código ruim sendo transformado em bom; listas de heurísticas, orientagóes
técnicas; e também exemplo após exemplo. Depois disso, é por sua conta.

Lembre-se daquela piada sobre o violinista que se perdeu no caminho para a apresentagdo em

Bibliogratia 15

um concerto? Ele aborda um senhor na esquina e Ihe pergunta como chegar ao Camegie Hall. O
senhor observa o violinista com seu violino debaixo dos bragos e diz “Pratique, filho. Pratique!”.

Bibliografia
[Beck07|: Implementation Patterns, Kent Beck, Addison-Wesley, 2007.

[Knuth92]: Literate Programming, Donald E. Knuth, Center for the Study of Language and
Information, Leland Stanford Junior University, 1992.

2

Nomes Significativos

por Tim Ottinger

Introducáo

Há nomes por todos os lados em um software. Nomeamos nossas variäveis, füngdes, parámetros,
classes e pacotes, assim como os arquivos-fonte e os diretórios que os possui. Nomeamos também
nossos arquivos jar € war e car. E nomeamos, nomeamos e nomeamos. Como fazemos muito isso, €
melhor que o fagamos bem. A seguir estáo algumas regras simples para a criaçäo de bons nomes.

18

Use Nomes que Revelem seu Propósito

Dizer que os nomes devem demonstrar seu propósito é fácil. Mas queremos que vocé saiba que
estamos falando sério. Escolher bons nomes leva tempo, mas economiza mais. Portanto, cuide
de seus nomes e troque-os quando encontrar melhores. Todos que lerem seu código (incluindo
vocé mesmo) ficardo agradecidos,

O nome de uma variävel, fungäo ou classe deve responder a todas as grandes questdes. Ele
deve Ihe dizer porque existe, o que faz e como € usado. Se um nome requer um comentário, entäo
ele náo revela seu propósito

int di // tempo decorrido em dias

O nome d nao revela nada, Ele náo indica a ideia de tempo decorrido, nem de dias. Devemos
escolher um nome que especifique seu uso para mensuragdo e a unidade usada.

int elapsedrimernDays:
int dayssincecreat ior
int dayssincemodification;
int AleAgeinDays;

Escolher nomes que revelem seu propósito pode facilitar bastante o entendimento ea alteragäo
do código. Qual o propósito deste código?

public List<int(]> getThem() (
List<intl]> listl = new Arraybisteint[l>0z
for (nel) x : thelist)
iE (xo) == 4)
List1.add(x):
return List:

)

Por que € dificil dizer o que o código faz? Nao hä expresses complexas.

O espagamento e a endentaçäo so cabiveis. Só há trés variáveis e duas constantes. Nem
‘mesmo há classes refinadas ou métodos polimérficos, apenas uma lista de vetores (pelo menos
¿o que parece), O problema nao € a simplicidade do código, mas seu aspecto implicito, isto é, 0
«contexto náo está explícito no código. Devido a isso, é necessário que saibamos as repostas para
questdes como:

1. Que tipos de coisas estdo em theList?
2. Qual a importáncia de um item na posiçäo zero na theLást?
3. Qual a importáncia do valor 4?

4. Como eu usaria alista retornada?

As respostas para tais questöes nio esto presentes no exemplo do código, mas poderiam.
Digamos que estejamos trabalhando num jogo do tipo “campo minado”. Percebemos que o
tabuleiro € uma lista de células chamada the. se. Vamos renomeá-la para gameBoard.

Evite Informagdes Erradas 1

Cada quadrado no tabulciro é representada por um vetor simples, Mais tarde, descobrimos
que a posigäo zero € armazena um valor de status e que o valor 4 significa “marcado com uma
bandeirinha". Ao dar esses nomes explicativos, podemos melhorar consideravelmente o código:

public Listeint[]> getrlaggedcelle
List<int()> faggedcells = new ArrayListeint(}>0;
for nel) cell : gameBoard)
3£ (cell (STATUS_VALUE]
Aaggedcells.adäicell)
return flaggedcells;

FLAGGED)

)

Note que a simplicidade do código no mudou. Ele ainda possui o mesmo número de
operadores e constantes, com o mesmo número de ¡tens aninhados. Entretanto, o código
ficou muito mais explícito.

Podemos continuar e criar uma classe simples para as células, em vez de usar um vetor int,

Ela pode ter uma funçäo com um nome revele seu propósito (chamada isFlagged, ou seja
“está marcada com uma bandeirinha”) para ocultar os números mágicos,

© resultado € uma nova versio da fungáo:

public List<Cell> getrlaggedcells() {
List<Cell> faggedcells = new Arraytist<Cell=():
for (Cell cell : gameBoard)
if (cell.isFlaggedi)
aggedcel1s.addíce11);
return Naggedcells;

3

Com essas simples alteragdes de nomes, náo fica difícil entender o que está acontecendo. Esse €
‘© poder de escolher bons nomes.

Evite Informacóes Erradas

Os programadores devem evitar passar dicas falsas que confundam o sentido do código.
Devemos evitar palavras cujos significados podem se desviar daquele que desejamos. Por
exemplo, hp, aix e aco seriam nomes ruins de varidveis, pois sio nomes de plataformas
Unix ou variantes. Mesmo se estiver programando uma hipotenusa e hp parecer uma boa
abreviado, o nome pode ser mal interpretado.

No se refra a um grupo de contas como accountist, a menos que realmente seja uma
List. A palavra lis (lista) significa algo especifico para programadores, Se o que armazena as
contas ndo for uma List de verdade, poderá confundir os outros.! Portanto, account Group ou
bunchofaccounts ou apenas accounts seria melhor.

Cuidado ao usar nomes muito parecidos. Fica dificil perceber a pequena diferenga

entre XYZControllerForEfficientHandlingofStrings em um módulo €
xyZControllerForkfficientstorageofstrings em outro. Ambas as palavras so
‘muito semelhantes.

20 ‘Capitulo 2: Nomes Significativos

Usar conceitos similares para montar palavras é informaçäo. Usar formatos inconsistentes
para palavras leva a uma má interpretagdo. Com os ambientes Java de hoje dispomos do recurso
‘de autocompletar palavras. Digitamos alguns caracteres de um nome ¢ pressionamos uma
combinacdo de teclas (se houver uma) e, ento, aparece uma lista de possiveis palavras que
Tetras. Isso é muito prático se houver nomes muito parecidos organizados
s provável que 0
desenvolvedor escolha um objeto pelo nome, sem consultar seus comentários ou mesmo a lista
de métodos fomecidos por aquela classe.

Um exemplo real de nomes que podem gerar confusdo é o uso da letra “I” minúscula ou da
vogal “o” maiúscula como nome de variáveis. O problema € que eles se parecem com o um € o
zero, respectivamente.

© leitor pode achar que inventamos esse exemplo, mas já vimos códigos nos quais isso
acontecia bastante. Uma vez, o autor do código sugeriu o uso de fontes distintas de modo a
realgar mais as diferengas, uma solugdo que teria de ser repassada de forma oral ou escrita a
todos os futuros desenvolvedores. Com uma simples troca de nome, o problema € resolvido com
bjetividade e sem precisar criar novas tarefas.

Faça Distinçôes Significativas

Os programadores criam problemas para si próprios
quando criam um código voltado unicamente para
‘um compilador ou interpretador. Por exemplo, como.
mio é possivel usar o mesmo nome para referirse.
a duas coisas diferentes num mesmo escopo, voce y
pode decidir alterar o nome de maneira arbitrária. Ás 1
Vezes, isso é feito escrevendo um dos nomes errado,
produzindo a uma situagdo na qual a corregdo de erros
de ortografía impossibilita a compilagäo.' Nao basta.
adicionar números ou palavras muito comuns, mesmo.
‘que o compilador fique satisfeito, Se os nomes precisam ser diferentes, entho também devem ter
significados distintos.

"Usar números sequenciais em nomes (al, a2,...aN) € 0 oposto da seleçäo de nomes
expressivos. Eles ndo geram confusäo, simplesmente nao oferecem informaçäo alguma ou dica
sobre a intençäo de seu criador. Considere o seguinte:

public static void copyCharsichar alll, char a2) (
for (int i= 0; i < al.length; i+) (
a2ti] = ailils
9
)

{Use Nomes Pronunetiveis

Tica muito mais fácil ler essa fungdo quando usam-se source e destination como nomes
de parámetros. Palavras muito comuns sdo outra forma de distinçäo que nada expressam.
Imagine que vocé tenha uma classe Product. Se houver outra chamada Productinfo ou
ProductData, vocé usou nomes distintos que ndo revelam nada de diferente. Info e Data sio
palavras muito comuns e vagas, como “un”, “uma” €

‘Observe que nio há problema em usar prefixos como “um” e “a”, contanto que fagam
uma distinçäo significativa. Por exemplo, vocé pode usar “um” para variáveis locais e “a”
para todos os parámetros de fungdes. O problema surge quando vocé decide chamar uma
variável de aZork só porque já existe outra chamada zork.

Palavras muito comuns slo redundantes. O nome de uma variável jamais deve conter a
palavra “variável”. O nome de uma tabela jamais deve conter a palavra tabela. Entäo como
NameString é melhor do que Name? Um Name pode ser um número do tipo ponto flutuante?
Caso possa, estaria violando uma regra sobre passar informagdes incorretas. Imagine que
‘voc’ encontre uma classe Customer € outra CustomerObject. O que a diferenga nos nomes
Ihe diz? Qual seria a melhor para possuir o histórico de pagamento de um cliente?

'Conhecemos um aplicativo que é assim. A fim de proteger seus desenvolvedores,trocamos
‘os nomes, mas abaixo está exatamente o tipo de ero:

getActiverccount();
getActiverccounts();
getactiverccount Info()?

Como os programadores desse projeto poderiam saber qual das rés fungdes chamar?

Na auséncia de convengdes específicas, ndo há como distinguir moneyAmount de money,
customerlnfo de customer, accountData de account e theMessage de message. Faça adistingäo
dos nomes de uma forma que o leitor compreenda as diferengas.

Use Nomes Pronunciáveis

Os ser humano € bom com as palavras. Uma parte considerável de seu cérebro é responsävel
pelo conecito das palavras. E, por definiçäo, as palavras sao pronunciäveis. Seria uma lástima
io tirar proveito dessa importante parte de nosso cérebro que evoluiu para lidar com a lingua
falada.Sendo assim, rie nomes pronunciäveis.

‘Se näo puder pronunciá-lo, näo teri como discutir sobre tal nome sem parecer um idiot.
“Bem, aqui no bé cé ere très cé ene 18, temos um pé esse zé qué int, viram?”. Isso importa
porque a programacáo é uma atividade social

Conhego uma empresa que possui uma variável genymdhms (generation date, year,
‘month, day, hour, minute e second) e seus funcionários saem falando “ge däbliu eme dé agi

“eme esse”. Tenho um hábito iritante de pronunciar tudo como está escrito, portanto comecei
a falar “gen-yah-muddahims”.

Depois desenvolvedores e analistas estavam falando assim também, e ainda soava
estúpido. Mas estivamos fazendo uma brincadeira e, por isso, foi divertido. Engragado ou
ndo, estamos tolerando nomeaçäes de baixa qualidade, Novos desenvolvedores tiveram de
pedir que Ihes explicassem as variáveis, e, entäo, em vez de usar termos existentes na lingua,
inventavam palavras bobas ao pronuncié-las. Compare

a

2 Capítulo 2: Nomes Signiicativos

class DraRcra102 (
private Date genymähns;
Private Date modyndhns;
private final String pszqint = “102;
Lee

)

com

class Customer {
private Date gener:
private Date modificar ionTimestamp;
private final String recordid = “102
Pe
%
Agora € possivel uma conversa inteligente: ikey, dé uma olhada este registro! A generation
‘timestamp ("criagäo de marcaçäo de horário”) está marcada para amanhä! Como pode?”.

Use Nomes Passiveis de Busca

'Nomes de uma sö letra ou números possuem um problema em particular por näo ser fcil localiza
los ao longo de um texto. Pode-se usar facilmente o grep para MAX_CLASSES_PER_STUDENT,
mas buscar o número 7 poderia ser mais complicado. Nomes, defini;des de constantes e árias
outras expressöes que possuam tal número, usado para outros propósitos podem ser resultados da
busca. Pior ainda quando uma constante € um número grande e alguém talvez tenha trocado os
‘digits, criando assim um bug € ao mesmo tempo nâo sendo captada pela busca efetuada.

‘Damesma forma, onome “e" éumacscolharuim para qualquer variávela qual umprogramador
talvez precise fazer uma busca. É uma letra muito comum e provavelmente aparecerá em todo
texto em qualquer programa. Devido a isso, nomes longos se sobressucm aos curtos, e qualquer
nome passivel de busca se sobressai a uma constante no código.

Particularmente, prefiro que nomes com uma única letra SÓ sejam usados como variáveis
locais dentro de métodos pequenos. O tamanho de um nome deve ser proporcional ao
tamanho do escopo.

[NS]. Se uma variável ou constante pode ser vista ou usada em varios lugares dentro do código,
é imperativo atribui-la um nome fácil para busca. Novamente, compare

for (int j=0; je; jee (
s += (131%49/5;
3

int realDaysPerIdealDay = 4;
const int WORK DAYS_PER MEEK =
int sun = 0
for (int j=0; j < NUMBER_OF_TASKS; j++) (
int realTaskDays = task£stimatelj) * realDaysPerIdealDay;
int realTaskieoks = (realdays / WORK_DAYS_PER WEEK);
sun += zealTasklieeks;

>

Evite Codifieaçües 2

Note que sun ndo é um nome prático, mas pelo menos é fácil de procurar. O nome usado no
código serve para uma funçäo maior, mas pense como seria muito mais fácil encontrar WORK
DAYS_PER_WEEK do que buscar em todos os lugares nos quais 0 5 aparece e, entio, filtrar os
resultados para exibir apenas as instáncias que vocé deseja.

Evite Codificaçôes

Já temos de lidar com bastante codificagdo © ndo precisamos acrescentar mais. Cc
informagóes do escopo ou tipos em nomes simplesmente adiciona uma tarefa extra de decifraçäo.
Diticilmente parece lógico contratar um novo funcionario para aprender outra “linguage
‘codificadora além da atual usada no código no qual se está trabalhando. E uma sobrecarga mental
desnecessäria ao tentar resolver um problema. Nomes codificados raramente sto pronunciáveis,
além de ser facil escrevé-los incorretamente.

ANotagáo Hüngara

Antigamente, quando trabalhávamos com linguagens com limite de tamanho para os nomes,
violávamosessaregra quando necessário, com certo arrependimento. O Fortran forgava codificagdes.

ao tomar a primeira letra uma indicaçäo para o tipo. Versdes anteriores do BASIC só permitiam

‘uma letra mais um digito. A Notagdo Húngara (NH) inovou essas limitagdes. Na época da API em

‘C do Windows, a NH era considerada muito importante, quando tudo era um inteiro ou um ponteiro

‘para um inteiro long de 32 bits ou um ponteiro do tipo void, ou uma das diversas implementagöes.

de “strings” (com finalidades e atributos diferentes). O compilador nao verificava os tipos naquele

tempo, entdo os programadores precisavam de ajuda para lembrar dos pos.

‘Em linguagens modernas, temos tipos muito melhores, e os compiladores os reconhecem ¢ os
tomam obrigatérios. Ademais, há uma certa tendéneia para acriagdo de classes e fungdes menores.
de modo que as pessoas possam ver onde cada variável que estdo usando foi declarada.

(Os programadores Java nfo precisam definir o tipo. Os objetos já sdo 0 proprio tipo, ea edigño
de ambientes se tornow to avangada que detectam quando se usa inadequadamente um tipo antes.
‘mesmo da compilagäo! Portanto, hoje em dia, a NH e outras formas de convengäo de tipos sto
basicamente obstáculos. Eles dificultam a alteraçäo do nome ou do tipo de uma variável, fungáo
ou classe; dificultam a leitura do código; e criam a possibilidade de que o sistema de codificaçao
induza o leitor ao erro.

PhoneNumber phonestring:
17-0 nome nao muda na alteracao do tipo!

Prefixos de Variáveis Membro

Vocé ndo precisa mais colocar o prefixo m_em variáveis membro. Mas para isso, suas classes e
füngöes devem ser pequenas. E vocé deve usar um ambiente de ediçäo que realce ou colore as
variáveis membro de modo a distingui-las.

public class Part {
private String m_dsc; // Descricäo textual
Void setName(string name) (

2 Capitulo 2: Nomes Signticativos

mdse = name;
>


public class Part {
String description;
void setDescription(string description) {
this.description = description;


»

Alt disso, as pessoas rapidamente aprendem a ignorar o prefixo (ou sufixo) para visualizar
a parte significativa do nome. Quanto mais lemos o código, menos prefixos enxergamos. No
final, estes se tomam parte invisiveis e um indicativo de código velo.

Interfaces e Implementagöes

As vezes há casos.specias par coficagdes. Por exemplo, digamos que vocéestejaconsrindo
‘uma ABSTRACT FACTORY para criar formas. Essa factory será uma interface, e implementada
por uma classe concreta. Como devemos nomeá-la? 1ShapeFactory e Shaperactory? Prefiro
‘no enfeitar as interfaces. O “1” no inicio, täo comum no hoje em dia, é na melhor das hipöteses
"ima distacio, e na por slo informagdes excesivas. No quero que meus usuários sibam
que estou lhes dando uma interface, e sim, apenas uma Shapezactory. Poranto, se eu devo
Colic sejaa interface ou a implementacio,escolho est, Para codificar a interface, preferivel
chamé-lade ShopePactoryinp, où mesmo de CShapeFactory.

Evite o Mapeamento Mental

Os leitores nflo devem ter de traduzir mentalmente os nomes que vocé escolheu por outros que
cles conheçam. Essa questäo costuma levar a decisäo de no usar os termos do dominio do
problema e nem os da solugäo. Este € um problema com nomes de variáveis de uma só letra
Certamente um contador de iteragdes pode ser chamado de “i”, “3” ou “" (mas nunca 1) isso
Já se tomou uma tradigäo — se seu escopo for muito pequeno e no houver outros nomes que
possam entrar em conflito com ele.

Entretanto, na maioria dos contextos, um nome de uma só letra € uma escolha ruim; € apenas
“um armazenador que o leitor deverá mentalmente mapear de acordo com conceito em uso. Nao
há razdo pior do que usar o nome “<” só porque “a” e “9” já eto sendo usados.

‘De maneira geral, os programadores säo pessoas muito espertas. E esse tipo de pessoas gosta
de se exibir mostrando suas habilidades mentais. Apesar de tudo, se vocé puder confiantemente
se lembrar de que 0” minúsculo € uma versio da url sem o host eo contexto, entáo obviamente
vocé é muito esperto.

‘Uma diferenga entre um programador esperto e um programador profissional & que este
entende que clareza é fundamental. Os profissionais usam seus poderes para o bem, e escrevem
códigos que outros possam entender.

Nomes de Métodos 25

Nomes de Classes

Classes e objetos devem ter nomes com substantivo(s), como Cliente, Paginaiixi, Conta e
Anal iseEndereco. Evite palavras como Gerente, Processador. Dados ou Info no nome de uma
classe, que também ndo deve ser um verbo.

Nomes de Métodos

Os nomes de métodos devem ter verbos, como postarPagamento, excluirPagina ou salvar.
Devem-se nomear métodos de acesso, alteragáo e autenticacáo segundo seus valores e adicionar
os prefixos get, set ou is de acordo com o padritojavabean.*

string name = employee.getName();
customer. setName("mike’
Af (paycheck-isFosted())

Quando os construtores estiverem sobrecarregados, use métodos factory estáticos com nomes
que deserevam os parámetros. Por exemplo,

Complex fulcrumPoint = Complex.FromtealNumber (23.0);

é melhor do que

Complex fulerumpos:

new Complex(23.0);

Para forgar seu uso, torne os construtores correspondentes como privados.

Náo dé uma de Espertinho

Se os nomes forem muito “espertinhos”,
apenas as pessoas que compartithem do mesmo
senso de humor que seu dono into lembri-los,
€ só enguanto se lembrarem da brincadeira
Eles saberlo o que deve fazer a funcio
HolystandGrenade? Claro, € engragado, mas
talvez nesse caso Deletezteno fique melhor
Opte por clareza no lugar de divertimento.

'Essas gracinhas em códigos costumam aparecer na forma de coloquialismos e girias. Por
‘exemplo, näo use firmax () para significar terminar (). Ndo use piadas de baixo caldo,
como cairFora para significar abortar (). Diga o que vocé quer expressar, Expresse 0
que vocé quer dizer.

26 Capítulo 2: Nomes Signiicativos

Selecione uma Palavra por Conceito

Escolha uma palavra por cada conceito abstrato e fique com ela. Por exemplo, é confuso ter
pegar. recuperar e obter como métodos equivalentes de classes diferentes. Como lembrar a
qual método pertence cada classe? Infelizmente, vocé geralmente precisa se lembrar qual empresa,
grupo ou pessoa criou a biblioteca ou a classe de modo a recordar qual termo foi usado, Caso
contririo, vocé perde muito tempo vasculhando pelos cabegalhos e exemplos de códigos antigos.

Os ambientes modemos de ediçäo, como o Eclipse e 6 IntelliJ, oferecem dicas relacionadas
a0 contexto, como a lista de métodos que vocé pode chamar em um determinado objeto. Mas
note que a lista geralmente nao Ihe oferece os comentários que vocé esereveu em tomo dos
nomes de suas fungdes. Vocé tem sorte se receber o parámetro nomes (names) das declaragdes
das funçôes. Os nomes das fungdes tém de ficar sozinhos, e devem ser consistentes de modo que
vocé possa selecionar o método correto sem qualquer busca extra

Da mesma forma, é confuso ter um controlador, um gerenciador e um driver no
‘mesmo código-fonte. Qual a principal diferenga entre um GerenciadorDeispositivo € um
controlador -de-protoco1o? Porque ambosndo slo controladores ougorenciadores?
Ambos sto realmente drivers? O nome faz com que vocé espere dois objetos com tipos bem
distintos, assim como ter classes diferente,

Um léxico consistente € uma grande vantagem aos programadores que precisem usar
seu código.

Nao Faça Trocadilhos

Evite usar a mesma palavra para dois propósitos. Usar o mesmo termo para duas i
é basicamente um trocadilho.

Se voce seguir a regra “uma palavra por conceito”, vocé pode acabar ficando com muitas
classes que possuam, por exemplo, um método ada. Contanto que as listas de parámetros € 05
valores retornados dos diversos métodos add sejam semanticamente eq

Entretanto, uma pessoa pode decidir usar a palavra ada por fins de “consisiéncia” quando
ela na verdade nio aplica o mesmo sentido a todas. Digamos que tenhamos muitas classes nas
quais ada criará um novo valor por meio da adiçäo e concatenaçäo de dois valores existentes,
Agora, digamos que estejamos criando uma nova classe que possua um método que coloque
seu único parámetro em uma coleçäo. Deveriamos chamar este método de ada? Por termos
tantos outros métodos ada, isso pode parecer consistente. Mas, neste caso, a semántica €
diferente. Portanto, deveriamos usar um nome como inserir ou adicionar. Chamar este novo
método de add seria um trocadilho.

Nosso objetivo, como autores, é tornar a leitura de nosso código o mais fácil possivel
Desejamos que nosso código seja de rápida leitura, e nâo um estudo demorado. Queremos usar a
linguagem de um livro popular no qual é responsabilidade do autor ser claro, e náo uma linguagem
académica na qual a tarefa do estudioso é entender minuciosamente o que está escrito.

ferentes.

Use nomes a partir do Domínio da Solugáo

Lembre-se de que serdo programadores que leräo seu código. Portanto, pode usar termos de
Informática, nomes de algoritmos, nomes de padrdes, termos matemáticos etc. Nao € prudente

Adicione um Contexto Significativo 27

‘pensar num nome a partir do dominio do problema, pois ndo queremos que nossos companh
de trabalho tenham de consultar o cliente toda hora para saber o significado de um nome o qual
cles já conhecem o conceito, só que por outro nome.

‘O nome AccountVisicor (“conta do visitante”) significa 0 bastante para um programador
familiarizado com o padráo VISITOR. Qual programador ndo saberia o que € uma JobQueue
(fila de tarefas")? Há muitas coisas técnicas que os programadores devem fazer. Selecionar
nomes técnicos para tais coisas &, geralmente, o método mais adequado.

Use nomes de Domínios do Problema
Quando nic houver uma solugdo “à Ia programador”, use o nome do dominio do problema. Pelo
menos o programador que fizer a manutengdo do seu código poderá perguntar a um especialista
em tal dominio o que o nome significa

Distinguir os conceitos do dominio do problema dos do domínio da solugáo € parte da tarefa
de um bom programador e designer. O código que tem mais a ver com os conceitos do dominio
do problema tem nomes derivados de tal dominio.

Adicione um Contexto Significativo

Há poucos nomes que sio significativos por si soa maioria no é. Por conta disso, vocé precisa.
usar nomes que fagam parte do contexto para o leitor. Para isso vocé os coloca em classes,
fungöes e namespaces bem nomeados. Se nada disso funcionar, entio talvez como último recurso.
seja necessário adicionar prefixos ao nome.

Imagine que vocé tenha variáveis chamadas firstivane, LastName, street, houseNumber,
city, state e zipcode. Vistas juntas, fica bem claro que elas formam um enderego. Mas e se
‘vocé só visse a variável state sozinha num método? Automaticamente vocé assumiria ser parte
de um enderego?

Podem-se usar prefixos para adicionar um contexto: adärrirstiiane, addrLastNane,
adárstate ete. Pelo menos os leitores entenderäo que essas varidveis sio parte de uma estrutura
‘maior. É claro que uma melhor soluçäo seria criar uma classe chamada Address. Entfo, até 0
“compilador sabe que as variáveis pertencem a um escopo maior.

Veja o método na Listagem 2.1. As variáveis precisam de um contexto mais significativo? O
nome da funçäo oferece apenas parte do contexto; o algoritmo apresenta o rest.

‘Apés ter lido a fungo, vocé vé que trés variáveis, nunber, verb e pluralModiñer, fazem
parte da mensagem de deduglo (guess statistics message). Infelizmente, o contexto deve ser
inferido. Ao olhar pela primeira vez o método, o significado das variäveis ndo está claro,

2 “Capítulo 2: Nomes Significativos

Listagem 2-1
Variávele com contexto obscuro

private void printGuessStatistics (char candidate, int count) (
String number;
String verb:
String pluralNedifier;

Se (count == 0) (
verb = “existen”
pluralMoainer = "8";

> else if ‘count == 1) (
‘number =

verb = “Existe”
pluralModiser =
y as
"number = Integer.toStráng (count y
Verb = “existen
pluralMmoai

1

String guesstessage = String. format (

“there te 8s teta”, verb, nusber, Candidate, pluralModifier
»

print (guessMessage) ;

A fungdo & um pouco extensa demais também e as variáveis sdo bastante usados. A fim de
¿dividir a fungio em partes menores, precisamos criar uma classe GuessStat ist icsMessage
‘¢ tomar as trés variáveis como campos desta classe. Isso oferecerä um contexto mais claro
para as trés variáveis. Elas säo definitivamente parte da GuessStatisticsMessage. A melhora do
contexto também permite ao algoritmo ficar muito mais claro ao dividi-lo em fungdes menores
(veja a Listagem 2.2).

Listagem 2-2
Variávels possuem contexto
public class GuenaStatisticamessago |
private String number:
Private String verb:
Private String pluralmedifier:

publie String make(char candidate, int count) (
‘creacePluralbependentMessageParts (count);
Terurn string. format
“there 4» 45 tata,
verb, mimber, candidate, pluralNodifier }+


private void createpluralepandentMescageParte (int count) (
LE (count == 0) €
‘choreareNoLet tere ()
y elas if teount == 1) (
therezsoneLerter ();
y else €

0 Adicione Contextos Desnecessários »

Listagem 2-2 (continuagao)
Variávels possuen contexto
EnerenreMany Letters (count)
>
à

private void thereAreManyietters(int count) {

fhunber = Integer -tostring (count)
verb = “Existen”
pluralModifer = "6";

private void therereonetettor() {

number = “1

Verb = Existe”
pluralMedier «
N

private void therearenoLettera() {
‘number = "no"

plvralmodifier » “a:

Náo Adicione Contextos Desnecessários

Em um aplicativo ficticio chamado “Gas Station Deluxe” (GSD), seria uma péssima ideia
adicionar prefixos a toda classe com GSD. Para ser sincero, vocé estará trabalhando contra suas
ferramentas. Vocé digita e pressiona a tecla de autocompletar e recebe uma lista quilométrica de *
cada classe no sistema. Isso é inteligente? Para que dificulta a ajuda da IDE?

Da mesma forma, digamos que vocé inventou uma classe Mailangadaresa no módulo.
de contabilidade do GSD e que o chamou de GSpaccountadaress Mais tardo, vocé precisa
armazenar um enderego postal de seu cliente no aplicativo. Vocé usaria GSDAccountAddress?
Parece que o nome é adequado? Dez dos 17 caracteres säo redundantes ou irrelevantes.

Nomes curtos geralmente slo. melhores contanto que sejam claros. No adicione mais
‘contexto a um nome do que 0 necessário.

‘Os nomes accountAdäress e customeradaress estio bons para instáncias da classe
“Address, mas seriam ruins para nomes de classes. Address está bom para uma classe. Se precisar
diferenciar entre enderegos MAC, enderegos de portas e enderegos da Web, uma ideia seria
Postaladáress, MAC € URI. Os nomes resultantes sdo mais precisos, motivo esse da tarefa de
se atribuir nomes,

Conclusáo

‘O mais dificil sobre escolher bons nomes éanecessidade de se possuirboas habilidades de descrigäo
‘eum histórico cultural compartilhado. Essa € uma questio de aprender, endo técnica, gerencial ou
empresarial. Como consequéncia. muitas pessoas nessa área náo aprendem essa tarefa muito bem.

30 Capitulo 2: Nomes Signiicativos

Elas também tém receio de renomear as coisas por temer que outros desenvolvedores sejam
contra. No compartilhamos desse medo e achamos que ficamos realmente agradecidos quando
os nomes mudam (para melhor). Na maioria das vezes, nlo memorizamos 0s nomes de classes
€ métodos. Mas usamos ferramentas modernas para lidar com detalhes de modo que possamos
nos focalizar e ver se o código & ido como parägrafos, frases, ou pelo menos como tabelas e
estatura de dados (uma fase nem sempre € a melhor forma de se exiir dados) Provavelmente
vocé acabará surpreendendo alguém quando renomear algo, assim como qualquer outra melhoria
mo código. Nao deixe que isso atrpalh seu progress.

Siga alguma dessas regras e note se vor? ndo melhorou a legibilidad de seu código. Se estiver
fazendo a manutengio do código de outra pessoa, use ferramentas de refatorado para ajudar a
resolver essas questdes. Em pouco tempo valeri a pens, e continuard avale em longo pra.

3

ungöes

Nos primórdios da programagdo, formávamos nossos sistemas com rotinas e sub-rotinas,
Já na era do Fortran e do PL/1, usävamos programas, subprogramas € fungdes. De tudo
isso, apenas fungáo prevalece. As fungdes sdo a primeira linha de organizaçäo em qualquer
programa, Eserevé-las bem é o assunto deste capítulo.

2 ‘Capitulo 3: Fungdes

Veja o código na Listagem 3.1. É dificil encontrar uma fungdo grande em FitNesse’, mas
:urando um pouco mais encontramos uma. Além de ser longo, seu código é repetido, há
diversas strings estranhas e muitos tipos de dados e APIS esquisitos e nada óbvios. Veja o quanto
vocé consegue comprender nos próximos très minutos.

Listagem 3-1
HemiUeil.java (Plenos

20070619)

Public static String testablewtal
PageData pagedata,
‚esultesetup

luda

FageCrawleriapl.get Inher! vedPage(
‘Suiteteaponder. SUITE. SETUP NAME, wikiPsge

suitesetup 12 null) {
Kipagerath. pagerath =
sultesetup.getPageCraslor() gatfullPathisuiteSetup) y
String pageräthlane = Pathbarcer. render ipagePathi;
buffer.append("tineluce setup *)

sppend (pagevathlaze)

0

3
Wikipage setup =
PagoCrawering] gee InheritedPage {#56

opt, wikivagel à

if (setup t= null (
ikiragepacn setuppach
ki Page get Pagecraver() .gecFull Pach (setup);
Satiprachlane - FathParser render (setupeath) à
‚append("Hinelude -seup »
appendsetuprattiiane)

N
buffer. appenälpageData.getcontent {|};
it (pagebata.hasättribite("Test")) |
wiki page tordo
FegeCtavier isp ge lnherLcedPege "TearDenm

wikiragel:

viksPage.geteagecravlor

string tearbownPathiane

butter append *\n"
‘append * include -teardom +")
‘append |cearbomPachvane)
append "in

jetFull Path (teardown) +
PathParser, render (tearDownfath) à

PEC APRA TERN

Funçôes ES

Listagem 3-1 (continnagäo)
HemlUtil.java (FitNesse 20070619)

EE (ineludesuitesetup) (
Wwikipage suiteTeardom
Page au er pl 9
‘Sultekesponder.s

wikiPage

1£ [ouiteteardown t= 1
Wikipagerach pagepath =
sulteteardown get PageCrawler() .getFulleath (euiteTeardonm) ;
string pagerat Pathrarser. render [pagePath +
buffer di

Dagedeta.sercentene (buffer. cos
eturn pageDara.gethtnl);
)

ang)

Conseguiu entender a funçäo depois desses trs minutos estudando-a? Provavelmente nfo.
Hé muita coisa acontecendo lá em muitos niveis diferentes de. Há strings estranhas e chamadas
a funçôes esquisitas misturadas com dois 4 £ aninhados controlados por flags.

Entretanto, com umas poucas extragdes simples de métodos, algumas renomeagdes e um
pouco dereestuturagdo fui capaz de entender o propósito da fungio nas nove linhas da Listagem
32. Veja se vocé consegue compreender tambén em trés minutos.

Listagem 32
memlutil.java (refatorado)

publi static ‘renderPagekicnser:
PageData pagedata, boolean “suite
à threxs Except ion [|
boolesn istesttage = pageata.hanktt
(letesteage) |
rage testPage = pagebata.getikifage();
Seringbutfer nenragecontent = new StringBuffer
IneluseseruppagesitestPage, nexfageContent, isSui
‘pewPageContent append pagebara.getcontent()) 1
ineludeTeardowmpages (vest Page, nenPagecontent, isSuite) :
pagedata.getContent (newPageconcent.tostring()}:

andre donna

return pageData.getital():

a Capítulo 3: Funçôes

‘A menos que ja estivesse estudando o FitNesse, provavelmente vocé näo entendeu todos
os detalhes.

‘Ainda assim vocé talvez tenha compreendido que essa funcio efetua a inclusäo de algumas
páginas SetUp e TearDown em uma página de teste e, entdo, exibir tal página em HTML. Se
estiver familiarizado com o JUnit, vocé já deve ter percebido que essa fungo pertenga a algum
tipo de framework de teste voltado para a Web. E vocé está certo. Deduzir tal informagdo da
Listagem 3.2 é muito fácil, mas ela está bastante obscura na Listagem 3.1

Entäo, o que toma uma fungo como a da Listagem 3.2 fácil de ler e entender? Como fazer
‘uma fungdo transmitir seu propósito? Quais atributos dar ás nossas fungdes que permitirdo um
Leitor comum deduzir o tipo do programa ali contido?

Pequenas!

A primeira regra para fungdes é que clas devem ser pequenas. A segunda € que precisam ser
mais espertas do que isso. Nâo tenho como justificar essa afirmaçäo. Nao tenho referéncias de
pesquisas que mostrem que fungöes muito pequenas sáo melhores. Só posso dizer que por cerca
de quatro décadas tenho criado fungdes de tamanhos variados. Já eserevi diversos monstros de
3.000 linhas; bastantes fungdes de 100 a 300 linhas; e fungdes que tinham apenas de 20 a 30
Tinhas. Essa experiéncia me ensinou que, ao longo de muitas tentativas cerros, as fungdes devem
ser muito pequenas.

Na década de 1980, costumávamos dizer que uma funcio ndo deveria ser maior do que a tela

É claro que na época usávamos as telas VT100, de 24 linhas por 80 colunas, e nossos editores.
‘usavam 4 linhas para fins gerenciamento, Hoje em dia, com fontes reduzidas e um belo e grande
monitor, vocé consegue colocar 150 caracteres em uma linha ~ ndo se deve ultrapassar esse
limite umas 100 linhas ou mais por tela—as fungdes nâo devem chegar a isso tudo, clas
devem ter no máximo 20 linhas.

(© quo pequena deve ser uma füngäo? Em 1999, fui visitar Kent Beck em sua casa, em
‘Oregon. EUA. Sentamo-nos e programamos um pouco juntos. Em certo ponto, ele me mostrou
um simpático programa de nome Java/Swing o qual cle chamava de Sparkle, Ele produzia na
{ela um efeito visual similar a uma varinha mágica da fada madrinha do filme da Cinderela
‘Ao mover o mouse, faiscas (sparkles, em inglés) cafam do ponteiro do mouse com um belo
cintilar até o fim da janela, como se houvesse gravidade na tela. Quando Kent me mostrou o

código, fiquei surpreso com tamanha pequeneza das fungdes. Eu estava acostumado a füngöes
que seguiam por quilömetros em programas do Swing. Cada fungdo neste programa tinha apenas
duas, ou trés, ou quatro linhas. Essg deve ser o tamanho das suas fungdes'

© quiio pequenas devem ser suas fungdes? Geralmente menores do que a da Listagem 3.2!
Na verdade, a Listagem 3.2 deveria ser enxuta para a Listagem 33,

aga Apenas uma Coisa 35

Listagem 3-3
HemlUeil java (refatorado novanente)

rageoata pageData, boolean issuite) ti
estrago (pagedatal)

Blocos e Endentagäo

Aqui quero dizer que blocos dentro de instrugdes if, else, while € outros devem ter apenas
‘uma linha, Possivelmente uma chamada de funçäo. Além de manter a funçäo pequena, isso.
adiciona um valor significativo, pois a funcio chamada de dentro do bloco pode receber um
nome descritivo. Isso também implica que as fungdes ndo devem ser grandes € ter estruturas
aninhadas. Portanto, o nivel de endentagáo de uma füngdo deve ser de, no máximo, um ou dois.
1550, é claro, facilita a leitura e compreensto das fungdes.

Faça Apenas uma Coisa

Deve ter ficado claro que a Listagem 3.1 faz muito mais
de uma coisa. Ela cria buffers, pega páginas, busca por
páginas herdadas, exibe caminhos, anexa strings estranhas
e gera HTML, dentre outras coisas. A Listagem 3.1 vive
“ocupada fazendo diversas coisas diferentes. Por outro
lado, a Listagem 3.3 faz apenas uma coisa simples. Ela
inclui SetUp e TearDown em páginas de teste.

O conselho a seguir tem aparecido de uma forma ou
de outra por 30 anos ou mais.

AS FUNGOES DEVEM FAZER UMA COISA. DEVEM FAZE-LA BEM.
DEVEM FAZER APENAS ELA.

© problema dessa declaragdo é que € dificil saber o que € “uma coisa”. A Listagem 3.3 faz
uma coisa? E fácil dizer que ela faz trés:

1. Determina se a página é de teste.

2. Se for, inclui SetUps e TearDo
3. Exibe a página em HTML.

nto, uma ou trés coisas? Note que os trés passos da funçäo esto em um nivel de abaixo do
nome da fungdo. Podemos deserever a funçäo com um breve parágrafo TO"

36 Capítulo 3: Funçôes

TO RenderPageWithSetupsAndTeardowns, verificamos se a página & de teste, se for.
incluimos Setlips e TearDowns. Em ambos os casos, exibimas a página em HTML.

‘Se uma funçäo faz apenas aqueles passos em um nivel abaixo do nome da fungáo, entäo ela
está fazendo uma só coisa. Apesar de tudo, o motivo de criarmos funçäo € para decompor um
‘conceito maior (em outras palavras, o nome da funçäo) em uma série de passos no próximo nivel
de abstraçao.

Deve estar claro que a Listagem 3.1 contém passos em muitos niveis diferentes de . Portanto.
‘obviamente ela faz mais de uma coisa, Mesmo a Listagem 3.2 possui dois niveis de abstraçäo
. como comprovado pela nossa capacidade de redugäo. Mas ficaria muito dificil reduzir a
Listagem 3.3 de modo significativo. Poderiamos colocar a instruçäo 1 £ numa funçäo chamada
AncludeSetupsandreardownsif7est Page, mas isso simplesmente reformula o código, sem
modificar o nivel de

Portanto, outra forma de saber se uma fungáo faz mais de “uma coisa” € se vocé pode
cextrair outra fungáo dela a partir de seu nome que ndo seja apenas uma reformulaçäo de sua

mplementacdo (G34),

Segöes Dentro de Funçôes

Veja a Listagem 4.7 na página 71. Note que a füngdo generatePrimes está dividida em
segdes, como declaragdes, inicializagdes e selecdo. Esse é um indicio dbvio de estar fazendo
mais de uma coisa. Nao dä para, de forma significativa, dividir em segdes as funges que
fazem apenas uma coisa.

Um Nível de Abstraçäo por Funçäo

A fim de confirmar se nossas fungdes fazem só “uma coisa”. Precisamos verificar se todas as
instrupdes dentro da fungáo estio no mesmo nivel de abstragäo. É fácil ver como a Listagem 3.1
viola essa regra. Há outros conceitos lá que esto em um nivel de bem alto, como 0 gettem2 ()

‘outros que esto em um nivel intermediário, como String pagePathName = PathParser.
render (pagePath); e outros que esto em um nivel consideravelmente baixo, como
-append(*\n").

‘Varios niveis de dentro de uma funglo sempre geram confusio, Os leitores podem nto
‘conseguir dizer se uma expresso determinada € um conceito essencial ou um mero detalhe. Por,
‘como janelas quebradas, uma vez misturados os detalhes aos conceitos, mais e mais detalhes.
tendem a se agregar dentro da fungdo.

Estrutura Switch

Ler o Código de Cima para Baixo: Regra Decrescente

¡Queremos que o código seja lido de cima para baixo, como uma narrativa". Desejamos que
cada fungáo seja seguida pelas outras no próximo nivel de de modo que possamos ler o
programa descendo um nivel de de cada vez conforme percorremos a lista de fungdes.
Chamamos isso de Regra Decrescente.

Em outras palavras, queremos poder ler o programa como se fosse uma série de
parigrafos TO, cada um descrevendo o nivel atual de e fazendo referéncia aos parágrafos
TO consecutivos no próximo nivel abaixo.

Para incluir SetUps e TearDowns, incluimos os primeiros, depois o contetido
da página de teste e, entäo, adicionamos os segundos. Para incluir SerUps,
adicionamos o suite setup, se este for uma colecdo, incluimos o setup normal.
Para incluir o suite setup, buscamos na hierarquia acima a página “SuiteSetUp”
e adicionamos uma instrucáo de inclusdo com o caminho áquela página. Para
procurar na hierarquia acima...

“Acaba sendo muito difícil para os programadores aprenderem a seguir essa regra e criar
funçôes que fiquem em apenas um nivel de . Mas aprender esse truque é também muito
importante, pois ele é o segredo para manter as funçües curtas € garantir que fagam apenas
“uma coisa”, Fazer com que a leitura do código possa ser feta de cima para baixo como uma.
série de parigrafos TO é uma técnica eficiente para manter o nivel de consistente.

Vejaa listagem 3.7 no final deste capitulo. Ela mostra toda a fungdo testableHuml refatorada
de acordo com os principios descrito aqui, Note como cada fungdo indica a seguinte e como
cada uma mantém um nivel consistente de

Estrutura Switch

É dificil criar uma estrutura switch pequena”, pois mesmo uma com apenas dois cases é maior
do que eu gostaria que fosse um bloco ou uma füngäo. Também € dificil construir uma que fala
apenas uma coisa, Por padrio, as estruturas switch sempre fazem N coisas. Infelizmente, nem
‘sempre conseguimos evitar o uso do switch, mas podemos gos certificar se cada um está em
uma classe de baixo nivel e nunca € repetido, Para isso, usamos o polimorfismo,

Veja a Listagem 3.4. Ela mostra apenas uma das operagdes que podem depender do tipo
de funcionário (employee, em inglés).

37

38 Capítulo

Listagem 3-4
Payroll. java

public Money calculateray[Enployee ej
throws 2 oveetype €

itch
case HOURLY
return caiculatetiourlyPay (el:
cate SHAR
‘caleulates

ray le)

throw naw Inval deeployeetypete.tyee) +

Esta funçäo tem varios problemas. Primeiro, ela € grande, e quando se adiciona novos tipos de
funcionários ela crescerá mais ainda. Segundo, obviamente ela faz mais de uma coisa. Terceiro,
ela viola o Principio da Responsabilidade Unica’ (SRP, sigla em inglés) por haver mais de um
‘motivo para alteré-la. Quarto, ela viola o Principio de Aberto-Fechado* (OCP, sigla em inglés),
pois precisa ser modificada sempre que novos tipos forem adicionados. Mas, provavelmente, o
pior problema com essa fungio é a quantidade ilimitada de outras fungóes que terdo a mesma
estrutura. Por exemple, podríamos ter

isPayday (Employee e, Date date)
ou
deliverbaylEmployee e, Money pay)

ou um outro grupo. Todas teriam a mesma estrutura deletéri

A soluçäo (veja a Listagem 3.5) € inserir a estrutura switch no fundo de uma ABSTRACT
FACTORY’ e jamais deixar que alguém a veja. A factory usará o switch para criar instáncias
apropriadas derivadas de Employee, e as füngdes, como calculateray. isPayday €
deliverPay, serdo enviadas de forma poliförmica através da interface Employee.

Minha regra geral para estruturas switch € que so aceitaveis se aparecerem apenas uma vez,
como para a criagäo de objetos polifórmicos, e estiverem escondidas aträs de uma relagdo de
heranga de modo que o resto do sistema ndo possa enxergä-la [G23]. claro que cada caso é um
caso e haverä vezes que ndo respeitarei uma ou mais partes dessa regra.

Listagem 3-5 Pi
Employee e Factory

‘bic abstract clase Employee {

ube abstract boolean ¡sPayday(;

public abstract Money calculatePay()

publ abstract vor) delerPay(Money pay)

}

{Use Nomes Desertivos 39

Use Nomes Descritivos

Na Listagem 3.7, eu mudei o nome do exemplo de nossa funçlo restableHenl para
SetupTeardownIncluder. render, que & bem melhor, pois descreve o que a funçäo faz.
“Também dei a cada método privado nomes igualmente deseritivos, como isTestable ou
AncludeSetupandTeardownPages. É dificil superestimar o valor de bons nomes. Lembre-se
do princípio de Ward: “Vocé sabe que está criando um código limpo quando cada rotina que
1006 16 € como vocé esperana”. Metade do esforgo para satisfazer esse principio € escolher bons
nomes para fungdes pequenas que fazem apenas uma coisa. Quando menor e mais centralizada
for a funglo, mais fácil será pensar em um nome descritivo.

Näo tenha medo de criar nomes extensos, pois eles so melhores do que um pequeno e
enigmático. Um nome longo e deseritivo é melhor do que um comentário extenso e deseritivo.
Use uma convençäo de nomenclatura que possibilite uma fâcil leitura de nomes de fungdes com
várias palavras e, ento, use estas para dar à funçäo um nome que explique o que ela faz.

Nao se preocupe com 0 tempo ao escolher um nome. Na verdade, vocé deve tentar varios
nomes e, entio, ler o código com cada um deles. IDEs modemas, como Eclipse ou InteliJ,
facilita a troca de nomes. Utilize uma dessas IDEs e experimente diversos nomes até encontrar
um que seja bem deseritivo.

Selecionar nomes descritivos esclarecerá o modelo do módulo em sua mente e the ajudará a
melhori-lo. É comum que ao buscar nomes adequados resulte numa boa reestruturagäo do código.

Seja consistente nos nomes, Use as mesmas frases, substantivos e verbos nos nomes de fungdes.
de seu módulo. Considere, por exemplo, os nomes includesetup-AndTeardownPages,
includesetupPages, includesuiteseruppage € includesetupPage. A fraseologia
nesses nomes permite uma sequéncia de fäcil deduçäo. Na verdade, se eu Ihe mostrasse
apenas a série acima, vocé se perguntaria: “O que aconteceu com includeteardownPages,
includeSuiteTeardowPage e includeTeardownPage?”, como isso € *... como o que
oc esperava?”.

Parámetros de Funçôes

A quantidade ideal de parámetros para uma funçäo
é zero (nulo). Depois vem um (mönade), seguido de
dois (díade). Sempre que possivel devem-se evitar
res parámetros (riade). Para mais de trés deve-se ter
um motivo muito especial (poliade) ~ mesmo assim
o devem ser usados.

Parámetros sio complicados. Eles requerem
bastante conceito. É por isso que me livrei de
quase todos no exemplo. Considere, por exemplo,
o StringBuffer. Poderíamos télo passado
como parámetro em vez de instanciá-lo como
uma variável, mas entlo nossos leitores teriam
de interpretá-lo sempre que o vissem. Ao ler a

40 Capitulo 3: Funçoes

estória contada por pelo módulo, fica mais fácil entender ¿ncludesctuprage!) do que
includeSetupragelnto (newPage-Content). O parimetro nio está no nivel de que o
nome funçäo, forgando-Ihe reconhecer de um detalhe (ou seja, o StringBuffer) que näo seja
importante particularmente naquele momento,

Os parámetros slo mais dificeis ainda a partir de um ponto de vista de testes. Imagine a
dificuldade de escrevertodos os casos de teste para se certificar de que todas as várias combinagdes
de parámetros funcionem adequadamente. Se nao houver parámetros, essa tarefa é simples. Se
houver um, ndo € to dificil assim.

Com dois, a situaçäo fica um pouco mais desafiadora. Com mais de dois, pode ser
desencorajador testar cada combinaçäo de valores apropriados. Os parámetros de salda so ainda
mais dificeis de entender do que os de entrada. Quando lemos uma funçäo, estamos acostumados
à ideia de informagdes entrando na funçäo através de parámetros e saindo através do valor
retornado. Geralmente no esperamos dados saindo através de parámetros. Portanto, parámetros
de saida costumam nos deixar surpresos e fazer com que Iciamos novamente.

Um parámetro de entrada é a melhor coisa depois de zero parámetro. É fácil entender
SetupTeardown-Includer. render (pageData). Está óbvio que renderizemos os dados
no objeto pageData.

Formas Mónades Comuns

Há duas razdes bastante comuns para se pasar um único pardmeto a uma funçäo. Voce pode estar
fazendo uma pergunta sobre aquele parimetro, como em boolean fileBxists(*MyPile"). Ou
vocé pode trahalhar naquele parámetro ransformando-0 em outra coisa eretomando-o. Por exemplo,
InputStream filedpen ("MyPie") transforma a String do nome de um arquivo em um valor
retomado por InputStream. Sao eses dois usos que os Litres esperam ver em uma fungi,

Vocé deve escolher nomes que tomem clara a disingdo, e sempre use duas formas em um
contexto consistente. (Veja a seguir Separacao comando-consulia). Uma forma menos vomum
mas ainda bastante itil de um parámetro para uma fundo é um evento. Nesta forma, há um
parámetro de entrada, mas nenhum de sada. O programa em si serve para interpreta a chamada da
fungZo como um evento, e usar o parimetro para alterar 0 estado do sistema, por exemplo, void
passwordactemptrai leditimes(int_attempto). Use esse tipo com cautela Deve ficar
claro para leitor que se tata de um evento. Escolha os nomes e 0s contextos com aten.

“Tente evitar funçoes mönades que ndo sigam essas formas, por exemplo, void include
Setupragelnto(StringBuffor pageText). Usar um parimetro de saida em vez de
um valor de retomo para uma modificaäo fica confiso. Se uma fungJo vai transformar seu
parámetro de entrada, a alterago deve aparecer como o valor retomado. De fato, Str ingbutfer
trans form(StringBuffer im) é melhor do que void tranaforn-(Stringbuffor
out), mesmo que a implementagdo do primero simplesmente retome o parámetro de entrada
Pelo menos ee ainda segue o formato de uma modificaçäo.

Parámetros Lógicos

Esses parámetros so feios. Passar um booleano para uma fungáo certamente é uma prática horrível,
pois ele complica imediatamente a assinatura do método, mostrando explicitamente que a fungio
faz mais de uma coisa. Ela faz uma coisa seo valor for verdadeiro, outra se for falso!

{Use Parámetros de Fungöes a

Na Listagem 3.7, ndo tinhamos alternativa, pois os chamadores já estavam pasando
aquela flag (valor booleano) como parámetro, e eu queria limitar o escopo da refatoraçäo à
funçäo e para baixo. Mesmo assim, a chamada do método render (true) & muito confusa
para um leitor simples. Analisar a chamada e visualizar render (boolean isSuite) ajuda
um pouco, mas nem tanto, Deveriamos dividir a fungdo em duas: renderForSuite() €
renderForSingleTest()

Funçôes Díades

Uma füngdo com dois parámetros € mais dificil de entender do que uma com um (mônade).
Por exemplo, € mais ficil compreender writeFieldinane) do que writeField(output-
Stream, name)", Embora o significado de ambas esteja claro, a primeira apresenta seu propósito
explicitamente quando a lemos. A segunda requer uma pequena pausa até aprendermos a ignorar 0
primeiro parámetro. E isso, é claro, acaba resultando em problemas, pois nunca devemos ignorar
‘qualquer parte do código. O local que ignoramos é justamente aonde se esconderäo os bugs.

Hé casos, € claro, em que dois parámetros sio necessirios como, por exemplo, em Point >
= new Point (0,0). Os pontos de eixos cartesianos naturalmente recebem dois parámetros.
De fato, ficariamos surpresos se vissemos new Point (0). Entretanto, os dois parámetros neste
caso sdo componentes de um único valor! Enquanto que output-Stream € name náo sáo
partes de um mesmo valor.

Mesmo fungôes diades óbvias, como assertEquals (expected, actual), so
problemática.

Quantas vezes vocé já colocou actual onde deveria ser expected? Os dois parámetros nño
possuem uma ordem pré-determinada natural. A ordem expected, actual é uma convengdo que
requer prática para assimild-Ia.

Diades nio sio runs, € vocé certamenteterá de usd-las. Entretanto, deve-se estr ciente de que
haverá um prego a pagar c, portant, deve-se pensar em tirar proveito dos mecanismos disponiveis
a vocé para converté-los em mónades. Por exemplo, vocé poderia tomar o método writeField
um membro de outputStrean de modo que pudesse dizer output StreanwriteField (name) ;
tomar output stream uma variável membro da classe em uso de modo que náo precisasse passálo
por parámetro; ou extrair uma nova clase, como Pelar ter, que receba o outputStream em seu
constrtor e possua um método write.

Tríades

Fungdes que recebem trés parámetros sto consideravelmente mais difieeis de entender do
que as diades. A questo de ordenagäo, pausa e ignoragdo aßresentam mais do que o dobro de
dificuldade. Sugiro que vocé pense bastante antes de ciar uma ride.

Por exemplo, considere a sobrecarga comum de assertEquals que recebe trés parámetros

assertequals (message, expected, actual). Quantas vezes voce precisou ler o
parámetro message e deduzir o que ele carrega? Muitas vezes j me deparei com essa trade em
particular e tive de fazer uma pausa, Na verdade, toda vez que a vejo,teho de ler novamente e,
entdo, a ignoro,

Use Parámetros de Funçües 5

formato imbuimos os nomes dos parámetros no nome da füngdo. Por exemplo, pode ser melhor
escrever assertEquale do que assertExpectedEqualsActual (expected, actual), o
que resolveria o problema de ter de lembrar a ordem dos parámetros.

Evite Efeitos Colaterais

Ets colterais sdo mentiras, Sua funço promet fazer penas uma cos, mas ela tambén faz
outras cosas escondida. As vezes, la fark ateragdes inesperadas nas variáeis de ua pröpria
classe. As vezes, ela adicionar as varidves aos parimetrs passados funcio ou ds globais
do sistema. Em ambos 0s casos elas slo “verdades” enganosas € prejudicias, que gealmente
resalta em acoplamentos temporiiosestranbos e dependéncis.

Considere, por exempl, a funso aparentemente inofensiva na Listagem 3.6. Ela usa um
aigorimo padrio para comprar um userNane (nome de ususrio) a um password
(sens). Elaretoma true (verdadeire) se forem iguais, e false (£810) caso conri.
Mas hi também um efit colateral Consegue identifico?

Listagem 3-6
uservalidator. java

¡ptographecz

ring codedPhrase raseencodedbyFassword()
ceryptographer.decrypt (codedPhrase, password)
id Password" equals(phrase)) |

O efeito colateral € a chamada a0 Session. initialize(), € claro. A funglo
checkPaseword, segundo seu nome, diz que verifica a senha. O nome náo indica que ela
inicializa a sessdo. Portanto, um chamador que acredita no que diz o nome da funçäo corre o
risco de apagar os dados da sessao existente quando ele decidir autenticar do usuario.

Esse efeito colateral cria um acoplamento temporário. Isto €, checkPassword só poderá ser
chamado em determinadas horas (em outras palavras, quando for seguro inicializar a sessdo). Se
or chamado fora de ordem, sem querer, os dados da sessäo poderäo ser perdidos. Os acoplamentos
temporärios so confusos, especialmente quando sdo um efeito colateal. Se for preciso esse tipo

“4 Capítulo 3: Funçôes

de acoplamento, é preciso deixar claro no nome da funçäo. Neste caso, poderiamos renomear
a füngdo para checkPasswordAndinitializesession, embora isso certamente violaria o
“fazer apenas uma única coisa”.

Parámetros de Saída
Os parámetros so comumente interpretados como entradas de uma fungi. Se jé usa o programa

há alguns anos, estou certo de que vocé já teve de voltar e ler novamente um parámetro que cra,
na verdade, de saída, e näo de entrada. Por exemplo:

appendFooter(s);

Essa funçäo anexa 9 como rodapé (Footer, em inglés) em algo? Ou anexa um rodapé as? a &
‘uma entrada ou uma saida? Nao precisa olhar muito a assinatura da fungäo para ver:

public void appendFooter (StringBuffer report)

1550 esclarece a questäo, mas à custa da verificagdo da declaraçäo da funçäo. Qualquer coisa
que Ihe force a verificar a assinatura da fungto é equivalente a uma relida. Isso é uma interrupgäo
do raciocinio e deve ser evitado.

‘Antes do surgimento da programacdo orientada a objeto, ás vezes era preciso ter parámetros de
saida. Entretanto, grande parte dessa necessidade sumiu nas linguagens OO, pois seu propósito éservir
‘como um parámetro de sada. Em outras palavras, seria melhor invocar appendFooter como:

report.appendFooter();

De modo geral, devem-se evitar parámetros de
de algo, faga-a mudar o estado do objeto que a pertence.

Caso sua fungko precise alterar o estado

Separagáo comando-consulta

As funçües devem fazer ou responder algo, mas náo ambos. Sua fungáo ou altera o estado de
um objeto ou retorna informagdes sobre ele. Efetuar as duas tarefas costuma gerar confuso,
Considere, por exemplo, a funçäo abaixo:

public boolean setíString attribute, String value);

Esta funçäo define o valor de um dado atributo e retorna true (verdadeiro) se obtiver
éxito e false (£a150) se al atributo ndo existir. Isso leva a instrugdes estranhas como:

if (setí"username”, "unclebob"))...

Imagine isso pelo ponto de vista do leitor. O que isso significa? Está perguntando se o atributo.
“usernane” anteriormente recebeu o valor “unclebob”? Ou se “username” obteve éxito ao
receber o valor “unclebob”? É dificil adivinhar baseando-se na chamada, pois ndo está claro se
a palavra “set” € um verbo ou um adjetivo.

Profira excegöes a retorno de códigos de erro 4

© intuito do autor era que set fosse um verbo, mas no contexto da estrutura if, parece
um adjetivo. Portanto, a instrugdo lé-se “Se o atributo username anteriormente recebeu o valor
unclebob"endo”atribuzo valor une Lebob aoatributo username, e se isso funcionar, ent...”
Poderíamos tentar resolver isso renomeando a fungdo set para setAndcheckrf2xists, mas
no ajudaria muito para a legibilidade da estrutura 4

1£ tattribuceExiets(-username")) {
setattribute("username", “unclebob*);

>

Prefira excegöes a retorno de códigos de erro

Fazer fungdes retomarem códigos de erros é uma leve violaçäo da separagäo comando-consulta,
pois os comandos sto usados como expressdes de comparaçäo em estruturas if

if (deletePago(page) == E_0K)

© problema gerado aqui näo & a confusdo verbo‘adjetivo, mas sim a criagdo de estruturas
aninhadas.

‘Ao retomar um código de erro, vocé cria um problema para o chamador, que deverá lidar
imediatamente com o erro.

Af (deletepage(page) == E_OR) (
if (registry.deleteReference(page.name) == E_OK) (
LE (configkeys.deleteKey (page name.makexey()) == E_OK){
logger.log("página excluida’); E
) else {
logger.log(“configkoy nao foi exelufdar);
)
} else {
logger.log("deletereference nao foi excluído do
registro");

3
) else (
logger.log("a exclusdo falhou
return & ERROR:

1

Por outro lado, se vocé usar excegdes em vez de retornar códigos de erros, ento o código de
‘atamento de erro poderá ficar separado do código e ser simplificado:
try (
deletorageipage);
registry.deleteReference(page.name);
configkeys.deletexey (page.name.makeKey ()

)
catch (Exception e) {

logger. log(e.getMessage()):
>

46 Capitulo 3: Fungdes.

Extraia os blocos try/catch

Esses blocos ndo tém o direito de serem feios. Eles confundem a estrutura do código e misturam
© tratamento de erro com o processamento normal do código. Portanto, € melhor colocar as
estruturas czy € catch em suas pröprias fungdes.

public void delete(Page page) (
try 4
deleterageAndAllReferences (page);
à
catch (Exception ej {
Logerror(e):
3
>

priva

t

void deleterageAndAllReferences(Page page) throws Exception

deleterage(page);
registry.deleteReference(page-nane);
configkeys.deletekey (page.name.makeKey());


private void log8rror(Exception e) (
logger-log(e.getMessage());
)

A fungáo delete acima só faz tratamento de erro. E € fácil entendé-la e seguir adiante.
A fungio deleterageAndAliReferences só trata de processos que excluem toda uma
página. Pode-se ignorar o tratamento de ero. Isso oferece uma boa separagdo que facilita a
‘compreensio e alteragäo do código.

Tratamento de erro é uma coisa só

As fungöes devem fazer uma coisa só. Tratamento de erro € uma coisa só, Portanto, uma
fungo que trata de erros ndo deve fazer mais nada. Isso implica (como no cxemplo acima)
que a palavra try está dentro de uma funçäo e deve ser a primeira instruçäo e nada mais deve
virapós os blocos catch finally.

va, o chamariz A dependencia

Retomar códigos de erro costuma implicar que há classes ou enum nos quais estäo definidos
todos os códigos de erro, =

public enum Error {
ok,
INVALID,
NO_SUCH,
LOCKED,
OUT_OF_RESOURCES,
NALFING_FOR_EVENT:

Programacio estruturada a

Classes como esta sdo chamarizes à dependéncia, muitas outras classes devem importé-las
e usé-las, Portanto, quando o enum da classe Error enum, é preciso recompilar todas as outras
classes eredistribui-Ias Isso coloca uma pressäo negativa na classe Error. Os programadores nio
querem adicionar novos erros porque senäo cles teriam de compilar e distribuir tudo novamente,
Por isso, eles reutilizam códigos de erros antigos em vez de adicionar novos.

‘Quando se usam excegdes em vez de códigos de erro, as novas excegúes sáo derivadas da
classe de excegdes. Podem-se adicioná-las sem ter de recompilar ou redistribuir”,

Evite repetigáo»

Leia novamente com atençäo a Listagem 3.1 e notará
que há um algoritmo que se repete quatro vezes em
quatro casos: Setup, SuiteSetUp, TearDown €
‘Sui teTearDown, Näoéfécil perceberessaduplicagao,
pois as quatro instáncias esto misturadas com outros.
códigos e nfo esto uniformemente repetidas, Mesmo
assim, a duplicaçäo é um problema, pois ela amontoa
‘0 código e serio necessárias quatro modificagdes se o
algoritmo mudar. Além de serem quatro oportunidades
para a omissto de um erro,

Sanou-seessaduplicaçäo através do método include na Listagem3.7.Leiaestecédigonovamente
e note como a legibilidade do módulo intro foi methorada com a retirada de tas repetigdo.

A duplicagäo pode ser a raiz de todo o mal no software. Muitos principios e práticas tém
sido criados com a finalidade de controlá-la ou climiná-la. Considere, por exemplo, que todas
as regras de normalizagäo de bando de dados de Ted Codd servem para eliminar duplicagáo”
de dados. Considere também como a programagáo orientada a objeto serve para centralizar o
‘codigo em classes-base que seriam outrora redundantes. Programaçäo estruturada, Programagáo
Orientada a Aspecto e Programagdo Orientada a Componentes sño todas, em parte, estratégias
‘para eliminar duplicagäo de código. Parece que desde a invengäo da sub-rolina, inovagdes no
desenvolvimento de software tém sido uma tentativa continua para eliminar a duplicado de
nossos códigos-fonte.

Programagäo estruturada

Alguns programadores seguem as regras programaçäo estruturada de Edsger Dijkstra que disse
‘que cada funçäo e bloco dentro de uma füngdo deve fer uma entrada e uma saida. Cumprir essas
regras significa que deveria haver apenas uma instruçäo return na funçäo, nenhum break ou
continue num loop e jamais um goto.

Enquanto somos solidärios com os objetivos e disciplinas da programaçäo estruturada, tais
regras oferecem pouca vantagem quando as füngdes «io muito pequenas. Apenas em fungdes
maiores tais regras proporcionam beneficios significativos.

Portanto, se vocd mantiver suas fungdes pequenas, ento as värias instrugdes return, break
où continue casuais ndo traráo problemas e poderdo ser até mesmo mais expressivas do que

EEE OOOO PO NON ÓN

8 Capítulo 3: Funçües

a simples regra de uma entrada e uma saida. Por outro lado, o goto só faz sentido em funçües
grandes, portanto ele deve-se evitá-lo.

Como escrever funçôes como essa?

Criar um software & como qualquer outro tipo de escrita. Ao escrever um artigo, v
coloca seus pentamentos no papel e depois os organiza de modo que fuer fk
primeiro rscunho pode car desastroso desorganizado, eno voc o ape, recstrurra € refina
at que ele que como voce dese.

Quando escrevofungoes cls comegam longa e complexas; há muitasendentades e loops
anid; possum longas lisas de parámetros; os nomes so arbinris; e hi duplica de
código. Mas cu também tenho uma cole de testes de unidado que analisam cada uma dessas
lias desorganizadas do código.

Sendo assim, eu organizo reino o código, divido fangdes, troco os nome, elimino a
(uplicagäo, reduno os métodos eos reorganizo, As vers, desmonto clases incas, tdo com
os testes em exccuglo.

No final, minhas fungdes seguem as regras que cite neste capitulo, No as aplico desde o
inicio, Acho que iso no sea possivel

Conclusáo

Cada sistema € construido a partir de uma linguagem específica a um dominio desenvolvida por
programadores para descrever o sistema. As fungöes sio os verbos dessa linguagem,e classes
‘0s substantivos. Isso náo é um tipo de retomada da antiga nogäo de que substantivos e verbos
‘nos requerimentos de um documento sejam os primeiros palpites das classes e füngdes de um
sistema. Mas sim uma verdade muito mais antiga. A ate de programar €, e sempre foi, arte do
projeto de linguagem.

Programadores experientes veem os sistemas como historias a serem contadas em vez de
programas a serem escritos, Eles usam os recursos da linguagem de programaçäo que escolhem
ara construir uma linguagem muito mais rica e expressiva do que a usada para contara estria
Pare da linguagem especifica a um dominio € a hierarquia de fungdes que descreve todas as
agdes que ocorrem dentro daquele sistema. Em um ato engenhoso, escrevem-se essas funpdes
para usar a mesma linguagem específica a um dominio que eles criaram para contar sua propria
parte da história.

Este capitulo falou sobre a mecánica dese escrever bem fines Se segui as reas aqui descritas,
suas fungdes serio curas, bem nomeadas e bem organizadas. Mas jamais se esquega de qe seu
‘objetivo verdadeiro é contar a história do sistema, e que as füngöes que vocé escreverprecisam estar
‘em perfctasincronia e formar uma inguagem clara e precisa para Ihe ajudar na narra.

SetupTeardownlacluder

SetupTeardownIncluder

Listagem 3-7
SetupreardomnIncluder . java

package fitnesse. html:

import fitnesse.reeponders.run.SuiteResponders
import fitnesse.wikl.*;

public class SetupTeardomtacluder (
private Fagedate pageData;
private boolean issuite:
private WikiFage vestage:
private Stringtutfer nevPageContent
Private PageCravler pagecrawler;

public static String render (Fagedata pageDsta) throws Exception
return render(pagebeta, false)


public static Pagedata, boolean issuite)
throws Exception |
return new Setupteardowincluder (pageData) ‚render (isSuit

private Setupteardowlncluder (Pagevate pagedata) |
‘his.pagebats ~ pagebata;
testiage » pagedata.getiikiPaget}:
pagecrawler = testFage.getPageCrawler()
BewPagecontent = new Stringbuffer();

private suring render (botean Louise) throw Steepcion (
this.isouite » issuiter
Ef tletesePaget))
includeSetupAndesrdownPages();
return pagebata.gecätal()7

private boolean istestPage() throws Exception {
return pagedsta.hasAttribute( Test");

private void includesetupandteardomPages() throws Exception |
{includesecupPages ()
include?agecontent 1
includeTeardownPages 1;
upsaceragecontent 1);

)

Capítulo 3: Fungdes

Listagem 3-7 (continungäo):
SetupTeardownincluder.java

Private void includeSetupfages() throws Exception (
SE ssuite)
inciudesuiceserupraget);
ineludeSetupfage():

private void includesuiteSerupPage() throws Recept
inelude(Suitehesponder. SUTTE_SETUP NAHE, *-secup”) à

>

private void includesetuprage(1 throws Exception {
ineludet*setupt, *-setuprl;

>

private void includePagecontent() throws Exception {
‘newPagaContent append pageDuta.getContent());
>

private void ineludoTeardownFages!) throws Exception {
inelusereardomrage );
if (ssuitel
includeSuiteTeardownPage();
>

private void include
include (*PearDown"


private void includeSuteteardomPage() throws Exception
{nelude(Seitekesponder SUITE TEAADIN NAME, *-teardow:
;

private void updat
‘pagebata.setConter
)

private void inelude(string pagelane, string arg) throws Exception (
wikitage inheritedfage = tinäinheritodrage (pagelane);
if (imberitedrage != mal) (
string pageratilane = getPachlaneforfage(inheritedrage) ;
builateeluaeDirective(pagePachilane, arg)


’ x

private WikiPage findinheritedPage(String pageline) throws Exception À
return Pagecrawlerinpl get tnhoritedPage pagellane, tostPage)
1

rageconten (} throws Excepción |
newPageContent .2oString())

private String getPachianeForPage(WikiFage page) throws Exception {
ikiPogePath pagebsth = pageCravler.geeFullPath (page) 7
return Pathparser. render {pago2ath)

>

private void bulldineludsbirectiveistring pagerathiane, String arg) |
‘ewFagetontent
‘appendi*int include +)

4

Comentarios

‘Nao insira comentários num código ruim, reescreva-o”.
Brian W. Kemighan e. J. Plaugher

Nada pode ser o útil quanto um comentário bem colocado. Nada consegue amontoar um
módulo mais do que comentários dogmáticos e supérfluos. Nada pode ser o prejudicial quanto
um velho comentário mal feito que dissemina mentiras e informagóes incorretas.

Comentários nao säo como a Lista de Schindler. Nao slo o “bom puro". De fato, eles sto,
no máximo, um mal necessärio. Se nossas linguagens de programagáo fossem expressivas 0
suficiente ou se tivéssemos o talento para manipular com destreza tai linguagens de modo a
expressar nossa intençäo, no precisaríamos de muitos comentários, quigá nenhum,

54 Capítulo: Comentários

© uso adequado de comentários é compensar nosso fracasso em nos expressar no código.
‘Observe que usei a palavra fracasso. E € isso que eu quis dizer. Comentários säo sempre fracassos.
Devemos usé-los porque nem sempre encontramos uma forma de nos expressar sem eles, mas
seu uso ndo é motivo para comemoracáo.

Entio, quando vocé estiver numa situado na qual precise criar um comentário, pense bem
€ veja se nao há como se expressar através do código em si. Toda vez que vocd fizer isso, dé em
si mesmo um tapinha de aprovagdo nas costas. Toda vez que vocé escrever um comentário, faga
‘uma careta € sinta o fracasso de sua capacidade de expressäo.

Por que sou nio gosto de comentários? Porque eles mentem. Nem sempre, e nio
intencionalmente. mas é muito comum. Quanto mais antigo um comentário for e quanto mais
longe estiver do código o qual ele descreve, mais provável será que esteja errado. O motivo &
simples. Nao é realístico que programadores consigam manté-los atualizados.

Códigos mudam e evoluem. Movem-se blocos para lá e paracá, quese bifurcam e se reproduzem
‘ese unem novamente, formando monstros gigantescos. Infelizmente, os comentários nem sempre 03
seguem — nem sempre é possivel. E, muito frequentemente, os comentários ficam longe do código
o qual deserevem e se tornam dizeres órfios com uma exatidäo cada vez menor. Por exemplo, othe
‘0 que aconteceu com o comentário abaixo € a linha que ele procurava descrever:

MockRequest request;

private final String HTTP_DATE_REGEXD =
* (SMTP! fa~2] (2)\\,\\s{0-9)(2)\\s|[FHASOND] [2-2] (2)\\3"+
“(0-91 (4)\\s(0-9) 2\\:(0-9}(2)\\:10-91 2 SGMT

private Response responsi

private FitNesseContext context:

private FileResponder responder:

private Locale saveLocale;

71 Exemplo: “Tue, 02 Apr 2003 22:18:49 GMT”

‘ous insincias de variéveis que provavelmente foram aicionada posteriormente fiaram
care constante #P7P_DATE_ROGEX®e eu comentio deserve.

Y possivel dizer que os programadores deveria ser disciplinados o bastante para manter
cs comentários em um clevado estado de atunizado, riovänci preciso. Concorde que
dleveriam, Mas cu preferiria que essa energa fossediecionadapara tomar o código to claro €
descrito que de inicio nem se preisri de comentários

Comentrios imprecisos sto muito pires do que nenhum. le enganam e idem; deixam
expectativas que jamais sero cumprdas; tam regras antigas que ndo preci cundo
‘deveriam, ser seguidas "

Soc pode encontar a verdade em um lugar no código S le pode realmente fe dizer 0 que
cle fr. Ele a única fone de inormagóss vedadeimmente precisas. Entretanto, emborn is vezes
comentários sejam necessrios,gastriamos energin consderävel para minimizá los

Comentários Compensam um Código Ruim

‘Uma das motivagdes mais comuns para criar comentários & um código ruim. Construimos um
módulo e sabemos que está confuso e desorganizado. Estamos cientes da bagunga. Nós mesmos
dizemos “Oh, € melhor inserir um comentärio!”. Nao! E melhor limpé-lo.

#

Comentarios Bons

Códigos claros ¢expressives com poucos comentários so de longe superiores aumamontoado
«e complexo com muitos comentários. Ao invés de gastar seu tempo criando comentários para
explicar a bagunga que vocé fez, use-o para limpar essa zona,

Explique-se no código

Certamente há vezes em que nto é possivel se expressar di
A isso, muitos programadores assumiram que o código raramente é, se € que possa ser, um bom
‘meio para se explicar. Evidentemente isso é falso. O que vocé preferiria ver? Isso:

// Verifica se o funcionario ten direito a todos os beneficios
if ((employee.fags & MOURLY_FLAG) ús
lemployee.age > 65)

Ou isso?
i£ (employee. isFligiblerorFullBenefts())
Só é preciso alguns segundos de pensamento para explicar a maioria de sua intengáo no

código. Em muitos casos, & simplesmente uma questäo de criar uma fungäo cujo nome diga a
‘mesma coisa que vocé deseja colocar no comentário.

Comentários Bons

Certos comentários sdo necessários ou benéficos, Veremos alguns que considero valerem 0s
bits que consumen. Tenha em mente, contudo, que o único comentário verdadeiramente bom &
aquele em que voeé encontrou uma forma para náo escrevé-lo,

Comentários Legais

As vezes nossos padroes de programa coporaiva nos forgam a escrovr certs comentários
por quesis legis. Por cxcmplo,ffaes sobre direitos autoras e autoria sio informagdes
ecesiia e lógicas para se colocar no inicio de um arquivo fonte

Por exempo, abo está comentário padrto de cabegalho que colocamos no incio de todo
arquivo font do FitNesse, Fico feliz em dizer que nosa IDE evita a unio automática dese
comentário paa que no que aglomerado

4 Direitos autorais (C) 2003,2004,2005 por Object Mentor;* Inc. Todos
os direitos reservados.

7) Distribuido sob os termos da verso 2 ou posterior da Licenca
Publica Geral da GNU.

Comentärios como esse näo devem ser contratos ou tomos legais. Onde for possivel, faga
referéncia a uma licenga padräo ou outro documento extemo em vez de colocar todos os termos
«e condigdes no mesmo comentário.

ss 1 Capitale 4: Comentirios

Comentarios Informativos

As vezes prtico fomecer informagdes básicas em um comentário, Pr exemplo, considere o
comentário abo que explica o valor rtomado de um método absato:

// Retorna una instancia do Responder sendo testado.
protected abstract Responder responderInstancel)z

Um comentário como este pode ser útil ds vezes, mas, sempre que possivel, é melhor usar 0
nome da funedo para transmitir a informaci. Por exemplo, neste caso, o comentário ficaria
redundante se trocássemos o nome da funçäo: responderBcingTested.

Assim ficaria um pouco melhor:

Ui formato igual a kk:mm:ss EEE, MM dd, aaaa
Pattern cimeMatcher = Pattern.compile("\dts\ideiidt Nur, Mer Na, \de

Neste caso, o comentário nos permite saber que a expresso regular deve combinar com uma
hora e data formatadas com a funçäo SimpleDateFormat format usando a string específica com
0 formato. Mesmo assim, teria ficado melhor e mais claro se esse código tivesse sido colocado
em uma classe especial para converter os formatos de datas e horas. Entäo, o comentário
provavelmente seria supérfio,

Explicagäo da intençäo

As vezes, um comentário vai além de ser apenas informaçües úteis sobre a implementaçäo e
fomece a intençäo por trás de uma decisäo. No caso a seguir, vemos uma decisdo interessante
documentada através de um comentário, Ao comparar dois objetos, o autor decidiu que queria
classificar como superiores os objetos de sua classe em relagdo aos de outras.

public int comparoTo(Object 0)

iflo instanceof WikiPagePath)

WikiPagePath p = (WikiPagePath) 0;
String compressedNane = Stringutil.joininames, +"):
String compressedargumentilame = StringUtiljoin(p.names,
return comprossedliane.compareTo(conpressedÄrgumentNane);

}
return 1; // somos superiores porque somos tipo certo.


Abaixo está um exemplo melhor ainda. Talvez vocé discorde da soluçäo do programador, mas
pelo menos vocé sabe o que ele estava tentando fazer.
Publ void testConcurrantaddWidgots) throws Exception {

WidgetBuilder widgetBuilder =
new WidgetBuilder(new Class{](Holdwidget.class}):
String text = “bold text"

‘Comentarios Bons s

ParentWidget parent =

new Boldilidget(new MockwidgetRoot(), *'**bold text”
AtomicBoolean failFlag = new AtonicBoclean();
failFlag.setífalso);

(essa e à nossa melhor tentativa para conseguir uma condicao de corrida.
Jrpaxa aso criamos um grande numero de threads.
for (int 4 = 0; à < 25000; ist) (

WidgetBuilderThread widgetBuilderthread =

new WidgetBuilderThread(widgetBuilder, text, parent,

failPlag);

‘Thread thread = new ThreadiwidgetBuilderThread);

thread.start):

3

ascertequale(false, failFlag.get());
)

Esclarecimento

[As vezes € bom tradi o significado de alguns parámetros ou valore retomados obscuro para
algo incligvel. De modo geral, melhor encontar uma forma de schrecer al parámetro ou
‘alr retornado po sis, mas quando for arte da biblioteca padro, ou de um código que no se
posta altar ento um comentirioesclarecedo pode ser útil

public void testCompareTo() throws Exception

4
HikiPagePath a = Pathrarser.parse("Pagen”
WikipagePath ab = PathParser.parse("Pagea.PageB”);
WikiPagerath b = PathParser.parse(*Pa
WikiPagePach aa = PathParver.parse("PageA.PageA”
Wikiragerath bb = PathParser.parse|*PageB. Pages:
WikipagePath ba = Pathrarser.parse("PageB.Pagen”
assertTruc(a.compareTola) == 0); // a
assertrruelacomparero(b) !- 0); // a
assertTruelab.compareTolab) == 0); // ab
assertrruela.comparero(b) == -1); // a <b
assertTruelaa.compareTo(ab) == -1); // aa < ab
assertrrue(ba.compareTo(bb) == -1); // ba < bb
assertrrue(b.compareTo(a) == 1); // b> a
assertTruelab.compareTo(aa) == 1); // ab > aa
assertrrue(bb.compareTo(ba) == 1); // bb > ba

N >

um risco considerável, é claro, de que um comentário esclarecedor possa estar incorreto. Leia
‘0 exemplo anterior € veja como € dificil verficar se estio corretos. Iso explica tanto o porqué da
únecessidade do esclarecimento como seu risco. Portanto, antes de criar comentários como esses,
certifique-se de que no há outra saída melhor e, emo, cerifique-se ainda mais se esto precisos,

58 ¿Capítulo 4: Comentärios

Alerta Sobre Consequéncias

As vezes é útil alertar outros programadores sobre certas
consequéncias. Por exemplo, o comentário abaixo explica
porque um caso de teste em particular está desabilitado:

1 NäoNäo execute a menos que vocé
H venha tempo disponivel.

public void _testwithReallysigFile()

{
writeLinesrori le (10000000):
response.setBody(testPi 1e);
response.ready7oSend(thie);
String responsestring = output.

tostringús
assertSubString("Content-Length: 1000000000”, responsestring);
assertTrue(bytessent > 1000006000);

>

Hoje em dia, deabiliariamos o teste de caso através do atributo @ignore com uma string
explanatôria adequada: (ignore (“Leva muito tempo para exceutar") Antes da chegada do JUnid,
uma convençäo comum era colocar um trago inferior (underscore) no inicio do nome do método,
© comentário, enquanto divertido, passa sua mensagem muito bem.
Outro exemplo mais direo seria:

public static SimpleDateFormar makeStandardkttpDateFormat()
t
/1SimpleDaterormat näo 6 uma thread segura,
1/6 preciso criar cada instáncia independentemente.
SimpleDateFormat df = new SimpleDateFormat(“EEE, da MMM yyyy
HH:mmss 2°);
df.setTimezone(Timezone.get Timezone ("GMT"):
return af;
3

Talvez vocé reclame por haver melhores maneiras de resolver esse problema. Talvez eu
concorde com vocé, mas 0 comentário como fo feito éperfitamente lógico. Ele evitará que um
programador afoito use um inicializador estático em prol da eiciénca.

Comentário TODO Ñ

As vezes € cabivel deixar notas “To Do” (‘Fazer’) em comentários no formato //TODO. No caso
a seguir, o comentário TODO explica por que a funcio tem uma implementaçäo degradante e o
que se deveria fazer com aquela funçäo.

//2000-MAM essas näo año necessárias
// Esperamos que isso nio esteja mais aqui quando verificarmos © modelo
protected VersionInfo makeVersion() throws Exception
{

return null;
)

Comentários Ruins 5

roDos säo tarefas que os programadores acham que devem ser efetuadas, mas. por alguma
razio, ndo podem no momento. Pode ser um lembrete para excluir uma instrugáo desnecessária
‘ou um apelo para que alguém olhe o problema. Ou um pedido para que alguém pense em um nome
‘melhor ou um lembrete para fazer uma alteragdo que é dependente de um evento determinado,
Seja qual for o TODO, ele nño justifica deixar um código ruim no sistema,

Hoje em dia, a maioria das IDEs oferecem ferramentas e recursos para localizar todos os
comentários 7000; portanto, ndo € provável que fiquem perdidos no código. Mesmo assim, vocé
no deseja que seu código fique amontoado de TODOS, sendo assim, procure-os regularmente ©
climine os que pudes.

Destaque

Pode-se usar um comentário para destacar a importáncia de algo que talvez parega irrelevante,

String listItenconcent = match.group(3).trimi);
U a fungño trim muito importante. Ela remove os espaços
U iniciais que poderian fazer com que o item fosse

11 reconhecido como outra lista.

new ListItemWidget(this, listltencontent, this.level + 1
return buildList (text-substring(match.end()));

Javadocs em APIs Públicas

[Nao há nada de to prático e saisfatörio como uma API pública em descrita. Os javadocs para a
biblioteca Java padrio sño um exemplo. No máximo, seria dificil escrever programas Java sem elos.

Se estiver criando uma API pública, entio voc® certamente deveria escrever bons javadocs +
para ela. Mas tenha em mente os outros conselhos neste capítulo. Os javadoes podem ser tdo
enganadores, näo-locais e desonestos como qualquer outro tipo de comentário,

Comentários Ruins

‘A maioria dos comentários cai nesta categoria. Geralmente cles sto suportes ou desculpas para
um código de baixa qualidade ou justificativas para a falta de decisdes, amontoados como se o
programador estivesse falando com si mesmo.

Murmirio a

Usar um comentário só porque voce sente que deve ou porque o processo o requer é besteira. Se
‘optar criar um comentário, entio gaste o tempo necessário para fazé-lo bem.
Por exemplo, a seguir está um caso que encontrei no FitNesse, no qual um comentário poderia
ter sido útil. Entretanto, o autor estava com pressa ou ndo prestava muita atençäo. Seu murmúrio
foi deixado para trás como um enigma:

public void loaderoperties()

{
try

© x Capitulo 4: Comentários

String propertiespath =
propertiesLocation + */* + PROPERTIES FILE;
PilelnpurStream propertiesStream =
new FileInputStream(propertiesPath);
loadedProperties.load(propertiesstrean);
)
carch(10Exception e)
(
4 Nenhum arquivo de propriedades significa que todos os padroes
esto carregados
>

;

© que esse comentário no bloco catch significa? Claramente fazia sentido para © autor,
mas o significado ndo foi muito bem transmitido. Aparentemente, se capturarmos (catch) uma
ToRxception, significaria que näo hä arquivo de propriedades; e, neste caso, todos os padrdes.
esto carregados. Mas quem carrega os padres? Eles foram carregados antes da chamada ao
LoadPropert ies. load? Ou este capturou a exceyäo, carregou os padrdes e, entäo, passou a.
exceçäo para que ignorässemos? Ou loadProper ties. Load carregou todos os padrdes antes
de tentar carregar o arquivo? Será que o autor estava limpando sua consciéncia por ter deixado
‘© bloco do catch vazio? Ou ~ e essa possibilidade € assustadora — ele estava tentando dizer a si
‘mesmo para voltar depois e escrever o código que carregaria os padrdes?

Nosso único recurso é examinar o código em outras partes do sistema para descobrir o que
está acontecendo. Qualquer comentário que Ihe obrigue a analisar outro módulo em busca de um
significado falhou em transmitir sua mensage e ndo vale os bits que consume.

Comentarios Redundantes

A Listagem 4.1 uma funçäo simples com um comentário no cabegalho que € completamente
redundante. Provavelmente leva-se mais tempo para lé-lo do que o código em si.

Listagem 4-1
waitPorclose

7/ Geility method that rerurns when thie.cloved Le true. Throws an exception
11 3£ the timeout in reached.
public synchronized void wa

rows Exception

if{tclosed

Long timsoutnillis}

peur is)
AE (¿closed)
throw new Except ion(MeckResponseSender could not be closed")

Qual o propósito desse comentário? Certamente näo & mais informativo do que o código.
Nem mesmo justifica o código ou oferece uma intençäo ou raciocinio. É mais fácil ler o código
apenas. De fato, ele € menos preciso do que o código e induz o leitor a aceitar tal falta de preciso

Comentärios Ruins a

‘em vez da interpretaçäo verdadeira. É mais como um vendedor interesseiro de carros usados Ihe
garantindo que nao € preciso olhar sob o capó.

“Agora considere o grande número de javadocs inüteis e redundantes na Listagem 4.2 retirada
do Tomcat. Esses comentários só servem para amontoar e encobrir o código. Eles näo passam
informagdo alguma. Para piorar, só Ihe mostrei os primeiros, mas há muito mais neste módulo.

public abstract class Containersase
fecycle, Pipeline

delay for this compone

Indbrocessorbelay =

+ the Loader implementation

precected Loader loader = nu

sith whieh thie Container ia

Logger inplesent

+ associared.

protected Log logger = mulls

on with which this Container is

+ associated logger ane.
protected String logMane = null:

+ The Manager inplesentatil
* associated.
protected Manager manager = null;

with which this Container is

e Capitulo 4: Comentärios

Listen 42 (continuagto)

Java (Tomcat)

‘The cluster with which this Container ie ansociated,

protected Cluster cluster = null;

* tye human-readable name of this Container,

protected String nase = null;

the parent Container to which this Container is a child.

protected contain

2 parent = null:

* the parent ciass ioade
+ leader

protected ClassLoader parencClnastander » mais

be configured ween we install à

‘the Pipeline cbjece wich which thle Container le
Dre

protected Pipeline pipeline = new SrandardPipeline(Ehis) à

‘he Roala with which this con

protected Reala realn = nulls

A The resources Dizcentext cbject with Which this Container
+ iS associated.

roll:

protected Dixcontext resources =

Comentärios Ruins a

Comentários Enganadores

As vezes, com todas as melhores das intengdes, um programador faz uma afirmaçäo ndo mu
‘lara em seus comentários. Lembre-se também do redundante e enganador comentário que vimos
na Listagem 4.1

‘Como voce descobriu que o comentirio era enganador? O método nio retomava quando this.
closed virava true (verdadeiro), mas só se this.closed já fosse true (verdadeiro);
‘caso contrário, ele esperava por um tempo limite e, ento, langava uma exceçäose this .closed
ainda nio fosse true (verdadeiro).

Essa pequena desinformaçäo, expressada em um comentário mais dificil de ler do que o código em
si, podera fazer com que outro programador despreocupadamente chamasse essafunço esperando-a
‘que retomasse assim que this ,closedsetomasse true (verdadeiro) Esse pobreprogramador
Togo se vera efetuando uma depuraçäo tentando descobrir o porqué da lentidáo do código.

Comentários Imperativos

E basicamente tolo tr uma regradizendo que toda fimçäo dev ter um Javadoc, ou oda vaiávl
tim comentrio Estes podem se amontour no código, diseminar mentiras € era confuso ©
Sesorganizago.

Pa ctplo, os javadocs exigidos para cada funçäo levariam a abominagdes, como as da
Listagen 43 Ea zona ndo atescenta hada € 80 serve para ofuscar o código € abrir o Caminho
para menta € desinformagdes

Listagem 43 |

ne author of the CD
ke on the CD

+ paran durationinMinutes The duration of the CD in minutes
pal ele, seri
nt tracks, tintes)

<d.duration = duration:
aller ad lca):

Comentarios Longos

As vezes, as pessoas, toda vez que editam um módulo, sempre adicionam um comentário no
nee hos varie aies a quantidade de omentáios acumulada parece mais uma redaco
(um diario. J4 vi méduls com dezenas de pins assim

es Capítulo 4: Comentários

Re-organised the class and moved it to new package
: con, jrefinery.date (DO);
Added a getDescription() m
18 (03)
{RD requires setDeseription!) method, now that NotableDate
class is gone (D6); Changed gecPreviousbayOtWeek\)
getfollosingbnyOfneek() ard getileares:DayOtWeek\) t correct
Bugs. (Del:
05-Dec-2001 : Fixed bug in SpresdsheetDate class (96);
-May-2002 : Moved the ronth constants into a separate interface
: (Menthconstants) (261
19-2002 : Faxed bug in adakonths() method, thanks
2002 : Fixed errors reported by Checkstyle (EC);
Inplenented 5 ble (0)
Fixed bug in ad@Menths method (00);
Implenented comparable. Updated the isTnRange Javadoca (DO);
Fixed bug in adsveara() method (1096282) (05);

106, and eliminated Notabledate

Lerka Petr (D6);

Há muito tempo, havia um bom motivo para criar e preservar essas informagdes no inicio de
‘cada módulo, pois ndo existiam ainda os sistemas de controle de código fonte para fazer isso por
nés. Hoje em dia, entretanto, esses comentários extensos so apenas entulhos que confundem o
código, devendo assim ser completamente removidos.

Comentários Ruidosos

As vezes voce vé comentários que nada sio além de “chiados”. Eles dizem o óbvio € nio
fomecem novas informagöes.

Construtor padrao.

El
protected AnnualDaterule() {
j

fer Dia

+ Retorna do dia do més.
* areturn o día do més.
7
public int gethaÿofMonth() (
return dayOfMonth;
>

Esses comentários so to ielevantes que aprendemos a ignor-los, Ao lermos o código, nossos
oth passam dirt por els. No final, os comentários passam a “menti” conforme o código muda

Comentirios Ruins

6s

O primeiro comentário na Listagem 4 4 parece adequado‘. Ele explica por que o block catch.
ignorado. Contudo, o segundo näo passa de um chiado. Aparentemente, o programador estava

tio frustrado por criar blocos txy/catch na füngdo que ele precisou desabafar

Listagem 4-4
startSending

private void startSonding()
(

osendingt):

aten\sockerexception ©)

11 normal. sonsone stopped the request.
à
caren (exception el

ty

C

response addlErrorkasponder akoBxcept
response.clonehl!

1
catch Exception ei



onstringlel):

11 Give me à break!

)

Em vez de desabafar em comentários sem sentido € ruidosos, ele poderia ter pegado tal
frustragdo e usado-a para melhorar a estrutura do código. Ele deveria ter redirecionado sua energía
para colocar o último bloco tryicatch em uma funçäo separada, como mostra a Listagem 4.5.
Listagem 4-5

startsending (refatorado)

private void startsending()
t

ty

dose

ing):

1

ni Socket xcept:

¿1 normal. someone stopped the request
‘catch texception e)
t

EEE O ENS A

66, Capítulos

Listagem 4-5 (continuaçäo)
stortSoncing (refterade)

ranecloserosponse e)

adanxcopt

private void addrxceprionsnacloseñesponse (Exception e

response.add(Errortesponder.raketwcegt ionString (el):
response closes

“Troque a tentagdo para criarruídos pela determinagäo para limpar seu código. Vocé perceberá
que isso Ihe tornará um programador melhor € mais feliz

Ruídos assustadores

Os javadoes também podem ser vistos como ruídos. Qual o objetivo dos Javadocs (de uma
biblioteca de código livre bem conhecida) abaixo?

Ja Wome. */
private String name;
yt the version. Y
private string version;

/** None da licenca. */
private String licenceNare;

1
pr

versao. */
jate String info:

Releia os comentários com mais atengdo. Notou um erro de recortar-colar? Se os autores ndo.
prestarem atençäo na hora de escrever os comentários (ou colé-Ios), por que 0s leitores deveriam
esperar algo de importante deles?

Evite o comentário se € possivel usar uma fungäo ou uma variävel

Considere o pedago de código abaixo:

U © möäulo da lista global <nod> depende do
// subsistema do qual fazemos parte?
if (smodule.getDependSubsystens().contains(subSysMod.getSubsystem()))

Comentärios Ruins a

Poderia-se evitar o comentário e usar:

ArrayList moduleDependees = smodule.getDependsubsystems();
String ourSubSystem = subSysMod.getSubsystem();
if (moduleDependees.containsloursubsystem))

© autor do código original talvez tenha escrito primeiro o comentário (pouco provável) e, entäo,
‘0 código de modo a satisfazer o comentário. Entretanto, o autor deveria ter refatorado o código,
‘como eu fiz, para que pudesse remover o comentário.

Marcadores de Posiçäo

Algumas vezes os programadores gostam de marcar uma posiçäo determinada no arquivo fonte,
Por exemplo, recentemente encontrei o seguinte num programa:

11 Abe 70777777

É raro, mas há vezes que faz sentido juntar certas funçôes sob um indicador como esses. Mas, de
‘modo geral, eles sio aglomeragdes e devem-se exclui los —especialmente as várias barras no final.

Pense assim: um indicador € chamativo e óbvio se vocé mio os vé muito frequentemente.
Portanto, use-0s esporadicamente, e só quando gerarem beneficios significativos. Se usar
indicadores excesivamente, eles cairdo na categoria de ruídos e serio ignorados.

Comentários ao lado de chaves de fechamento

[As vezes, os programadores colocam comentários especiais ao lado de chaves de fechamento,
como na Listagem 4.6.

Embora isso poss fazer sentido em fundes longas com estruturas muito aninhadas, s6 serve para
amontoar o tipo de fungdes pequenas e encapsuladas que preferimos. Portanto, se perceber uma
vontade de comentar a0 lado de chaves de fechamento, tente primeiro reduzir suas fungdes.

Listagem 4-6
we.java

public class ve (
public static void mains

st] aras)

“teaser in = new BufferedReader (new InputStreasaeader (System,

=

Systen.out .printin(*wordCounc = *

cut prineln(*2in
Systencout println{charcount = * + charceunt);
Du

6 Capitulo 4: Comentirios
+

Listagem 4-6 (continuaçño)

(Hosxception ey {
en.err.printinl
y Heath

Zain

Creditos e autoria
/* Aäieionado por Rick *

Os sistemas de controle de código fonte sio muito bons para lembrar que adicionou o qué e
quando. Nao hä necessidade de poluir o código com comentários de autoria. Talvez vocé ache
‘que tais comentários sejam ites para ajudar outras pessoas a saberem 0 que falar sobre o código.
Mas a verdade é que eles permanecem por anos, ficando cada vez menos precisos e relevantes.

Novamente, o sistema de controle de código fonte é um local melhor para este tipo de
informagto.

Explicaçäo do código em comentários

Poucas práticas slo to condenáveis quanto explicar o código nos comentários. Nao faga is

Input$treamkesponse response = new InputStreamResponse();
Tesponse.setBody(formatter.getñesultstream(), formatter.
getBytecount(}}:

// Inputstrenn resultsstrean = formatter.getResultstrean();
U Streaneader reader = new StreamReader(resultsStrean);

N xesponse.setcontent (reader.read(formatter.getBytecount ()))}

Outros que vissem esse código ndo teriam coragem de excluir os comentários, Eles achariam que
esto Id por um motivo e sio importantes demas para serem apagados. Portanto, explicaçäo de
códigos em comentários se acumula como sujeira no fundo de uma garrafa de vinho ruim.
Observe o código do Commons do Apache:

this.bytePos = writeBytes(pn
IMmaxpos = bytebos;
wriveeader
writeResolution();
//datapos = bytepos;
if (writelmageData()) {

writeznd0;

this.pngBytes = resizeBytearray(this.pngBytes, this.maxPos):

Bytes, 0):

3
else (
this.pngBytes = null;

)
return this. pngBytos:
Por que aquelas duas linhas de código esto comentadas? Elas sño importantes? Foram deixados

como lembretes para alguma alteraçao iminente? Ou sdo apenas aglomerados que alguém
‘comentara anos atrás e simplesmente ndo se preocupou em limpar?

‘Comentarios Ruins o

>

Houve uma época. na década de 1960, em que explicar o código em comentários poderia ser
Prätico. Mas já faz um tempo que temos os sistemas de controle de código fonte, que lembrardo.
código para nés. Nao precisamos explicá-lo em comentários. Simplesmente exclua o código.
Prometo que ndo o perderá.

Comentários HTML
Códigos HTML em comentários de código fonte sio uma aberragäo, como pode ver no código

abaixo. Eles dificultam a leitura dos comentários onde seriam fäceis de ler — no editor/IDE. Se
{orem extrair comentários com alguma ferramenta (como o Javadoc) para exibir numa página da

Web,

>, entio deveria ser responsabilidado de tal ferramenta, e ndo do programador, adicionar 0s

comentários com os códigos HTML adequados.

Tarefa para executar os testes do Fit.
Essa tarefa efetua os testes do PitNesse e exibe os resultados.
PE

resource=áquot;tasks.propertiestquot; /ägt;
>

Eltyexecute-Atnesse-tests

suitepage-aquot ;FitNesse.SuiteacceptanceTest saquot:
Atnesseport=áquot;S0824quot;

resultsdir-squot;S(results.dirlaquot:
reaultshtmipage=aquorsfit-results.heml&quot; :
classpathref=kquot;classpathäquot; /&gt;

pres

+ Elt;taskdef classpathref:

Informaçôes náo-locais

Se vocé precisar escrever um comentário, nto, coloque-o pero do código que ele descreve. Nao
Fornega informagdes gerais do sistema no contexto de um comentário local. Considere, por exemplo.
‘0 comentário do Javadoc abaixo. Além de ser terrivelmente redundante, ele também fala sobre a
porta padräo. Ainda assim, a fungáo nem sabe que pora é essa. O comentário no está deserevendo
à füngdo, mas alguma outra em alguma parte distante do sistema. Certamente nto há garantia de
que esse comentário será atualizado quando o código que contém tal padräo for alterado.

4

* porta
+ eparam ficnesserort


qual o FitNesse deveria rodar. Padráo para <b>8082</b>.

public void serPitnessoPortíínt Atnesserort)

t
)

this.fitnessePort = ftnessePort;

» Capítulo 4: Comentários
x

Informagöes excessivas

"Nao adicione discussöes históricas interessantes ou deserigöes irrelevantes de detalhes em seus
comentários. Abaixo está o comentário de um módulo projetado para testar se uma fungdo
poderia codificar e decodificar base64. Além do número RFC, a pessoa que ler este código no
precisa das informagdes históricas contidas no comentário,

a
RFC 2045 - Multipurpose Internet Mail Extensions (MIME)
Parte um: seco 6.8 do Formato dos Corpos de Mensagens da Internet.
Codificacdo e Transferencia de Conteudo em Basesd
© processo de codificacio representa grupos de entrada de 24 bits
como strings de saída de 4 caracteres codificados. Da esquerda para
a direita, forma-se um grupo de entrada de 24 bits pela concatenacao
de 3 grupos de entrada de 8 bits
Beses 24 bits serdo, entäo, tratados como 4 grupos concatenados
de 6 bits, cada um traduzido para um único digito do alfabeto da
base64. ño codificar um fuxo de bits através da codificagäo para
base64, deve-se presumir que tal fluxo esteja ordenado com o bit mais
significante vindo primeiro,
Où seja, o primeiro bit no fuxo será o mais relevante no primeiro
byte de
8 bits, o oitavo será o bit menos relevante no mesmo primeiro byte
de 8 bits,

assim por diante.

Conexées nada óbvias

A conendo entre um comentário eo código que cle desereve dove ser Sbva, Se for fazer um
comentário. nio voce desea, pelo menos, que o eto se capaz de lero comentário e o código
ent, entender o que ol also.
Considere, por exemplo, o comentrioabaixo do Commons do Apache:
> comeca comun array grande o bastante para conter todos os
pixels
mais os bytes de fltragen) e 200 bytes extras para informacóes
mo cabecalho
7
this.pngsytes
‚© que é um byte de filtragem? Ele tem a ver com o +1? Ou com o *3? Com ambos? Um pixel
um byte? Por que 200? O objetivo de um comentário explicar o que o código náo consegue
por is. É uma lástima quando um comentário também precisa ser explicado.

new byte[((this.width + 1) * this-height * 3) + 200);

Cabecalhos de fungóes

Fungdes curtas no requerem muita explicaçäo. Um nome bem selecionado para uma funçäo
pequena que faga apenas uma coisa costuma ser melhor do que um comentário no cabegalho.

Comentérios Ruins a

a

Javadocs em códigos náo-públicos

Assim como os Javadocs sdo práticos para as APIS públicas, eles säo uma maldiçäo para o
código nio voltado para a distribuido ao público. Gerar páginas Javadoc para classes e fungdes
‘dentro de um sistema geralmente náo é prático, e a formalidade extra dos comentários javadocs
‘une um pouco mais de entulhos e distrasdo,

Exemplo

Na Listagem 4.7, criei um módulo para o primeiro XP /mmersion para servir de exemplo de má
programaçäo e estilo de comentário, Entdo, Kent Beck refatorou esse código para uma forma muito
mais agradável na presenga de algumas dezenas de estudantes empolgados. Mais tarde, adaptei o
exemplo para meu livro Agile Software Development, Principles. Patterns, and Practices e 0
primeiro de meus artigos da coluna Crafisman publicados na revista Software Development.

Para mim, o fascinante desse módulo € que havia uma época quando muitos de nós o teriamos.
considerado “bem documentado”. Agora o vemos como uma pequena bagunga. Veja quantos
problemas diferentes vocé consegue encontrar.

E Eratoctkones

»

D. c 296 Bc, Cyrene, Libya ~~
The first man to calculate the
simforénco of the Earth. Also known for working on
calendars with leap years

cross cut all zultipl
‘and cross out all of i

author Alphonse
version 15 Feb 2002 atp

import java.util.*s
public class GeneratePrines
t

‘paran raxvalue is the generation Linie

public static

U] generaterrines (int maxtaluen

£ (aaxvalue

only valid case

declarations

Capítulo 4: Comentários

4

Listagem 4-7 (continuagio)
GoneratePrines.java

tor
ei] = true:

/ get rid of knows non-prines
to) = #11) =

À e mathegrtts) +

fey

if 1ELAD) // LE à ds uncroseed, crose its multiples

jeu
multiple i

// how many prines are there?
for is

prineslj==] = ir

turn prises: // return the prizes

else // maxíalue < 2
return new int(0}; // return

12 array if bed input.

auto-explicativos.

Na Listagem 48 pode-se ver uma versio refatorada do mesmo módulo. Note que o uso de
comentários foi limitado de forma significativa — há apenas dois no módulo inteiro, ambos so

es prime numbers up to à user specified
maximum. The algorıche used is the Sieve of Bratostheres
Given an array of integers starting at 2:

ind the fire: uneros eger, and cross out all its

Comentários Ruins

B

Listagem 4-8 (continungäo)
PrmeGeneratorjava (rtatorado)

‘multiples. Repeat until there are 20 more multiples
array.

public class Prinetenerator
C
private static kooleanf] crossedout;
Private static dat [] result

public static incl] gene

ePrines (int raxtalue)

it iraxtalue < 2)
‘return new ine [0];

uncroseIntegersüpfoinaxvalue);

rossöumultiplesi);

putlncrossedintegersintoResult ();
ur result;

private static void uncrossIntegersupto(ine naxvalue)
t

crossedout = new boolean{naxvalue + 2];
for (int 1 = 25 1 < crossedDut length; i++)
‘crossedOut(i] = false;

1

pr
1 al
int Linie = determinelterationLin!
for tint i= 2: 1 ca Limit; der)
TE not£rosseaii))
‘erossoutMult iplesdE (i)

wate static void crossOutMultiples()

0:

private static int deterninetterationtimit ()
t

Every multiple in the array has a prise L
1 ig less than or equal te the root of the
1 so we don't have to cross out multiples of nusbers

// larger than that root.
Gouble icerationLinit = Mach. sgrt (crossesout. length;
return (int) iterat loaiiait;

private static void crossoutMultiplesor int 4)

for (int multiple « 211:
multiple < crosse length
multiple += {)
exoesadont Imultiple] = ervey

7 Capítulo 4: Comentários

Listagem 4-8 (continuaço)
PrimeGeneratorjava(refatorado)

private static boolean notCrossediint 1)

(turn crossedout [i]

private static void putUncrossedintegersTntoResult ()

result = new int fnumberofuncrossedintegers()1;
for (int $ = 0, À = 25 À < crossedOit.iength: Los)

it (notcrossea(:))

‘Como o primeiro comentário € muito parecido com a fungdo generatePrimes, fica fácil dizer
que ele € redundante. Mesmo assim, acho que o comentário serve para facilitar a leitura do
algoritmo, portanto prefiro manté+lo.

Já o segundo se faz praticamente necessärio. Ele explica a lógica por trás do uso da raiz
‘quadrada como o limite da iteraçäo. Nao consegui encontrar um nome simples para a variável
‘ou qualquer estrutura diferente de programaçäo que esclarecesse esse ponto. Por outro lado,
‘0 uso da raiz quadrada poderia ser um conceito. Realmente estou economizando tanto tempo
assim o limitar a iteracdo à raiz quadrada? O cálculo desta demora mais do que o tempo que
economizo?

Vale a pena ponderar. Usar a raiz quadrada como 0 limite da iteracdo satisfaz o hacker em
mim que usa a antiga linguagem C e Assembly, mas no estou convencido de que compense o
tempo e o esforgo que todos gastariam para entendé-la.

Bibliografia

IKP78]: Kernighan and Plaugher, The Elements of Programming Style, 2d. ed., MeGraw-
Hill, 1978.

5

Formataçäo

Quando as pessoas olham o código. desejamos que fiquem impressionadas com a polidez, a
consisténcia e a atengäo aos detalhes presentes, Queremos que reparem na organizaçäo.
Descjamos que suas sobrancelhas se levantem ao percorrerem os módulos; que percebam que
foram profissionais que estiveram ali. Se, em vez disso, virem um emaranhado de código como

7 Capítulo S: Formatagto

se tivesse sido escrito por um bando de marinheiros bébados, entäo provavelmente concluiräo
que essa mesma falta de atençäo foi perpetuada por todo o projeto.

'Vocé deve tomar conta para que seu código fique bem formatado, escolher uma série de regras
simples que governem seu código e, entdo, aplica-ta de forma consistente. Se estivertrabalhando.
em equipe, ento, todos devem concordar com uma única série de regras de formataçäo. Seria
bom ter uma ferramenta automatizada que possa aplicar essas regras para vocd.

O objetivo da formatagäo

Primeiro de tudo, sejamos claros. A formataçäo do código é importante. Importante demais para
se ignorar e importante demais para ser tratada religiosamente. Ela serve como uma comunicaçäo,
€ essa éa primeira regra nos negócios de um desenvolvedor profissional.

Talvez vocé pensasse que “fazer funcionar” fosse a primeira regra. Espero, contudo, que, a
esta altura, este livro já tenha tirado esse conceito de sua mente. À funcionalidade que vocé cria
hoje tem grandes chances de ser modificada na próxima distribuigäo, mas a legibilidade de seu
código terá um grande efeito em todas as mudanças que serio feitas. A formataçäo do código
€ a legibilidade anteriores que continuam a afetar a capacidade de extensdo € de manutençäo
tempos após o código original foram alteradas além de reconhecimento. Seu estilo e disciplina
sobrevivem, mesmo que seu código ndo.

Entdo quais as questöes sobre formatagdo que nos ajuda a comunicar melhor?

Formataçäo vertical

Comecemos com o tamanho vertical. O seu cédigo-fonte deve ser de que tamanho? Em Java, 0
tamanho do arquivo está intimamente relacionado ao da classe. Discutiremos sobre o tamanho das
classes quando falarmos sobre classes. Mas, por agora, consideremos apenas o tamanho do arquivo.

‘Qual o tamanho da maioria dos códigos-fonte em Java? Há uma grande diversidade de
tamanhos e algumas diferengas notáveis em estilo (veja a Figura 5.1). Há sete projetos diferentes
na figura: Junit, FitNesse, testNG, Time and Money, JDepend, Ant e Tomcat. As linhas verticais
mostram os comprimentos mínimo e máximo em cada projeto. A caixa exibe aproximadamente
um tergo (um desvio padräo‘) dos arquivos. O meio da caixa € a média. Portanto, o tamanho
médio do código no projeto FitNesse € de cerca de 65 linhas, e cerca de um tergo dos arquivos
esto entre 40 e 100+ linhas. O maior arquivo no FitNesse tem aproximadamente 400 linhas, e
0 menor 6.

Note que essa € uma escala logarítmica; portanto, a pequena diferenga na posigdo vertical
indica uma diferenga muito grande para o tamanho absoluto.

Junit, FitNesse e Time and Money so compostos de arquivos relativamente pequenos.
Nenhum ultrapassa 500 linhas € a maioria dos arquivos tem menos de 200 linhas, Tomcat e
‘Ant, por outro lado, tém alguns arquivos com milhares de linhas e outros, próximos à metade,
ultrapassam 200 linhas,

(© que isso nos diz? Parece ser possivel construir sistemas significativos (o FitNesse tem
quase 50.000 linhas) a partir de códigos simples de 200 linhas, com um limite máximo de 500.
Embora essa ndo deva ser uma regra fixa, deve-se considerá-la bastante, pois arquivos pequenos
costumam ser mais ficeis de se entender do que os grandes.

Formataçäo Vertical n

A metáfora do jornal

Pense mum artigo de jornal bem redigido. Vocé o lé verticalmente, No topo vocé espera ver
uma manchete que Ihe diz do que se trata a estória e he permite decidir se deseja ou nâo ler. O
primeiro parágrafo apresenta uma sinopse da estória toda, omitindo todos os detalhes, falando de
‘uma mancira mais geral. Ao prosseguir a leitura, verticalmente, vo surgindo mais detalhes até
que datas, nomes, citagdes, alogagdes e outras minücias sejam apresentadas.

Desejamos que um código fonte seja como um artigo de jornal. O nome deve ser simples
‘mas deserilivo. O nome em si deve ser o suficiente para nos dizer se estamos no módulo certo ou
do. As partes mais superiores do código-fonte devem oferecer os conceitos e algoritmos de alto
nivel. Os detalhes devem ir surgindo conforme se move para baixo, até encontrarmos os detalhes.
‘eas fungdes de baixo nivel no código-fonte.

Escala logarítmica de distribuigäo de tamanho de arquivos (altura da caixa = sigma)

Um jornal € composto de muitos artigos: a maioria € bastante pequena. Alguns slo um.
‘pouco maiores, Muito poucos possuem textos que preencham a página toda. Isso torna o jornal
“aproveitável. Se ele fosse apenas uma estória extensa com uma aglomeragäo desorganizada de
fatos, datas e nomes, nós simplesmente ndo o leriamos.

Espaçamento vertical entre conceitos

Quase todo código é lido da esquerda para a direita e de cima para baixo. Cada linha representa
uma expresso ou uma estrutura, e cada grupo de linhas representa um pensamento completo.
Esses pensamentos devem ficar separados por linhas em branco.

Considere, por exemplo, a Listagem 5.1. Há linhas em branco que separam a declaragaoe a
importagáo do pacote e cada uma das fungdes. Essa simples e extrema regra tem grande impacto.
‘no layout visual do código. Cada linha em branco indica visualmente a separagao entre conecitos,
‘Ao descer pelo código, seus olhos param na primeira linha após uma em branco.

7 Capítulo 5: Formataçao

Retirar essas linhas em branco, como na Listagem 5.2, gera um efeito notavelmente obscuro.
na legibilidade do código.

Listagem 52
Bolawidget Java

package fitnesse. wikitext wis
Import Java aci regex e
public class Boldéidget ex
public static Final String REGEXP
Private static final Pattern pattern
Pattern MOLTILING + Pattern. DOTALL)

public Boldkidget (parent
super (par

% patter
patch. ind)

asechilaiiógers (match group{1)}}}
public String render() throws Exception (
ingtutfer html = new Seringutfer (cb
tal) .append(*</b>*)
return heal. toString à

y

Esse cfcito € ainda mais realçado quando vocé desvia seus olhos do código. No primeiro
exemplo, os diferentes agrupamentos delinbas saltam aos olhos, enquanto no segundo tudo fica
meio confuso. A diferenga entre essas dus listagens € um pouco de espagamento vertical

Continuidade vertical

Se o espagamento separa conceitos, entño a continuidade vertical indica uma associagäo intima,
Assim, linhas de código que estio intimamente relacionadas devem aparecer verticalmente
unidas, Note como os comentários inüteis na Listagem 5.3 quebram essa intimidade entre a
instáncia de duas variáveis.

Listagem 53

public class Reportercentig {

se class name of the reporter

private String m classtene:

> Te properties 0

reporter Listener

private Liet<Property> m properties = new Arca

public void adéProperey Property property) {
properties. addlproperty);

Formatagdo Vertical m

A Listagem 5.4 está muito mais fácil de se ler. Ela cabe numa única viso, pelo menos para
‘mim, Posso olhä-la e ver que € uma classe com duas variáveis e um método, sem ter de mover
‘muito minha cabega ou meus olhos. A listagem anterior me faz usar mais o movimento dos olhos
+ da cabega para obter o mesmo nivel de entendimento.

Listagem 5.4

raybasteProperty> tz

Distancia vertical

Já ficou tentando se encontrar numa classe, passando de uma funçäo para a próxima, subindo €
“descendo pelo código-fonte, tentando adivinhar como as fungóes se relacionam e operam, só para
se perder nesse Iabirinto de confusio? Já subiu pela estrutura de herança buscando a definiçäo de
uma variável ou funglo? Isso é frustrante, pois vocé está tentando entender o que o sistema faz,
‘enquanto gasta tempo e energia mental numa tentativa de localizar e lembra onde esti as pegas.

‘Os eonceitos intimamente relacionados devem ficar juntos verticalmente [G10}

Obviamente essa regra nfo funciona para conceitos em arquivos separados. Mas, ento, ndo
‘se devem separar em arquivos distintos conceitos intimamente relacionados, a menos que tenha
‘uma razdo muito boa, Na verdade, esse € um dos motivos por que se devem evitar variéveis
protegidas. Para os conceitos que slo (Jo intimamente relacionados € que esido no mesmo
arquivo-fonte, a separaçäo vertical deles deve ser uma medida do quio importante eles sto para
a imeligibilidade um do outro. Queremos evitar que nossos leitores tenham de ficar visualizando
vários dos nossos arquivos-fonte e classes,

Declaraçäo de variávels. Devem-se declarar as variäveis o mais próximo possivel de onde
serdo usadas.

Como nossas fungdes säo muito pequenas, as variáveis locais devem ficar no topo de cada
funçäo, como mostra a funçäo razoavelmente longa abaixo do Junit4.3.L.

private static void readpreferences() (

Imputstream is“ mull)

try (
iss new Filernputstream(getPreferencesFile()):
setPreferences(new Properties(getPreferences())):
getPreferences().load\is);

) cateh (dOEXCEption e) {
try €

if Gs

1s.close0;
) catch (ioßxception el) (
)

mul

a Capitulo $: Formatagko

Geralmente, devem-se declarar as variäveis de controle para loops dentro da estrutura de iteragdo,
‘como mostra essa pequenina funçäo da mesma fonte acima,
public int couneTestcases() (
int count= 0
for (Test each : tests)
count += each.countTestCases();
return 2

Em raros casos pode-se declarar uma variável no inicio de um bloco ou logo depois de um
Loop em uma funcio razoavelmente longa. Veja um exemplo no pedacinho abaixo de uma fungáo
‘muito extensa do TestNG.

for (iniTest test : msuite.getTests()) {
‘TestRunner tr = m runnerPactory.newTestRunner(this, test);
tr.addbistenerim_textReporter);
n_tostRunners,adältr);

getinvoker();

for (ITestiiGMethod m : tr.getBeforesuiteMethods()) {
beforesuitenechods.putim.getmethod(). =):
>

for (ITestnomethod m : tr.getAftersuitemethods()) (
afterSuiteMethods.put(m.getMethod\), m;
>

Instáncias de vaiéves, Por outro lado, devem-se declarar as instáncias de vai
classe, Isso nio dove aumentar a distincia vertical entre tas variáveis, pois, numa classe bem
projetada, elas sio usadas por muitos, send todos, os métodos da classe.

“Muito já se discut sobre onde devem fica as instáncias de variáveis. Em C++ € comum a
regra da tesoura, na qual colocamos todas as instáncias das variáveis no final. Em java, contudo,
a convenpdo é colocódas no inicio da classe.

[Nilo vejo motivo para seguir uma ou outra convengáo. O importante é que as instáncias de
variáveis sejam declaradas em um local bem conhecido. Todos devem saber onde buscar as
declaraçües

Considere, por exemplo, o estranho caso da classe Testsuite no JUnit 43.1. Resumi
bastante essa classe para mostrar a questo. Se vocé ler até cerca de metade do código, verá
dus instncias de variáveis declarada. Seria dificil oculté-ls num lugar melhor. Quem ler este
código se depararia por acaso com as declaraçôes (como ocorreu comigo).

public class Testsuite implements Test (
static public Test createTest(Class<? extends TestCase>

Formataçäo Vertical si

theclass,

String name) (

1

public static Constructor<? extends Testcase>
GetTestConstructor(Classe? extends TestCase> theClass)
throws NoSuchMethodgxception (

)
public static Test warningífinal String message) (

: a

private static String exceptionTostring(throwable t) (
Ñ à

private String fNane;

private VectorcTest> fTesta= new Vectorcrest> (10);

public Testsuite() {
}

public TestSuite(final Classe? extends TestCase> theClass)

)

public TestSuite(Class<? extends TestCase> theClass, String
name) (

3

i

Fungdes dependentes. Se uma funçäo chama outra, elas devem ficar verticalmente próximas,
€ a que chamar deve ficar acima da que for chamada, se possível. Isso dé um fluxo natural ao
programa. Se essa convengäo for seguida a fim de lgibilidade, os leitores poderdo confiar que
as declaraçües daquelas fungdes virdo logo em seguida após seu uso, Considere, por exemplo,
6 fragmento do FitNesse na Listagem 5.5. Note como a fungdo mais superior chama as outras
dela e como elas, por sua vez, chama aquelas abaixo delas também. Iso facilita encontrar
clmente a legibilidade de todo o módulo.

as funçdes chamadas e aumenta considera

Capitulo S: Formataçäo

Listagem 5-5
MikiPageResponder java

public class WikifageResponder Inplanents Secureñesponder |
‘protected WikiFage page;
Protected FageData Pageñaras
Protected String pagetitie:
protected Request requests
Protected Pagecrawler crawlers

public Response nakeResponselPitNesseContext context, Request request
throws Excapeion |
String pageliane ~ getPageianeorDefault (request, FrontPage")

oadPago pagelane, context);
AE (page == mil)
„again extouatespene ocn seit
return makePageResponse content);


private string getPageianeOrdefault (Request request, String defaultragenano)
t

String pagellano = request .getResource();
EE, (Geringle 1. se8lank pageiane))
page = dotaultragetane;

return pagellne;

protected void loadrage(String resource, Pithiessetonsext context)
hres Exception |
Wikivagerath path = PathParser.parse (resource!
crawler = context root .getPageCravier):
Crawler. setbeadináS:rategy (new Varcual nabledPageCravler ()):
page = crawler.getPage(concext.rcot, path):
FE (page t= null)
‘pageDeta = page.getDatat):

private Response notPoundResponse(FitNesseContext context, Request request)
throws Exception |
{return new Notfoundkesponder ) .nskeResponse(concext, request);



private Simpleresponse makePageResponse(FitNesseContext context)
‘rows Exception {
pageTitle = Pathrarser.rendor (crauler.getFullfachipage)l;
Beving heal = sakoltel (context);

= new Simplenesponse();

return response:

Formatagdo Horizontal 8

Além disso, esse fragmento apresenta um bom exemplo de como manter as constantes
no nivel apropriado [G35], A constante “FrontPage” poderia ter sido colocada na fungdo
getPageNameOrDefault, mas isso teria ocultado uma constante bem conhecida e esperada em
uma fungdo de baixo nivel. Foi melhor passar tal constante a partir do local no qual ela faz
sentido para um onde ela realmente é usada,

Afinidade conceitual. Determinados bits de código
querem ficar pero de outros bits. Eles possuem uma certa
afinidade conceitual. Quanto maior essa afnidade, menor
deve sera distancia vertical entre eles

Como vimos, essa afinidade deve bascarse numa
<ependéncia direta, como uma fungdo chamando outra où
uma funçäo usando uma variävel. Mas há outras causas
possiveis de afinidade, que pode ser causada por um grupo
de fungdes que efetuam uma operagdo parecida. Considere
© pedago de código abaixo do JUnit 43.1:

public class Assert {
static public void assertrrue(String
message, boolean condition) {
if (condition)
fail(message);



static public void assertTrue(boolean condition) {
assertrrue(null, condition);
>

static public void assertFaise(string message, boolean
condition) {
assertTrue(message, condition);
>

static public void assertFalse(boolean condition) {
assertPalse(mull, condition);

)

Essas fungdes possuem uma afinidade conceitual forte, pois compartilham de uma mesma
convençäo de nomes e efetuam variagdes de uma mesma tarefa básica. O fato de uma chamar a
outra € secundário. Mesmo se nao o fizessem, ainda iriam querer ficar próximas.

Ordenaçäo vertical

De modo geral, desejamos que as chamadas das dependéncias da funcio apontem para baxo,
Isto é, a funçäo chamada deve ficar embaixo da que a chama'. Isso cria um fluxo natural para
baixo no módulo do código-fonte, de um nivel maior para um menor.

Assim como nos artigos de jomais, esperamos que a maioria dos conceitos venha primeiro, e
também que seja expressada com uma quantidade minima de detalhes. Esperamos que os detalhes

4 Capitulo 5: Formataçao

de baixo nivel venham por último. Isso nos permite passar o olhos nos arquivos-fonte e aber
‘uma idéia de algumas das primeirasfungdes, sem ter de mergulhar nos detalhes. A Listagem 5.5
está organizada dessa forma. Talvez os exemplos da Listagem 15.5 (p. 263) e 3.7 (p. 50) estejam
ainda melhores.

Isso € exatamente o oposto de linguagens, como Pascal, C € C++, que exigem a definieto das
funçües, ou pelo menos a declaragdo, antes de serem usadas.

Formatagäo horizontal

Qual deve ser o tamanho de uma linha? Para responder isso, vejamos como ocorre em
Programas comuns.

Novamente, examinemos sete projetos diferentes. A Figura $2 mostra a distribuiçäo do
comprimento das linhas em todos os sete projets. A regularidade € impressionante, cada linha
fica com cerca de 45 caracteres, De fato, todo comprimento de 20 a 60 representa cerca de 1
por cento do nümero total de linhas. Isso sto 40%! Talvez outros 30 por cento possuam menos
do que 10 caracteres. Lembre-se de que é uma escala logarítmica, portanto a aparéncia linear
do declinio gradual acima de 80 caracteres € realmente muito significante. Os programadores.
claramente preferem linhas curas.

1550 sugere que devemos nos esforgar para manter nossas linhas curas. O antigo limite de 80
de Hollerith é um pouco arbiträrio, e mo sou contra línhas com 100 ou mesmo 120 caracteres.
Mas ultrapassar isso provavelmente é apenas falta de cuidado.

Figura 52:
Distribuiçäo da largura da linha em Java

Eu costumava seguir a regra na qual jamais se deve ter de rolar a tela para a direita. Mas, hoje
em dia, os monitores esto muito largos para isso, e programadores mais jovens também podem
diminuir a fonte de modo que 200 caracteres caibam na tela. Nao faça isso. Fu, pessoalmente,
determinei 120 como meu limite.

Formatasño Horizontal s

Espagamento e continuidade horizontal

"Usamos o espago em branco horizontal para associar coisas que estdo intimamente relacionadas
e para desassociar outras fracamente relacionadas. Considere a fungáo seguinte:

private void measureline(String line) {
Linecountes:
int linesize
totalchars += linesize;
LineWidthitistogram.addLine(LineSize, Linecount):
recordwidestLinetlinesizo);

Line.lengtht);

;

Coloquei os operadores de atribuigdo entre espagos em branco para destacá-los. As instruçües
de atribuigdo tém dois elementos principais e distintos: os lados esquerdo e Os espagos
ormam essa separaçäo 6bvia.

Por outro lado, ndo coloque espaços entre os nomes das fungdes cos parénteses de abertura. Iso.
porque a funçäo e seus parámetros esto intimamente relacionados. Separá-los iria fazer com que
parecesse que nao esto juntos. Eu separo os parámetros entre parénteses na chamada da funçäo
para realgar a virgula e mostrar que eles estdo separados.

Outro uso do espago em branco é para destacar a prioridade dos operadores.

public class Quadratic {

public static double rootlídouble a, double b, double cl {
double determinant = determinant(a, b, c)

return (-b + Math.sgre(determinant)} / (2*a); .

)
public static double root2(int a, int b, int c) (
double determinant = determinantía, D, cl;
return (-b - Math.sqrt(determinant)) / (2a):
>

private static double determinantidouble a, double b, double c)
return bth - graves
;

Note como ¢ fácil ler as equagdes. Os fatores no possuem espaços em branco entre eles
porque eles (dm maior prioridade. Os termos so separados por espagos em branco porque a
adiçäo e a subtraçäo tém menor prioridade.

Infelizmente, a maioria das ferramentas para reformataçao de código ndo faz essa distingäo
entre operadores e usam © mesmo espaçamento para todos. Portanto, costuma-se perder
‘espagamentos sutis como os acima na hora da reformataçäo do código.

= Capitulo S: Formataçäo

Alinhamento horizontal

Quando eu era programador em assembly”,cu usava o alinhamento horizontal para realgar certas
estruturas. Quando comecei a programar em C, C++ e, depois, em Java, continue a tentar alinhar
todos os nomes das variäveis numa série de declaragdes, ou todos os valores numa série de
instrugdes de atribuigäo. Meu código ficava assim:

public class FitNesseExpediter implemente Responsesender
{
private Socket socket;
private InputStream input;
private Outputstream output;
private Request request
private Response response;
private FitNessecontext context;
protected long requestParsingTimeLimt
private long requestProgress:
private long requestParsingdeadlin
private boolean hasError;

public FitNesseExpediter(Socket s,

this.context = context;
socket

input = s.getInputstream();
output = s.getoutputstream():
requestParsingTimelimit = 10000;

Entretanto, descobri que esse tipo de alinhamento ndo é prático. Ele parece enfatizar as coisas
erradas c afasta meus olhos do propósito real. Por exemplo, na lista de declaragdes acima, vocé
fica tentado a ler todos os nomes das variáveis sem se preocupar com seus tipos. Da mesma
forma, na lista de atribuigoes, vocé se sente tentado a lertoda lista de valores, sem se preocupar
‘em ver o operador de atribuiçäo. Para pioraras coisas, as ferramentas de reformatagáo automática
geralmente eliminam esse tipo de alinhamento.

Portanto, acabei ndo fazendo mais esse tipo de coisa. Atualmente, prefiro declaragdes e
atribuigdes ndo alinhadas, como mostrado abaixo, pois eles destacam uma deficiéncia importante.
Se eu tiver lisas longas que precisem ser alinhadas, o problema está no tamanho das listas, e
no na falta de alinhamento. O comprimento da lista de declaragdes na FitNesseExpediter
abaixo sugere que essa classe deva ser dividida.

public class FitNesseExpediter implements Responsesender
© private Socket socket:

private InputStream input;

private Outputstream output;

private Request request:

Formataçäo Horizontal s

‘Quem estou tentanto enganar? Ainda sou um programador em assembly. Pode-se afastar o
programador da linguagem, mas ndo se pode afastar a linguagem do programador!

private Response response;
Private FitNesseContext context;
Protected long requestParcingTimeLimit;
Private long requestProgress;

Private long requestrarsingDeadline;
private boolean hasError;

public PitNesseExpediter(Socket s, FitNesseContext context) throws
Exception
{
this.context = context;
socket = 8:
input = s.getInputStream();
output = s.getOutputstream|):
requestParsingTimeLimit = 10000;
>

Endentagäo

Um arquivo-fonte € mais como uma hierarquia do que algo esquematizado. Há informagdes
pertinentes a0 arquivo como um todo, ás classes individuais dentro do arquivo, aos métodos
dentro das classes, aos blocos dentro dos métodos c, recursivamente, aos blocos dentro de
locos. Cada nivel dessa hierarquia € um escopo no qual se podem declarar nomes € no qual sto
interpretadas declaraçôes e instrugdes executives,

"A fim de tomar visivel a hierarquia desses escopos, endentamos as linhas do código-fonte
de acordo com sua posigäo na hierarquia. Instrugdes no nivel do arquivo, como a maioria das“
declaragdes de classes, näo sio endentadas. Métodos dentro de uma classe sto endentados
‘um nivel à direita dela, Implementagdes do método so implementadas um nivel à dircita da
declaraçäo do método. Implementagdes de blocos säo implementadas um nivel à ireita do bloco
que as contém, € assim por diante.

‘Osprogramadoresdependem bastante desse esquema de endentaçäo. Elesalinham visualmente
na esquerda as linhas para ver em qual escopo elas estdo. Isso Ihes permite pular escopos,
‘Como de implementagöes de estruturas 1£ ¢ while, que ndo so relevantes no momento. Eles
procuram na esquerda por novas declaragdes de métodos, novas variáveis e até novas classes.
Sem a endentagäo, os programas seriam praticamente ininteligíveis para humanos.

Considere os programas seguintes sintática e semanticamente idénticos:

public class PitNesseServer implemente SocketServer { private
FitNessecontext
Context; public PitNesseServer(FitNesseContext context) { this.context =

context: ) public void serve(Sockat 8) { serve(s, 10000); ) public void
Servelsocket s, long requestTimeout) { try ( FitllesseBxpediter sender =
FitNesseExpediter(s, context);

Sender. setRequestParsingTimeLimitirequestTimeout); sender.start(); )
catch(Exception e) { e.printstackTracel): ) } }

3 Capitulo 5: Formataçäo

public class FitNesseserver implements SocketServer (
private FitNessecontext context;

public FitNesseserver(eitNesseContext context) (
this.context = context;

)

public void serve(Socket 5) (
serve(s, 10000);
?

public void servelSocket s, long requestTimeout) {
try (
FitnesseExpediter sender = new FitNesseExpediter(s,
context);
sender. set Request ParsingTimeLimit (requestTimeout);
sender start]:
)
catch (Exception e) {
e.printStackTrace():
)

)

Seus olhos conseguem discemir rapidamente a estrutura do arquivo endentado. Quase
instantaneamente vocé localiza as variáveis, os construtores, os métodos acessores (leitura e
escrita, ou setter and getter) e os métodos. Bastam alguns segundos para perceber que se trata
de um tipo simples de interface pra um socket, com um tempo limite. A verso sem endentagäo,
‘contudo, é praticamente incompreensivel sem um estudo mais profundo.

Ignorando a endentaçäo. As vezes, ficamos tentados a nio usar a endentagdo em estruturas 3 £ curas,
loops si Le pequenos ou fungúes pequenas, Sempre que náo resisto a esa tentagáo, quase sempre
acabo voltando e endentando tas partes. Portanto, evito alinhar uniformemente escopos como est:
public class ConmentWidget extends Textiidget
public static final string REGEXP = °#[*\rin}*(2:(2:\r\n)|\n)\2)

public Conmentiidget(Parentividget parent, String cext)(super(parent,
texts

public String render() throws Exception (return “+; )


Prefiro expandir e endentar escopos, como este:

public class Conmentwidget extends Textinidget {
public static final String REGEXP = **#(-\r\nl*(?

Aria) tals}?

public Commentwidget (Parentwidget parent, String text) (
super(parent, text);
)

Regras de formataçäo do Uncle Bob 5

public String render() throws Exception {
return
>
>

Escopos minúsculos

De vez em quando, o corpo de uma estrutura while ou for € minúscula, como mostra abaixo.
Como náo gosto disso, procuro evitá-las, Quando isso nfo for possivel, verifico se o corpo da
estrutura está endentado adequadamente e entre parénteses. Inümeras vezes jä me enganei com
‘um ponto-e-virgula quietinho lá no final de um loop while na mesma linha. A menos que voce
tome esse ponto-e-virgula visivel endentando-o em sua prépria linha, fica dificil visualizá-lo.

while (dis.readíbuf, O, readBufferSize) != -1)

Regra de equipes

O título desse tópico faz um jogo com as
palavras. Todo programador tem suas regras de
formatagao prediletas, mas se ele for trabalhar
em equipe, as regras slo dela. Uma equipe de
desenvolvedores deve escolher um único estilo
de formatagio, e, entlo, todos os membros
devem usá-lo. Desejamos que o software tenha
‘um estilo consistente. Näo queremos que pensem
que o código foi escrito por um bando de pessoas em desacordo.

‘Quando entrei no projeto FitNesse em 2002, sentei com a equipe para escolher um estilo de
programaçäo. Isso levou 10 minutos. Decidimos onde colocariamos nossas chaves, o tamanho
da endentago, como nomeariamos as classes, variáveis e métodos, e assim por diante. Entño,
ccodificamos essas regras no formatador de código de nossa IDE e ficamos com ela desde ento.
Nao eram as regras que eu preferia, mas as que foram decididas pela equipe. E como membro,
tive segui-las na hora de programar no projeto FitNesse.

Lembre-se: um bom sistema de software & composto de uma série de documentos de fácil leitura.
Eles precisam ter um estilo consistente e sutil O leitor precisa poder confiar que as formatagdes
que ele vir em um arquivo-fonte teräo o mesmo significado nos outros. A última coisa que
queremos é adicionar mais complexidade ao código-fonte programando-o com um monte de
estilos diferentes.

Regras de formatagäo do Uncle Bob

AS regras que uso sto muito simples e esto ilustradas no código da Listagem 5.6.
Considere isso um exemplo de como o código € o melhor documento padräo em programagäo.

Capitulo 5: Formatacto

Listagem 5-6
CodeAnalyzer.java

public class Codeanalyzer implesents JavaFileanalysis
private int 1inecount
Private int nexLineWi
Private int widestLinelusber;
private LineWidthiiscogran Lineltidthitistogram;
private int totalChare;

public Codeanalyzert) (
linekidthiiecogran = new Linohidtntistogran(

public static Listerilo> findsavaPiles (File parentDirectory) {
Listetiles files = now ArraylistePile>(:
findlavaFites parentDirectory, files);
return files;



private static void findJavaPiles File parentbireston
for (File file : parentDirectory.listPiles()) (
TE. (fie. getNane() ‚endshich(". Java?)
Les aa (
else if (file. iedizectory())
Finatavarilesitile, files);
1

>

public void analyzerileifile javaFile) throws Exception {
Bufferedseader br = new BufferedReader (new FileReader (javaPile} 1;
String liner
while | (Line = br.reaáLino())
‘peasureline(1ine) à
>

Last<rile> files) {

mull)

private void measureLine(String line) {

Line. Length):

Linesi
linekidthiistogram.adälinetline£ize, LinéCount);
recorawidestLine(Tinesizel

[Regras de formataçäo do Uncle Bob

9

Listagem 5-6 (continuagao)
CodeAnalyzer.java

private void recoränidesttinelint lineSizel |
iF (iineSuze > maxLinehiäth) |
axLineWieth = Linesizer
WidestLinelumber = TineCounts
;



public int gertinecoune(} {
return linecount;

public int getmaxbinenidth() (
return naxLinelidths

public int gertidestLinelusber() {
turn widestLinolluacers

public LinenidthHistogran getLinewiéchiistogram() (
return linewdchiietograny
3

public double getMeanLineWiath() {
return (Souble) fotelChars/LineCounts


public ine gekedianLireniäth() (
Integer(} sortediidtie = geiSortedkid
int cumlativeLinecaunt = 07
for (int width + sortedindthe) (
InecounsFeruidenimäth);
> Tinecsune/2)

if (cumilativeLinecount
return widths
1

throw new Error ("Cannot get


private int LineCountForwidth tint widch) {
return Jinehldthiistogran.getLinesforwidth(wiäth).size();
1

private Intoger!] getgorzeahidche() |
‘Seteinceger> widths = Linokidthiistogran.getidchs ();
Tnteger{] sortedhidths = (widths. coarray new Integer (0101;
Array sort (sorteänidehs)
return sortedNidths;

)

6

Objetos e Estruturas de Dados

Há um motivo para declararmos nossas variäveis como privadas. Näo queremos que ninguém
dependa delas. Desejamos ter a liberdade para alterar o tipo ou a implementaçäo. seja por
capricho ou impulso, Por que, ento, tantos programadores adicionam automaticamente métodos
de acesso (escrita, ou setters, e leitura, ou gerters) em seus objetos, expondo suas variáveis
privadas como se fossem públicas?

Abstragäo de dados

Considere a diferenga entre as listagens 6.1 e 6.2. Ambas representam os dados de um ponto no
plano cartesiano. Um expöe sua implementaçäo e o outro a esconde completamente,

os Capitulo 6: Objetos e Estruturas de Dados

Listagem 6-1
Caso concreto

getrheta
ar double y, double chetal;

O belo da Listagem 6.2 € que ndo hä como dizer se a implementaçäo possui coordenadas
retangulares ou polares. Pode ndo ser nenhuma! E ainda assim a interface representa de modo
claro uma estrutura de dados.

Mascla faz mais do que isso.Os métodos exigem uma regra de acesso. Vocé pode leras coordenadas
individuais independentemente, mas deve configuri-ls juntas como uma operaco atómica.

‘ALListagem 6.1, por outro lado, claramente está implementada em coordenadas retangulares,
+ nos obriga a manipulá-las independentemente. Isso expde a implementaçäo. De fato, ela seria
exposta mesmo se as variúveis fossem privadas ¢ estivéssemos usando métodos únicos de escrita
+ leitura de variáveis

Ocultar a implementagdo no € só uma questdo de colocar uma camada de fungdes entre
as variáveis. É uma questdo de ! Uma classe nio passa suas variáveis simplesmente por meio
de métodos de escrita e leitura. Em vez disso, ela expde interfaces abstratas que permite aos
usuários manipular a esséncia dos dados, sem precisar conhecer a implementagäo.

Considere as listagens 6.3 e 6.4. A primeira usa termos concretos para comunicar o nivel de
combustivel de um veiculo, enquanto a segunda faz o mesmo, só que usando . No caso concreto,
vocé tem certeza de que ali estio apenas métodos acessores (escrita e leitura, ou getter e setter)
de varidveis. No caso abstrato, ndo há como saber o tipo dos dados.

Listagem 6-3
Veículo concreto

public interface Vehicle |
double gecfuelTankcapacity
double getGal LonsOfGssol i

)

Ant-simetria data/objeto 95

Listagem 6-4
Veiculo abstrato

Em ambos os casos acima, o segundo épreferivel. No queremos expor os detalhes de nossos
dados. Queremos expressar nossos dados de forma abstata, Iso nio se consegue meramente
Aves de interfaces clou métodos de escrita e letra, preciso pensar bastante na melhor
maneira de representar os dados que um objeto contenha. A pior apso adicionar levianamente
métodos de aa e leur.

Anti-simetria data/objeto

Esses dois exemplos mostram a diferenga entre objetos e estruturas de dados, Os objetos usam
abstragóes para esconder seus dados, e expdemas fungdes que operam em tas dados. As estruturas de
“dados expôem seus dados e ndo possuem fungdes significativas. Leia este parágrafo novamente.

Note a natureza complementar das duas definigdes. Elas säo praticamente opostas. Essa
diferença pode parecer trivial, mas possui grandes implicagdes.

“Considere, por exemplo, a classe shape procedimental na Listagem 6.5. A classe Geometry
opera em trés classes shape que s3o simples estruturas de dados sem qualquer atividade. Todas
as ages esto na classe Geometry

public class Geonetry

public final double Fr = 3.141592653589783;

public double arealübject shape
{
if (shape inscanceof Square) À
Square $ = (Square) shape:
return e.aide * ri

0 Nosuc

9 Capítulo 6: Objetos e Estruturas de Dados.

Listagem 6-5 (continuacio)

return Pr + c.radice * e-radiue;
N
throw new NoSuchshapeßxceptuon();
1

Programadores de orientagäo a objeto talvez toryam o nariz € reclamem que isso é
procedimental—e estdo certos. Mas nem sempre. Imagine o que aconteceria se adicionássemos
uma fungäo perimecer() à Geometry. As classes shape mo seriam afetadas! Assim como
‘quaisquer outras classes que dependessem delas!

Por outro lado, se adicionarmos uma nova classe shape, teremos de alterar todas as fungdes em
Geometry. Leia essa frase novamente. Note que as duas situagdes sto completamente opostas.

Agora, considere uma soluçäo orientada a objeto na Listagem 6.6. O método área() €
polifórmico. No € necessäria a classe Geometry. Portanto, se eu adicionar uma nova forma,
nenhuma das fungdes existentes serdo afetadas, mas se cu adicionar uma nova funçäo, todas as
classes shape deverdo ser alteradas".

Listagem 6-6
Classes shape polifórmicas
public class
private Poin
Private double side:

public double areat) {
return siderside,

public class Rectangle implemente.
private double keightz
private double wadeh:

ape {

public double area()
return height * widen;
,

TE SR AT TR A A E

Alel de Demeter ”

Listagem 6-6 (continuagäo)
Classes shape polifórmicas
eplenents shape

private Poin"
private double

Novamente, vemos que anatureza complementar dessas duas definigdes: elas so praticamente
opostas! Isso expde a dicotomia fundamental entre objetos e estruturas de dados:

O código procedimental (usado em estruturas de dados) facilita a adiçäo de novas funcoes
sem precisar alterar as estruturas de dados existentes. O código orientado a objeto (00), por
‘outro lado, facilita a adiçäo de novas classes sem precisar alterar as fungdes existentes,

O inverso também € verdade:

O código procedimental dificulta a adiçäo de novas estruturas de dados, pois todas as
fungöes teriam de ser alteradas. O código OO dificulta a adiçäo de novas fungdes, pois
todas as classes teriam de ser alteradas.

Portanto, o que é difícil para a OO é fácil para o procedimental, e o que é dificil para o.
procedimental é fácil para a 00!

Em qualquer sistema complexo haverá vezes nas quais desejaremos adicionar novos tipos de *
dados em vez de novas fungdes. Para esses casos, objetos e OO sio mais apropriados. Por ouro
lado, também haverá vezes nas quais desejaremos adicionar novas fungdes em vez de tipos de
dados. Neste caso, estruturas de dados e código procedimental s3o mais adequados.

Programadores experientes sabem que a ideia de que tudo € um objeto é um mito. As vezes,
vocé realmente deseja estruturas de dados simples com procedimentos operando nelas.

A lei de Demeter

Há uma nova heurística muito conhecida chamada Lei de Demeter: um módulo nâo deve enxergar
interior dos objetos que ele manipula. Como vimos na segäo anterior, os objetos escondem seus
dados e expdem as operagdes, Isso significa que um objeto näo deve expor sua estrutura interna
por meio dos métodos acessores, pois isso seria expor, e náo ocultar, sua estrutura interna.
Mais precisamente, a Lei de Demeter diz que um método f de uma classe C só deve chamar
‘os métodos de:

-c
= Um objeto criado por £

{Um objeto passado como parámetro para f
Um objeto dentro de uma inslänca da variével ©

DES SE nouer

9 Capítulo 6: Objetos e Estruturas de Dados

O método ndo deve chamar os métodos em objetos retornados por qualquer outra das fungdes
permitidas. Em outras palavras, fale apenas com conhecidos, no com estranhos.

O código! seguinte parece violar a Lei de Demeter (dentre outras coisas). pois ele chama a
fungäo getScratenDit) no valor retornado de getOptions() e, entio, chama getAbsolutePath() no
valor retornado de getSeratenDi).

final String output!
getAbsoluterach();

+ = ctxt.getoptions().getScratchDiri)

Carrinhos de trem

Esse tipo de código costuma ser chamador de carrinho de trem, pois parece com um monte
de carrinhos de trem acoplados. Cadeias de chamadas como essa geralmente so consideradas
descuidadas e devem ser evitadas [G36]. Na maioria das vezes € melhor dividi-las assim:

Options opte = ctxt.getoptione(
File scratchDir = opts.getseratchDir();
final String outputDir = scratchDir.getAbsolutePath();

Esses dois pedagos de código violam a
Lei de Demeter? Certamente módulo que os
contém sabe que o objeto ctxt possui opgdes
(options), que contém um diretório de rascunho
(scratchDir), que tem um caminho absoluto
(AbsolutePath). É muito conhecimento para
uma fungdo saber. A fungdo de chamada sabe
‘como navegar por muitos objetos diferentes.

Se isso € uma violaçäo da Lei de Demeter depende se ctxt, Options ¢ SeracthDir slo
ou ndo objetos ou estruturas de dados. Se forem objetos, entáo sua estrutura interna deveria estar
culta ao invés de exposta, portanto o conhecimento de seu interior é uma violaçäo clara da I
Por outro lado, se forem apenas estruturas de dados sem atividades, entäo eles naturalmente
‘expdem suas estruturas intemas, portanto aqui ndo se aplica a li.

© uso de fungdes de acesso confunde essas questöes. Se o código tiver sido escrito como
abaixo, entio provavelmente náo estaríamos perguntando sobre cumprimento ou náo da lei.

final String outputDir = ctxt.options.scratchDir.absoluterachz

Essa questio seria bem menos confusa se as estruturas de dados simplesmente tivessem
variáveis públicas e nenhuma funçäo, enquanto os objetos tivessem apenas variáveis privadas e
fungdes públicas. Entretanto, há frameworks e padrdes (e.g... “heans”) que exigem que mesmo
estruturas de dados simples tenham métodos acessores e de alterado.

ER

Ali de Demeter »

Híbridos

De vez em quando, essa confusäo leva a estruturas híbridas ruins que sáo metade objeto e metade
estrutura de dados. Elas possuem fungóes que fazem algo significativo, e também variáveis où
métodos de acesso e de alteragdo públicos que, para todos os efeitos, tomam públicas as variáveis
privadas, incitando outras funçôes externas a usarem tais variáveis da forma como um programa
procedimental usaria uma estrutura de dados".
Esses híbridos dificultam tanto a adiçäo de novas fungdes como de novas estruturas de dados.

Eles säo a pior coisa em ambas as condigöes. Evite riá-los.Eles indicam um modelo confuso cujos
‘autores ndo tinham certeza — ou pior, no sabiam — se precisavam se proteger de fungdes ou tipos.

Estruturas ocultas

E se ctxt, options € seratchDir forem objetos com agdes reais? Entäo, como os objetos
devem ocultar suas estruturas internas, ndo deveriamos ser capazes de navegar por eles. Entio,
‘como conseguiríamos o caminho absoluto de seratehDir (‘diretério de rascunho”)?

ctxt getAbeoluteParhofseratchDirectoryoption();
ou
ctx.getSeratchDirectoryoption() .getAbsolutePath()

[A primeira opgiio poderia levar a uma abundáncia de métodos no objeto ctxt. A segunda

resume que get ScratchDirectoryOption() retorna uma estrutura de dados, e näo

um objeto, Nenhuma das opgdes parece boa. .

Se ett for um objeto, devemos dizé-lo para fazer algo; no devemos perguntá-lo sobre sua
estrutura interna. Por que queremos o caminho absoluto de sersichDr? O que faremos com ele?
Considere o código, muitas linhas abaixo, do mesmo módulo:

String outFile = outputDir + /* + className-replace(", 1/1) +
“class”

Fileoutputstream fout = new FileOutputStreamloutrile);
Buffereaoutputstrean bos = new Bufferedoutputstream(fout)

‘A mistura adicionada de diferentes niveis de detalhes [G34][G6] € um pouco confusa. Pontos,
barras, extenslo de arquivos e objetos Fi 1e ndo devem ser misturados entre sie nem com o código
que os circunda. Ignorando isso, entretanto, vimos que a intençäo de obter o caminho absoluto do
diretério de rascunho era para criar um arquivo de rascunho de um determinado nome.

Entdo, e se disséssemos ao objeto cb para fazer isso?

BufferedoutputStrean bos = ctxt.createscratchFilestream(classFileName);
“That seems like a reasonable thing for an object to do! Isso permite ao ct esconder sua estrutura
interna e evitar que a fungdo atual viole a Lei de Demeter ao navegar por objetos os quais ela
io deveria enxergar.

TTT

100 Capitulo 6: Objetos e Estruturas de Dados

Objetos de transferéncia de dados

‘A forma perfeita de uma estratur de dados é uma classe com variáveis públicas e nenhuma fungäo.

As vezes, chama-se isso de objeto de transferéncia de dados, ou DTO (sigla em inglés). Os
DTOs sio estruturas muitos üteis, especialmente para se comunicar com bancos de dados ou
analisar sintaticamente de mensagens provenientes de sockets e assim por diante. Eles costumam
se tomar os primeiros numa série de estágios de traduçäo que convertem dados brutos num banco
de dados em objetos no código do aplicativo.

De alguma forma mais comum € 0 formulärio “bean” exibido na Listagem 6.7. Os beans tem
variäveis privadas manipuladas por métodos de escrita leitura. O aparente encapsulamento dos
beans parece fazer alguns puristas da OO sentirem-se melhores, mas geralmente nao oferece
vantagem alguma.

street, String streetBxtra,
ing city. string state, String zip) (

,

public ser

Bibliograña ot

O Active Record

Os Active Records sao formas especiais de DTOs. Eles säo estruturas de dados com variáveis
públicas (ou acessadas por Beans); mas eles tipicamente possuem métodos de navegagdo, como
save (salvar) e find (buscar). Esses Active Records slo tradugdes diretas das tabelas de bancos
de dados ou de outras fontes de dados.

Infelizmente, costumamos encontrar desenvolvedores tentando tratar essas estruuras de dados
(como se fossem objetos, colocando métodos de regras de negócios neles. Isso é complicado, pois
cria um híbrido entre uma estrutura de dados e um objet.

‘A solugo, & claro, 6 tratar o Record Active como uma estrutura de dados e criar objetos
separados que contenham as regras de negócio e que ocultem seus dados intemos (que
provavelmente sio apenas instáncias do Active Record).

Conclusäo

Os objetos expdem as agdes e ocultam os dados. Isso facilita a adiçäo de novos tipos de objetos
sem precisar modificar as agdes existentes e dificulta a inclusäo de novas atividades em objetos
existentes. As estruturas de dados expdem os dados e mio possuem açôes significativas. Isso
facilita a adigäo de novas agdes ás estruturas de dados existentes e dificulta a inclusño de novas
estruturas de dados em fungdes existentes,

Em um dado sistema, ás vezes, desejaremos flexibilidade para adicionar novos tipos de
dados, ¢, portanto, optaremos por objetos. Em outras ocasides, desejaremos querer flexibilidade
para adicionar novas agdes, e, portanto, optaremos tipos de dados e procedimentos.

Bons desenvolvedores de software entendem essas quesides sem preconceito e selecionam a.
‘abordagem que melhor se aplica no momento.

Bibliografia

[Refatoragäo] Refatoracdo - Aperfeigoando o Projeto de Código Existente, Martin Fowler et
al., Addison-Wesley, 1999.

7

Tratamento de Erro

por Michael Feathers

Pode parecer estranho ter uma seçäo sobre tratamento de erro num livro sobre código limpo,
mas essa tarefa € uma das quais todos temos de fazer quando programamos. A entrada pode
“star errada e os dispositivos podem falhar. Em suma, as coisas podem dar errado, e quando isso
‘ocorre, nds, como programadores, somos responsáveis por certificar que nosso código faça o que
seja preciso fazer.

À conexo com um código limpo, entretanto, deve ser clara. O tratamento de erro domina
completamente muitos códigos-fonte. Quando digo “domina”, ndo quero dizer que eles só fazem
Kratamento de erro, mas que € quase impossivel ver 0 que o código faz devido a tantos tratamentos
de erros espalhados. Esse recurso é importante, mas se obscurecer a lógica, está errado,

‘Neste capitulo ressaltarei uma série de técnicas e consideragdes que vocé pode usar para criar
um código que seja limpo e robusto, que trate de erros com clegáncia e estilo.

104 Capítulo 7: Tratamento de Erro

Use excegöes em vez de retornar códigos

Num passado longínquo havia muitas linguagens que näo suportavam excegdes. Nelas, as
técnicas para tratar e informar erros era limitada. Ou vocé criava uma flag de erro ou retormava
um código de erro que o chamador pudesse verificar. O código na Listagem 7.1 ilustra essas
abordagens.

Listagem 7-1
Devicecontroller.java

public class DeviceController I

blac void sendshutdown(
Devieeliandle handle = getilendle (V1):
(Check the state of the device
£ (handle = Devicetandie. INVALID)
1 Save the device status Lo the
rerrievebeviceñecoré handle)
1£ not suspended, shut down
xecord.getstatut() {= DEVICE_SUSA
pauseDerice handel;
arDe: ceorkqueue (handle);
abevice handle!
i
jer log(*Device suspended. Unab

shut down

|, Jogger. log(*Tnvalid handle for: * + DEVI.tosering(}iz

O problema era que essas técnicas entupiam o chamador, que devia verificar eros
imediatamente apös a chamada. Infelizmente, facilmente se esqueciam de fazer isso. Por esse
‘motivo, € melhor langar uma exceçäo quando um erro for encontrado. O código de chamada fica
mais limpo e sua lógica nao fica ofuscada pelo tratamento de erro.
A Listagem 7.2 mostra o código depois de termos optado por langar excepdes em métodos
que podem detectar erros.

Listagem 7-2
DeviceController.java (com excecóas)

Crie primeiro sua estrutura try-cateh-finally 105

Listagem 7-2 (continuacäo):
Devicecontroller.Java (com excesses)
private void tryToshutDom() throws DeviceshutDomBrror {
Devicelandle handle = getHandie
Dovieetecord record à retrievasevicekecord (handle à

ausenevice handle);
learDevicetiorkQueve (handle)
‘losedevice (handle)

(Der

private Devicedandie get

valid handle for: * +

throw new Deviceshut Down 290)

‘Observe como fica muito mais claro. Isso no é apenas uma questäo estética, O código fica
melhor porque duas preocupapdes que estavam intrincadas, o algoritmo para o desligamento
do dispositivo e o tratamento de erro, agora estdo separadas. Voeé pode pegar cada uma delas e
estudä-las independentemente.

Crie primeiro sua estrutura try-catch-finally

seu programa. Ao executar o código na parte ty da estrutura ty..cath..naly, vocé declara que +
aquela execuçäo pode ser cancelada a qualquer momento e, ento, continuar no catch.

De certa forma, os blocos try säo como transagdes. Seu catch tem de deixar seu programa
num estado consistente, nño importa o que aconteça no try Por essa razáo, uma boa prática €
começar com uma estrutura ty..caleh.imaty quando for eserever um código que talvez lance
excegdes. Isso Ihe ajuda a definir o que o usuario do código deve esperar, independente do que
corra de errado no código que € executado no ty.

Vejamos um exemplo. Precisamos eriar um código que acesse um arquivo e consulte alguns
objetos em série.

Comesamos com um teste de unidade que mostra como capturar uma excegdo se 0
arquivo ndo existi

erestiexpected = StorageException.class)
public void retrievesectionshouléThrowOnIavalidFileName() (
SectionStore.retrievesection(*invalid - fle")

>

© teste nos leva a cria esse stub:
public List<RecordedGrip> retrievesectioníString sectionName) {

// xetorno ficticio ate que tenhamos uma implementacao real
return new ArrayList<RecordedGrip>();

106 Capitulo 7: Tratamento de Erro.

Nossoteste falha porque leno langauma excegZo. Em seguida, mudamos nossa implementagdo
de modo a tentar acessar um arquivo inválido. Essa operagdo lança uma excegäo:

public ListeRecordedGrip> retrievesection(string sectiontiame) (
try 1
Filernputstream stream = new Fileinputstream(sectionName)
) catch (Exception e) {
throw new StorageException(*retrieval error”, el:
)
return new Arraybist<RecordedGrip>():

>

Nosso teste funciona agora porque capturamos a excegäo. Neste momento, podemos refatorar.
Podemos reduzir o tipo de execugäo que capturamos para combinar com aquele que realmente
élangado pelo construtor Fi1eInputStream: FileNotPoundException

public List<RecordedGrip> retrievesection(string sectionName)
try (
Piletaputstream stream = new FileInputStream(sectionName);
strean.close);
} catch (FileNotFoundException e) (
throw new StorageException("retrieval error", el

3
return new ArrayList<RecordedGrip (+


Agora que definimos © escopo com uma estrutura try. catch, podemos usar o TDD para
construir o resto da lógica que precisamos, que será adicionada na criagáo do FielnputStream do
‘dose e poderä fingir que nada de errado aconteceu.

Experimente criar testes que forçam excegdes e, entdo, adicione a ago ao seu tratador para
‘cumprir seus testes. Isso fard com que vocé crie primeiro o escopo de transagdo do bloco y e the
ajudard a manter essa natureza de transaçäo daquele escopo.

Use exceçôes náo verificadas

A discussäo acabou. Por anos, programadores Java tém discutido sobre as vantagens €
desvantagens de excegdes verificadas. Quando a verificaçäo exceydes surgiu com a primeira
‘versio do Java, parecia uma ótima ideia. A assinatura de todo método listaria todas as encegdes
que ele passaria ao seu chamador. Ademais, essas exceçôes eram parte do tipo do método, Seu
código literalmente nao seria compilado se a assinatura ndo fosse a mesma da que seu código
podia fazer.

Naquela época, pensamos que excepdes verificadas fosse uma ideia ótima; e era, elas tinham
“algumas vantagens, Entretanto, ficou claro agora que elas ndo säo necessárias para a produgäo
de um software robusto. O C# ndo verifica exceçäes. e, apesar das tentativas, nem o C++, Nem
‘mesmo Python ou Ruby. Ainda assim € possivel criar um software robusto em todas essas
linguagens, porque, nesse caso, temos de decidir se realmente as excegdes verificadas valem o
prego que se paga.

‘Que prego? O de verificar excegdes € a violagdo do Principio de Aberto-Fechado

Se vocé langar uma exceçäo a ser verificada a partir de um método em seu código € 0

rie primeiro sua estrutura try-catch-fnally 107

catch estiver trés niveis acima, será preciso declard-la na assinatura de cada método entre
vocé e o catch. Isso significa que uma modificagäo em um nivel mais baixo do software pode
forgar a alteragdo de assinaturas em muitos niveis mais altos. Os módulos alterados podem ser
jos, mesmo que nada inerente a eles tenha sido mudado.
Considere a hierarquia de chamadas de um sistema grande. As fungdes no topo chamam
que chamam outras abaixo delas e ad infinitum. Agora digamos que uma das
is mais baixos seja modificada de uma forma que ela deva langar uma excegäo.
Se essa exces for verificado, entio a assinatura da funçäo deverá adicionar uma instruçäo
‘tows. Mas isso significa que cada funçäo que chamar nossa fungdo modificada também deverá
seralterada para capturar a nova excegäo ou anexar a instruçäo throws apropriada a sua assinatura.
‘Ad infinitum. O resultado aninhado é uma cascata de alteragdes que vio desde os niveis mais
baixo do software até o mais alto! Quebra-se o encapsulamento, pois todas as fungdes no caminho
de um langamento (throw) devem enxergar os detalhes daquela exceçäo de nivel mais baixo.
Segundo o propósito de excegdes de que elas Ihe permitem tratar eros distantes, € uma pena que
as excegóes verificadas quebrem dessa forma o encapsulamento.

‘As exceydes verificadas podem ás vezes ser iteis se vocéestiver criando uma biblioteca critic
€ preciso capturi-las. Mas no desenvolvimento geral de aplicativo, os custos da dependéncia
superam as vantagens.

Fornega exceçôes com contexto

‘Cada exceg3o langada deve fomecer contexto o suficiente para determinar a fonte ea localizagäo
de um erro. Em Java, vocé pode pegar um stack trace de qualquer excegáo; entretanto, ele näo
‘consegue Ihe dizer o objetivo da operaçäo que falhou,

Crie mensagens de erro informativas € as passe juntamente com as excegdes. Mencione a
‘operagio que falhou e o tipo da falha, Se estiver registrando as agdes de seu aplicativo, passe
informagdes suficientes para registrar o erro de seu catch.

Defina as classes de excegóes segundo as necessidades
do chamador

Há muitas formas de classificar eros. Pode ser pela origen: eles vieram desse componente ou
daquele? Pelo tipo: sáo falhas de dispositivos, de redes ou erros de programaçäo? Entretanto,
quando definimos as classes de excegdo num aplicativo, nossa maior preocupagdo deveria ser
como elas sdo capturadas.

Vejamos um exemplo de uma classificagdo ruim de exceçäo. Aqui, há uma estrutura try.
catch..finally para uma chamada a uma biblioteca de outro fabricante, Ela cobre todas as
excegdes que a chamada talvez lance:

ACMEPOFE port = new ACMEPort(12);

try 1
port.epen():
} catch (DeviceResponseException e) {
reportPortError(e):
logger.log("Device response exception”, e);
} catch (ATMI212Unlockedexception e) (
report PortError (0);

108 Capitulo 7: Tratamento de Erro.

logger-log(*Unlock exception”, eh
) catch (GHXError e) {

reportPortErrorle);

logger.log("Device response exception");
) finally (



A estrutura possui muita dupicagdo,e lo deveriamos star surpresos. Na maioria dos casos
de trtamento de excegdes, o que fazemos é relativamente padrio, independente da situagño no
‘momento, Temos de registrar um erro € nos cetficar que podemos prossegui,

‘Neste caso. como sabemos que atarefa que estamos fazendo €basicamenteamesmaindependente
da exceso, podemos simplificar nosso código consideravelmente. Para isso, pegamos a API que
‘estamos chamando e garantimos que ela retorne um tipo comum de exceso.

LocalPort port = new LocalPort(12);

try (
port.open():

} catch (PortDevicerailure e) {
reportError (
logger-logle.getMessage(), e);

} finally (

>

Nossa classe Local ort € um simples wrapper (“empacotador”) que captura € traduz as.
cexceges langadas pela classe ACMEPort:

public class LocalPort (
private ACMEPort innerPort;

public LocalPort(int portNumber) (
innerPort = new ACMEPort(portNumber);
>

public void open() {
try €
innerPort .opent}:
) catch (DeviceResponseException el (
‘throw new PortDevicePailurele)
) catch (ATW1212UnlockedException e)
throw new PortDevicerailure(e);
) catch (GuxError e) {
throw new PortDevicerailure(e);

)

>

Wrappers como o que definimos para a acmeport. podem ser muito üteis. Na verdade,
empacotar APIs de terceiros é a melhor prática que existe. Ao fazer isso, vocé minimiza as
dependéncias nelas: vocé pode escolher migrar para uma biblioteca diferente no futuro sem

Defina o Auxo normal 109

‘muitos problemas. Empacotar também f
testar seu proprio código.

Uma última vantagem de empacotar (wrapping) é que vocé nâo fica preso ás escolhas do
modelo de API de um fomecedor em particular. Vocé pode defini a API que preferir. No exemplo
anterior, definimos um único tipo de exceçäo para a falha do dispositivo port e descobrimos que
Poderiamos escrever um código muito mais limpo.

Geralmente, uma única classe de exceçäo está bom para uma parte específica do código.
As informagdes enviadas com a exceçäo podem distinguir os erros. Use classes diferentes
apenas se houver casos em que vocé queira capturar uma exceçdo € permitir que a outra
passe normalmente.

ta a simulagdo de chamadas de terceiros quando for

Defina o fluxo normal FS

Se vocé seguir os conselhos das segdes anteriores, 97
acabará com uma boa quantidade de divisäo entre
sua lógica do negócio e seu tratamento de erro
A maioria de seu código comegará a parecer um
algoritmo limpo e sem apetrechos. Entretanto, esse
processo eleva ao máximo a detecpdo de erro em
seu programa. Vocé empacota suas APIs de modo
que voce possa langar suas próprias excegdes €
definir um controlador acima de seu código para
que vocé possa lidar com qualquer computaçäo
cancelada. Na maioria das vezes, essa € uma abordagem ótima, mas ha situagdes nas quais vocé.
talvez no queira cancelar.

Vejamos um exemplo. Abaixo está um código confuso que soma as despesas em um aplicativo
de finangas:

ery (
MealExpenses expenses = expenseReportDA0.getMeals (employee.
getID();
m_total += exponses.getTotal();
Y catchíMealExpensesMotFound e) {
‘m_total += getMealPerDiem();

;

Neste negócio, se as refeigdes (meals) forem um custo, elas se tornam parte do total. Caso.
contririo, o funcionário (employee) recebe uma quantia para ajuda de custos (PerDiem) pela
refeiçäo daquele dia. A exceçäo confunde a lógica.

‘Nao seria melhor se ndo tivéssemos de lidar com o caso especial? Dessa forma, nosso código.
seria muito mais simples. Ele fcaria assim:

MealExpenses expenses = expenseReportDA0.geteals(employee.gotTD()):
m_total += expenses.getTotal();

Podemostomarocódigomaissimples?Parecequesim.Podemosalterara ExponseRepor tDAO
de modo que ela sempre retorne um objeto MealExpense. Se näo houver gastos com refeigdes,

no Capítulo 7: Tratamento de Erro.

ela retorna um objeto Meal Expense que retoma a ajuda de custos como seu total
public class PerDienMealExpenses implemente MealExpenses (
public int gerrotal() (
// retorna a ajuda de custos padrao

)

1550 se chama o Padräo Special Case (‘padräo do caso especial’), de Fowler, Vocé cria uma
classe ou configure um objeto de modo que ele trate de um caso especial para voce. Ao fazer
isso, o código do cliente nao precisa lidar com o comportamento diferente, Este fica encapsulado.
num objeto de caso especial

Náo retorne null

“Acho que qualquer discussäo sobre tratamento de erro deveria inclui as coisas que fazemos que
levam a erros. A primeira da lista seria retornar null. Já perdi a conta dos aplicativos que já vi que
‘em quase toda linha verificava por null. Abaixo está um exemplo:

public void registeritem(item item) {
if (item != mull) (
TtemRegistry registry = perisventstore.getrtemmegistry();
Af (registry != null) (
Item existing = registry.getItem(iten.getzD());
Af (existing.getBillingPerioa()-hasRetailowner()) (
existing.register(item):

)

Se vocé trabalhar num código-fonte como esse, ele pode nio parecer táo ruim assim para
vocé, mas ele €! Quando retomamos nu11. basicamente estamos criando mais trabalho para nés
‘mesmos e jogando problemas em cima de nossos chamadores. Só basta esquecer uma verificagdo
LL para que o aplicativo fique fora de controle,

Percebeu que ndo havia uma verificagdo de null na segunda linha do if aninhado? O que
teria acontecido em tempo de execugäo se persistentstore fosse nul1? Teríamos uma
NuL1PointerException em tempo de execuçäo, e ou alguém está capturando-a no nivel mais
alto ou ndo, Em ambos os casos isso € péssimo. O que vocé faria exatamente em resposta a um
lançamento de Nul1PointerExcept ion das profundezas de seu aplicativo?

E fácil dizer que o problema com o código acima é a falta de uma verificado de null.
mas, na verdade, o problema € que ele tem muitos. Se vocé ficar tentado a retomar nui de um
método, em vez disso, considere langar uma exceco ou retomar um objeto SPECIAL CASE.
Se estiver chamando um método que retorne nul a partir de uma API de terceiros, considere
empacotá-lo com um método que lance uma exceço ou retorne um objeto de caso especial.

"Em muitos casos. objetos de casos especiais sio uma soluçäo Fácil. Imagine que seu código.
fosse assim:

List<Employee> employees = getEmployees();
if (employees != null) (
for (Employee e : employees) (

Nao passe null m

totalpay += e.getray();

»

[Neste momento, getEmployees pode retornar null, mas ele precisa? Se alterássemos getEmployee
de modo que ele retomasse uma lista vazia, poderiamos limpar o código:

List<Employec> employees = getEmployees {) ;
for(Employee e : employees) {
totalPay += e.getPay();

}

Felizmente, Java possui o Collections.emptyList), ¢ ele retorna uma lista predefinida e imutävel
que podemos usar para esse propósito:

public List <Enployee> getEmployees() (

if( .. there are no employees .. }

return Collections.emptyList ():
)

Se programar dessa forma, vocé minimizará a chance de Nullrointer&xceptions € seu
código será mais limpo.

Náo passe null

Retomar nu11 dos métodos & ruim, mas passar null para eles € pior. A menos que esteja
trabalhando com uma API que espere receber null, voce deve evitar passi-lo em seu código,
sempre que possivel. =

Vejamos um exemplo do porqué. Abaixo está um método simples que calcula a distincia
entre dois pontos:

public class MetricsCalculator

public double xProjection(Point pl, Point p2) {
return (p2.x = pLx) * 1.5
>



‘© que acontece quando alguém passa nu) como parámetro?

calculator.xProjection{null, new Point (12, 13)12

Receberemos uma Nut 1Pointersxception, é claro.

Podemos consertar isso? Poderiamos criar um novo tipo de exceçäo e langá-lo:
public clase Netricstalculster

public double xProjection(Point pl, Point p2) {
Af {pl == null || p2 == mull) (

m “Capítulo 7: Tratamento de Erro

throw InvalidargumentException(
“invalid argument for
MotricsCalculator-xProjection");
y
return (p2.x - pl.x) * 1.

>

Ficou melhor? Pode ser um pouco melhor do que uma excegio de ponteiro null, mas lembre-se
de que temos de definir um tratador para Tnval idargument Exception. O que ele deve fazer?
Há algum procedimento bom?

Há uma alternativa. Poderiamos usar uma série de confirmagdes:

public class Metricscalculator
(
public double xProjectioníPoint pi, Point p2 (
assert pl != null : "pl näo pode ser mulo*;
assert p2 != null : *p2 nao pode ser nulo”;
return (p2.x = pLx) * 1.55



É uma boa informaçäo, mas no resolve o problema. Se alguém passar nul1, ainda teremos um
erro em tempo de execuçäo.

Na maioria das linguagens de programaçäo ndo há uma boa forma de lidar com um valor nulo
passado acidentalmente para um chamador. Como aqui este é o caso, a abordagem lógica seria,
Proibir, por padräo, a passagem de ru 1. Ao fazer isso, vocé pode programar com 0 conhecimento
de que um nu12 numa list de parámetros é sinal de problema, e acabar com mais alguns erros
por descuido.

Conclusáo

‘Um código limpo é legivel, mas também precisa ser robusto. Esses objetivos nao sáo conflitantes.
Podemos criar programas limpos e robustos se enxergarmos o tratamento de erro como uma.
preocupaglo à parte, algo que seja visivel independentemente de nossa lógica principal. Na
medida em que somos capazes de fazer isso, podemos pensar nisso de forma independente e dar
um grande passo na capacidade de manutengäo de nosso código.

Bibliografia

[Martin]: Agile Software Development: Principles, Patterns, and Practices, Robert C. Martin,
Prentice Hall, 2002.

8

Limites

por James Grenning

Raramente controlamos todos os softwares em nossos sistemas. De vez em quando compramos.
‘pacotes de outros fabricantes ou usamos códigos livres, ou dependemos de equipes em nossa propria
‘empresa para construir componentes ou subsistemas para nös. De algum modo, devemos integrar,
de forma limpa, esse código extemo ao nosso. Neste capítulo veremos as präticas e técnicas para
manter limpos 0s limites de nosso software

14 ‘Capitulo 8: Limites

O uso de códigos de terceiros

Há uma tensto natural entre o fomecedor de uma interface e seu usuirio. Os fornecedores de
pacotes e frameworks de outros fabricantes visam a uma maior aplicailidade de modo que
possam trabalhar com diversos ambientes e atender a um público maior. Já os usuários desejam
“uma interface voltada para suas próprias necessidades. Essa tensdo pode causar problemas nos
limites de nossos sistemas.

“Tomemos 0 java-ut2.Map como exemplo. Como pode ver na Figura 8.1, os Maps tem
‘uma interface bastante ampla com diversas capacidades. Certamente esse poder e Nexibilidade
‘fo Geis, mas também pode ser uma desvantagem. Por exemplo, nosso aplicativo pode construir
um map e passé-lo adiante, Nosso objetivo talvez seja que nenhum dos recipientes de nosso
Map nio exclua nada do Map. Mas logo no inicio da lisa está o método clear (). Qualquer
usuärio do Map tem o poder de apagi-Io. Ou talvez, segundo a convençäo que adolamos, 0 Map
pode armazenar apenas certs tipos de objetos, mas nio € certo que ele restrinja os tipos que
So adicionados a ele. Qualquer usuário determinado pode adicionar ¡tens de qualquer tipo a
qualquer Nap.

‘Se nosso aplicativo precisa de um Map de Sensrs,vocé pode se deparar com um Sensors assim:

clear() void - Map
containskey(Object key) boolean - Map
Sontainsvalue(object value) boolean - Map
enerySet() Set - Map

equals (Object 0) boolean - Map

get (Object key) Object - Map

Getclass() Classe? extends Object> - Object
hashCode() int - Map

äsEnpty() boolean ~ Map

keySet() Sot - Map

notify() void - Object

notifyALLO void - Object

putiobject key, Object value) Object - Map
PULALI(Map €) void - Map

remove(Object key) Object - Map

+ size() int - Map

+ tostring() String ~ Object

© values) Collection - Map

+ wait) void - Object

waitílong timeout) void - object
Wait(long timeout, int nanos) void - Object

Figura 8.1: Métodos do Map
‘Se nosso aplicativo precisar de um Map de sensors, vocé talvez encontre sensors assim:
Map sensors = new HashMapO;

E, quando alguma outra parte do código precisa acessar o Sensor, voce vé isso:

Sensor s = (Sensor)sensors.get(sensorté );

uso de códigos de tercciros us

Näo vemos isso apenas uma vez, mas varias ao longo do código. O cliente deste código fica com
a responsabilidade de obter um Object do ap e atribuílo otipo certo. Apesar de funcionar, ndo.
um código limpo. Ademais, esse código ndo explica muito bem o que ele faz. Pode-se melhorar
consideravelmente sua legibilidade com o uso de tipos genéricos, como mostra abaixo:

Map<Sensor> sensors

new HashMap<Sensor>():

Sensor $ = sensors.get(sensorid );

Entretato, isso ndo resolve o problema de que Map<sens
precisamos ou queremos,

Passar adiante pelo sistema uma insáncia de Map<Sensor> significa que haverí vários
lugares para mexer se a interface para o Map mudar. Voé talvez pense que uma mudanga seja
pouco provável, mas lembre-se de que house uma quando o suporte a genéricos foi adicionado
o Java 5. De fato, já vimos sistemas que impedem o uso de genéricos devido à magnitude das
alteragües necessáras para manter o uso abrangente dos aps.

"Abaixo está uma forma limpa de usar o map. Nenhum usuário do Sennor se importaria se
um pouco de genéricos for usado ou no. Essa escolha se tomou (e sempre deve ser) um detalhe
da implementacáo,

‘oferece mais eapacidade do que

public class Sensors {
private Map sensors = new HashMap();

public Sensor getayldistring 10) {
return (Sensor) sensors.ger(id)

,
¿codigo
>

A interface no limite (tap) está oculta. É possivel alter-ta causando muito pouco impacto no
resto do aplicativo. O uso de tipos genéricos náo & mais uma quest o problemática assim,
pois o gerenciamento de declarapdes e de tipos é feito dentro da classe Sensoxo,

Essa interface também foi personalizada para satisfazer as necesidades do aplicativo. Seu
resultado um código mais fil dese entender e mais dificil dese utilizado incoretamente. À classe
Sansors pode forgarregras de modelo e de negócios.

‘Nioestamos sugerindo que cada uso do map sejaencapsulado dessa forma. Mas he aconselhando
para ndo passar os Maps (ou qualquer outra interface num limite) por todo o sistema, Se usar uma
Interface, como a Map, no limite, a mantenha numa classe ou próxima a uma familia de clases em
‘que ela possa ser usada. Evite retorn-la ou aceitdJa como parämetro em APIs públicas.

Explorando e aprendendo sobre limites

Códigos de terceiros nos ajudam a obter mais funcionalidade em menos tempo. Por onde comegar
‘quando desejamos utilizar pacotes de terceiros? Nao étarefa nossa testä-los, mas pode ser melhor
para nés criar testes para os códigos externos que formos usar.

‘Suponha que näo esteja claro como usar uma biblioteca de terceiros. Podemos gastar um dia
‘ou dois (até mais) lendo a documentagäo e decidindo como vamos usd-la. Ento, esereveriamos

116 Capítulo 8: Limites

nosso código para usar o código extemo e vemos se ele € ou näo o que achávamos. No
ficariamos surpresos de acabar em longas sessdes de depuraçäo tentando descobrir se os bugs
‘que encontramos so do nosso código ou no deles.

Entender códigos de terceiros € dificil. Integrá-lo ao seu também é. Fazer ambos ao mesmo
tempo dobra a dificuldade. E se adotéssemos uma outra abordagem? Em vez de experimentar e
tentar o novo código, poderiamos criar testes para explorar nosso conhecimento sobre ele. Jim
‘Newkirk chama isso de festes de aprendizagem’.

Nesses testes, chamamos a API do código extemo como O faríamos ao usá-la em nosso
aplicativo.

Basicamente estaríamos controlando os experimentos que verificam nosso conhecimento
daquela APL

O teste se focaliza no que desejamos saber sobre a API.

Aprendendo sobre log4j

Digamos que queremos usar o pacote 10943 do Apache em vez de nosso proprio gravador de
registro interno.

Baixariamos o pacote e entäo abririamos a página de documentaçäo inicial. Sem ler muito,
criamos nosso primeiro caso de teste, esperando que seja impresso “oi” no console.

erect
public void testLogCreatel) (
Logger logger = Logger.getLogger(*MyLogger*):
Jogger.infol"oi”);

3

Quando o executamos, o registrador (logge) produz um erro o qual nos diz que precisamos de algo
chamado Appender. Após lerumpouco mais, descobrimos queexisteum Consolenppender. Entlo,
criamos um ConsoleAppender e vemos se desvendamos os segredos de registro no console

grest
public void testLogaddappender() (
Logger logger = Logger.getLogger (-MyLogger”):
Consoleappender appender = new Consoleapponder();
logger.addappender appender);
Logger.info(roi");
3

Desta vez, descobrimos que o Appender ndo possui fluxo de saida. Estranho... Parecia lógico ter
um. Depois de buscar ajuda no Google, tentamos o seguint

rest

public void testLogaddappendez() {
Logger logger = Logger.getLogger("HyLogger");
Togger.removeAlläppenders
logger.addAppender (new Consoleappender(
new PatternLayout("8p At Amin”),
Consoleappender.S¥STBM_OUT)):
Logger.info("oi");

Aprendendo sobre logs) u

Funcionou. Uma mensagem de registro com “oi” apareceu no console! Parece estranho ter de
dizer a0 Consoleappender o que ele precisa escrever no console. Mais interessante ainda é
quando removemos o parámetro Consoleappender. SystenOut e ainda é exibido “oi”. Mas
quando retiramos o Pat ternLayout, mais uma vez hä mensagem de falta de um fluxo de saida.
Esse comportamento é muito estranho.

Lendo a documentaçäo com um pouco mais de atençdo, vimos que o construtor
Consoleappender padrdo vem “desconfigurado”, o que no parece muito óbvio ou prático.
Parece um bug, ou pelo menos uma inconsisténcia, no 10943. Recorrendo novamente ao Google,
lendo e testando, acabamos chegando à Listagem 8.1. Descobrimos bastante coisa sobre como
funciona o 10343, e colocamos esse conhecimento numa série de testes simples de unidade.

Listagem 8-1 a]
Logrest. java
public clase Logtest 1

Le Logger loggers

void initialize() (
sgor = Logger. getLozger ("longer");
Éppenders à

rest
public void basieLogger (
Basieconfigurater.con

logger. info (*basicLog


fe Void adaappendorwithseream() {
acéappender (new Consoleäppender
‘ew PatternLayout ("tp tt tata"),
ender SYSTEM OU};
fo(*addippenderWithstrean*):

atest
public void addAppendernithoutstreamt) {
longer. adäkppander (new Conso leRppender
‚eternlayout (“dp dr Re”
nto "adhppenderhichoutstrean);

Agora sabemos como obter um console simples e inicializado de registro, e podemos
‘encapsular esse conhecimento em nossas classes de registro de modo que o resto de nosso
aplicativo fique isolado da interface limite do log,

Os testes de aprendizagem sáo melhores que de graca
Os testes de aprendizagem acabam náo custando nada, Tivemos de aprender sobre a API
mesmo, e escrever aqueles testes foi uma forma fil e scparada de obter o conhecimento que

us Capitulo 8: Limites

conseguimos. Os testes de aprendizagem so experimentos precisos que ajudam a aumentar
nosso entendimento.

Esses testes no só saem de graga como geram um retorno positivo de nosso investimento,
Quando houver nossas distribuigdes daquele pacote externo, podemos executar os testes para ver
se há diferengas nas atividades.

Os testes de aprendizagem verificam se os pacotes de terceiros que estamos usando se
comportam como desejamos. Uma vez integrados, nflo há garantias de que o código se manterá
compativel com as nossas necessidades. Os autores originais sofreräo pressäo para alterarem
seus códigos para satisfazer suas próprias necessidades. Eles consertardo bugs e adicionardo
novos recursos. Cada distribuigño vem com um novo risco. Se o pacote for alterado de uma
forma que fique incompativel com nossos testes, descobriremos de imediato,

‘Voeé precise ou nio do conhecimento proporcionado pelos testes de aprendizagem, deve-se
definir um limite claro por meio de uma série de testes extemos que experimentem a interface da
‘mesma forma que seu código faria, Sem esses restes limite para facilitar a migraçäo. poderemos
ficar tentados a manter por mais tempo do que deveriamos a versio antiga,

O uso de código que náo existe ainda

Há outro tipo de limite, um que separa o conhecido do desconhecide. Geralmente há lugares
no código onde nosso conheeimento parece sumir. As vezes, o que está do outro lado nos €
desconhecido (pelo menos agora). ÁS vezes, optamos no olhar alt do limite

Alguns anos ats fiz parte do time do desenvolvimento de sofware para um sistema de
comunicagdo de rádios, Havia um subsistema, o “Transmisor”, que eu pouco sabia à respeito,
+ as pessoas resporsiveis porel ndotinham ainda definido sua interface, No queriamos fear
parados, ento comesamos a trabalharlonge daquela pare desconhecida do código.

Sabíamos muito bem onde nosso mundo terminava e onde comegava o novo. Conforme
trabalhávamos, ás vezes chegávamos a ess límite entre os dois mundos. Embora névoas e
ravens de ignoráncia ofuscassem nossa viso para além do limite, nosso trabalho nos mostrou 0
que queriamos que fosse a interface limite. Desejvamos dizer ao transmissor algo assim

Configure o transmissor na frequéncia fornecida e emita uma representacáo analógica dos
dados provenientes desde flux.

Nao tinhamos ideia de como isso seria feito, pois a API ainda ndo havia sido desenvolvida.
Portanto. decidimos trabalhar nos detalhes depois.

Para náo ficarmos parados, definimos nossa própria interface. Demos um nome fácil de
lembrar, como Transmitter. Criamos um método chamado transmit que pegava uma
frequéncia e um fluxo de dados. Essa era a interface que gostaríamos de ter.

‘© bom de criar a interface que desejamos € que podemos controlä-la. Isso ajuda a manter o
código do lado do cliente mais legivel e centralizado na fungdo para a qual fora criado,

Na Figura 8.2 vocé pode ver que preenchemos as classes do ConmunicationeControl ler
a partir da API do transmitter, a qual ndo controlávamos e havia sido definida. Ao usar
nossa própria interface para o aplicativo, mantivemos limpo e expressivo nosso código
do Communicationscontroller. Uma vez definida a API do transmitter, eriamos 0

© uso de código que no existe ainda no

Transnitterädapter para fazer a conexto, O ADAPTER? encapsulou a interaçäo com a APT
+ fomeceu um único local para ser modificado se a API for aperfeigoad

ah er
E] Fe

Figura 8.2: Adivinhando deve ser 0 transmitter

Esse modelo também nos dá um seam? bastante conveniente no código para testarmos,
Ao usar um FakeTransmitter (transmissor falso) adequado, podemos testar as classes
do CommunicatonsControler, Além de podermos criar testes limite uma vez que temos a
“Transmi tterAPT para garantir que estamos usando corretamente a API.

Limites limpos

Coisas interessantes ocorrem nos limites. A alteragdo € uma delas. Bons projetos de software
modam modificagdes sem muito investimento ou trabalho. Quando usamos códigos que
stáo fora de controle, deve-se dar uma atençäo especial ao nosso investimento e garantir que
‘uma mudanga futura ndo seja muito custosa.
O código nos limites precisa de uma divisäo clara e testes que definem o que se deve esperar.
Devemos evitar que grande parte de nosso código enxergue as particularidades dos de terceiros.
É melhor depender de algo que vocé controle do que pegar algo que acabe controlando vocè.
Lidamos com os limites de códigos extemos através de alguns poucos lugares em nosso código
que fazem referéncia a eles. Podemos empacoté-los como fizemos com o Map, ou talvez usar um
ADAPTER para converter nossa interface perfeita na que nos for fomecida. De qualquer forma,
‘nosso código se comunica melhor conosco, prové uso consistente interno pelo limite e possui
poucos pontos para serem mexidos quando o código extemo sofrer alteragäo.

Bibliografia

[BeckTD!

est Driven Development, Kent Beck, Addison-Wesley, 2003.

IGOFI: Padröes de Projeto, Solugöes Reutilizäveis de Software Orientado a Objeros, Gamma
tal, Addison-Wesley, 1996.

[WELC|: Working Effectively with Legacy Code, Addison-Wesley, 2004.

ae ss R

9

Testes de Unidade

Nossa profissäo evoluiu bastante ao longo dos últimos dez anos. Em 1997 ndo se ouvia falar
‘em Desenvolvimento Dirigido a Testes (TDD, sigla em inglés). Para a grande maioria de nós,
os testes de unidade eram um pequeno pedago de código descartävel que escreviamos para nos.
‘eertficar que nossos programas funcionavam. Meticulosamente criävamos nossas classes €
métodos e, entäo, improvisávamos um código para testá-los. Tipicamente, isso envolvia um
programa simples de controle que nos permitisse interagir manualmente com o programa que
havíamos escrito,

Lembro-me de ter criado em meados da década de 1990 um programa em C++ para um
sistema integrado em tempo real. Era um simples contador com a seguinte assinatura:

12 Capítulo 9: Testes de Unidade

void Timer::ScheduleConmand(Command* theCommand, int milliseconds)

‘A idéia era simples: o método execute de Command seria executado numa nova thread após
um número especifico de milésimos de segundos. O programa era como testi-lo.

Criei um simples programa de controle que esperava alguma agdo no teclado. Sempre que um
caractere era pressionado, ele agendaria um comando que esereveria o mesmo caractere cinco
‘segundos depois. Entlo eu digitava uma melodia no teclado e esperava que ela fosse reproduzida
na tela cinco segundos depois.

“Eu … quero-uma-mulher … igual... -com-quem-me-ca .. sei... querido... pa … pa.”

Realmente cantei essa melodia enquanto dig
quando aparecia na tea,

Esse era meu teste! Depois que o vi funcionar e o mostrei aos meus colegas, joguei o código.
do teste fora

Como eu disse, nossa profissio evoluiu. Atualmente eu criaria um teste que garantisse que
‘cada canto do código funcionasse como eu esperava. Eu isolaria meu código do resto do sistema
‘operacional em vez de apenas invocar as fungdes padrdo de contagem:; simularia aquelas fungdes
¿de modo que eu tivesse controle absoluto sobre o tempo; agendaria comandos que configurassem
flags booleanas; e, ento, adiantaria o tempo, observando as flags e verificando se elas mudavam.
de falsas para verdadeiras quando eu colocasse o valor correto no tempo.

‘Quando pego uma colegäo de testes para passar adiante, cu me certifie se eles sto adequados
para serem executados por qualquer pessoa que precise trabalhar com o código, e se eles € 0
código estavam juntos no mesmo pacote de origem.

Isso, progredimos bastante, mas ainda podemos ir mais longe. Os movimentos do Agile e
do TDD tem incentivado muitos programadores a criarem testes de unidade automatizados, ¢
muitos outros estäo se unindo a cada dia. Mas nessa correria para adicionar testes 20 nosso
oficio, muitos programadores tém se esquecido de alguns dos pontos mais sutis e importantes de
se escrever bons testes.

As trés leis do TDD

jtava o ponto ".” e, entäo, a cantava novamente

Hoje em dia todos sabem que o TDD nos pede para criar primeiro os testes de unidade antes do
código de produgdo. Mas essa regra é apenas o inicio. Considere as trés les! abaixo:

Primera Lei Nao se deve escrever o código de produgo até criar um teste de unidade de falhas.
‘Segunda Lei Nao se deve escrever mais de um teste de unidade do que o necessärio para falhar,
‘endo compilar é falhar.

Tercelra Lei Nao se deve escrever mais códigos de produgto do que o necessärio para aplicar 0
teste de falha atual.

1

Essas trösleis Ihe colocam numa rotina que talvez dure trinta segundos. Os testes € 0 código.
de produçäo sio escritos juntos, com os testes apenas alguns segundos mais adiantados,

Se trabalharmos dessa forma, criariamos dezenas de testes a cada dia, centenas a cada més
e milhares a cada ano; os testes cobririam praticamente todo o nosso código de produgdo.
© tamanho completo desses testes, que pode competir com o tamanho do proprio código de
producto, pode apresentar um problema de gerenciamento intimidador.

Como manter os testes limpos

Há alguns anos, pedi para orientar uma equipe que tinha decidido explicitamente que seus códigos
de testes ndo deveriam ser preservados segundo os mesmos padröes de qualidade que seu código
‘de produgäo. Eles se deram autorizagdo para violar as leis em seus testes de unidade. “Rápida e
porcamente” era o lema. As variáveis nio precisavam de nomes bem selecionados, as fungdes
de teste näo tinham de ser curtas e descritivas. Os códigos de testes ndo precisavam ser bem
desenvolvidos e divididos de modo pensado, Se que o resto dos códigos de testes funcionasse ©
que cobrisse o código de produçäo, já era o suficiente,

‘Alguns de vocés lendo isso talvez simpatizem com tal decisdo, Talvez, lá no passado, vocé criow
testes tipo aquele que fiz para aquela classe Timer. É um grande passo ir da criagdo daquele tipo de
teste descartável para uma colegio de testes de unidade automatizados. Portanto, assim como a equipe
que eu orientara, vocé talvez decida que testesfeios “poreamente” sejam melhores do que nada,

‘O que aquela equipe no percebera era que ter testes daquele tipo € equivalente, se no pior,
a nio ter este algum. O problema € que muitos testes devem ser alterados conforme o código de
produgio evolui. Quanto pior o teste, mais dificil será de mudá+lo. Quanto mais confuso for o
“Código de teste, sio maiores as chances de vocé levar mais tempo espremendo novos testes para
¿entro da colegáo do que na criagäo do código de produgäo. Conforme vocé modifica o código
de produgäo, os testes antigos comegam a falhar e a bagunga no código de teste dificulta fazé-
los funcionar novamente. Sendo assim, os testes comegam a ser vistos como um problema em
constante crescimento.

‘De distribuigäo em distribuigdo, o custo de manutengo da coleçäo de testes de minha equipe
aumentou. Com o tempo, isso se tomou a maior das reclamagdes entre os desenvolvedores. Quando
68 gerentes perguntaram o motivo da estimativa de finalizaçäo estava ficando to grande, os
desenvolvedores culparam os testes. No final, foram forgados a descartar toda colegio de testes

Mas, sem uma colegäo de testes, eles perderam a capacidade de garantir que, aps alteragdes
‘no código-fonte, ele funcionasse como o esperado e que mudangas em uma parte do sistema ndo
afetariam as outras partes. Entäo, a taxa de defeitos comegou a crescer. Conforme aumentava 0
"número de bugs indesejäveis, comegaram a temer fazer alteragdes € pararam de limpar o código
de produgäo porque temiam que isso poderia fazer mais mal do que bem. O código de producto
‘comegou a se degradar. No final das contas, ficaram sem testes, com um código de produgäo
‘confuso e cheio de bugs, com consumidores frustrados e o sentimento de que 0 esforgo para
eringäo de testes ndo valeu de nada.

De certa forma estavam certos. Tal esforgo tinha sido em väo. Mas fora decisdo deles permitir
que os teste ficassem uma bagunga e se tornasse a origem do fracasso. Se tivessem mantido os
testes limpos, o esforgo para a criago dos testes ndo teria os deixado na mio. Posso dizer isso
‘com um pouco de certeza, pois participei de tudo, e jé orientei muitas equipes que obtiveram
sucesso com testes de unidade limpos.

124 Capítulo 9: Testes de Unidade

A moral da história € simples: Os códigos de testes säo 180 importantes quanto o código de
produçäo. Ele nao é componente secundärio. Ele requer raciocinio, planejamento e cuidado. E
preciso manté-lo 10 limpo quanto o código de produgdo,

Os testes habilitam as “-idades”

Se nilo mantiver seus testes limpos, irá perdé-los. E, sem eles, vocé perde exatamente o que
mantém a flexibilidade código de produgäo. Isso mesmo, vocé leu certo. Sdo as testes de
unidade que mantém seus códigos flexiveis, reuilizäveis e passiveis de manutençäo. A razdo &
simples. Se vocé tiver testes, nfo terá medo de alterar o código! Sem os testes, cada modificagäo
pode gerar um bug. Nao importa o grau de flexibilidade de sua arquitetura ou de divisto de seu
‘modelo. pois sem 05 testes vocé ficará relutante em fazer mudangas por temer gerar bugs nfo
detectados.

Mas com os testes esse medo praticamente some. Quanto maior a cobertura de seus testes,
‘menor o medo. Vocé pode fazer mudanças quase sem penalidade ao código que tenha uma
arquitetura emaranhada € um modelo confuso e opaco. De fito, vocé pode improvisar essa
arquitetura e esse modelo sem temer!

Portanto, ter uma coleçäo de testes de unidade automatizados que cubram o código de
produçäo € o segredo para manter seu projeto e arquitetura os mais limpos possiveis. Os testes
habilitam todas as “-idades”, pois eles possibilitam as alteragdes.

Dessa forma, caso seus testes estejam ruins, entAo sua capacidade de modificar seu código fica
comprometida e vocé começa a perder a capacidade de melhorar a estrutura dele. Quanto piores
forem os testes, por o código se toma. No final, vocé perde os testes e seu código se degrada.

Testes limpos

© que toma um teste limpo? Trés coisas: legibilidade, legibilidade e legibilidade. Talvez isso
scja até mais importe nos testes de unidade do que no código de produgäo. O que torna os testes
legiveis? O mesmo que torna todos os códigos legiveis: clareza, simplicidade e consisténcia de
expressäo. Num teste vocé quer dizer muito com o mínimo de expressôes possiveis.

Considere o código do FitNesse na Listagem 9.1. Esses trés testes slo dificeis de entender
© certamente podem ser melhorados. Primeiro, há uma quantidade terrivel de código duplicado
165] nas chamadas repetidas a addPage e assertSubSting. Mais importante:
está carregado de detalhes que interferem na expressividade do test.

Listagem 9-1
SeralzedPageRespondertest java

void tostoetPageieratchyäskel

throws Exception.

‘Testes Limpos 125

Listagem 9-1 (continuagäo)
SarialzodPagoResponderTest java

request serfescurce(*z00t")
request addinput (*type", "page
Responder responder = new SerializedageRespond
SinpleResponse response
Ginpletesponee) responder makeResponse (
aeeContext (FO), request)
String xml = resgense.getContent (1

aseertiquals(*text/xal', response. getCont ert 1ype());
aseertsubstring|"<nane>FageOne</nase>*, xAl)
Aseert Substring "<nane>FageTwo</nane>*, Xe):
Sssertoubstring|"enaseschi1dOnec name>‘, sl):

public void testGetragel

Ehrovs Exception
WikiPage pagetne = craxler.addPage(root, Pathvarser.parse:
‘cavler.agaPage(root, FethParcer -parse(*Pagedne.chiidone")
Érauler adaPage root, PathParser parse(*Pagerwo”))5

catchy ssi] DoesneContasnsymbol tink:

Pagebata dara = pageone.getpatal):
wikiFagerroperties properties = date.getProperties():
WikiFageProgerty symlinks - properties.cet (Symbol icPage. PROF
symbinks.sec ("SyaPage", PAG");

‘pegedne.conmat (daca)

request setresoureo( root”
request addImput("Lype", "pages*)
Responder responder = new SeriallzedFageResponder();
Simpleresponse response =
(Sinplefespense) responder makeRecponco(
new FitkesseContext (1001), request);
String aml = response.getContént();

assertBquals(*text/mnl*, response.getContenttype()}:
AsserrSubString"cnane>Pageünec/nane>", là
assert uaString ("crane Pagetwo</nane>*, xl!
Assert Substring “<namescheld0nec!nane>*, aml)
lassertNot Substring *SynPage*, onl}

public void testoecpatadsital() throws Exception
y
crawler.adéPage(root, PathParser.parse(*TestPage0ne"), “test

request .setResource "Testegedne")
request adalmput (*type, “data"):

Por exemplo, veja as chamadas para PathParser. Elas transformam strings em instáncias
PagePath pelos crawler. Essa trunsformagdo & completamente irrelevante na execuçdo do
teste e serve apenas para ofuscar o objetivo. Os detalhes em tomo da criagäo do responder € a
definigäo do tipo de response também so apenas aglomerados. E tem a inepta forma pela qual

126 ‘Capitulo 9: Testes de Unidade

‘éconstruido URL requisitado a partir de um resource e um parámetro. (Ajudei a eserever esse
código, portanto me sinto completamente no direito de eriticé-Io).

No final, esse código nao foi feito para ser lido. O pobre leitoré inundado com um monte de
detalhes que devem ser entendidos antes que os testes fagam algum sentido. Agora, considere os
testes improvisados na Listagem 9.2. Eles fazem exatamente o mesmo, mas foram refatorados
de uma forma muito mais clara e deseritiva.

Listagem 9-2
SorializeaPageResponderTest Java (refatorado)

public void cestcetrageni
nakePages *Pagedne*,

rchyäskall) throws Exception (
ageOne.ChildOne", *Pageruo”) +

submiteequest (*root", “type:pages"):

assertResponselsiN.()
AssortResponseconta ing {
ScnanesPogetnec/néne>*, ‘<nane>RageTwoc/nane>*, *nane>childone</nasie>*

public void cestsyabolcbinksAeeNot Tata Pagel
WikiPage page - makopage *Fageone"
Hakopageo("Pageone.Childcne", "Fagerwo

throws txcepcion

binkTo(page, *Fagetwo", “SynPage

SubnitRequost (root, "typeipage

asserthesponselskkL()

assertResponseConcains
‘cnanesFagedne</nane>*, "enano

assert ResponseDoestiotContain\*SyaFage

yeTvo</nanes*, "nameschildonee/naae:

public void testgetDatakstel() throws Exception
akeragedithcontent (*TestPagetne", "test page");

ype:daca’):

submitRequest ("Test Pageoue",
ascercReeponseTsXHL.t):

assertresponsecontelns(*test page", “<Test");

A estrutura desses testes tomou óbvio o padrio CONSTRUIR-OPERAR-VERIFICAR:
Cada um dos testes está claramente dividido em trs partes. A primeira produz os dados do teste,
a segunda opera neles ea tercera veria se a operaçäo gerou os resultados esperados.

Note que a grande maioria dos detalhes magantes foi eliminada. Os testes vio direto ao
ponto e usam apenas os tipos de dados e funçües que realmente precisam. Quem ler esses
testes dev ser capaz de descobri rapidamente o que fazem, sem se deixar enganar ou ficar
sobrepujado pelos detalhes.

Testes Limpos 12

Linguagem de testes especifica ao dominio

Os testes na Listagem 9.2 mostram a técnica da construgáo de uma linguagem específica a um
dominio para seus testes. Em vez de usar as APIs que os programadores utilizam para manipular 0
sistema, construimos uma série de fungdes e utilitários que usam tais APIs e que tomam os testes
mais convenientes de se escrever e mais fäceis de le. Esses fungdes e utlitrios se tomaram uma
API especializada usada pelos testes. Eles testam a linguagem que os programadores usam para
‘auxiliélos a escrever seus testes e para ajudar áqueles que precisem ler esses testes mais tarde.

Essa API de teste o foi desenvolvida de uma s6 vez; ela evoluiu de uma continua refatoragao
de códigos de testes que ficaram demasiadamente ofuscados por detalhes confusos. Assim como
vocé me viu refatorar a Listagem 9.1 para Listagem 9.2, desenvolvedores disciplinacos também
refatoram seus códigos de teste para formas mais sucintas e expressivas.

Um padrao duplo

De um certo modo, a equipe que mencionei no inicio deste capítulo estava certa. O código dentro
da API de teste tem um conjunto diferente de padrôes de engenharia em relaçäo ao código de
produgäo. Ele pode ser simples, sucinto e expressivo, mas ndo precisa ser mais eficiente do
que o do código de produçao. Apesar de tudo, ele roda num ambiente de teste, e nao de um de
produçäo, e esses dois ambientes possuem requisitos diferentes.

Considere o teste na Listagem 9.3 que escrevi como parte de um sistema de controle de
ambiente que eu estava fazendo a prototipagem. Sem entrar em detalhes, posso Ihe dizer que o
teste verifica se o alarme de temperatura baixa, o aquecedor e o ventilador estäo todos ligados
‘quando a temperatura estiver “fia demais”.

Listagem 9-3
Environment Controllertet

Java

throws Exception
rue ts. neaverstate())

assert tre (me

SsserePalse he, cooler

hw ni Perpal

tn. Lorna

Há, é claro, muitos detalhes aqui. Por exemplo, o que faz a funçäo tic? Na verdade, prefiro
que vocé no se atenha a isso ao ler esse teste, mas que se preocupe apenas se vocé concorda ou
no se o estado final do sistema é consistente com a temperatura sendo “fia demais”.

Note que, ao lero teste, seus olhos precisam ler e reler entre o nome do estado sendo verificado
© a medida do estado sendo verificado. Vocé vé heaterstate e, ento, seus olhos deslizam
para a direita, para asserttrue. Vocó vé coolerstate e seus olhos devem se voltar para a

128 Capítulo 9: Testes de Unidade

esquerda, para asserc#alse. Isso é entediante e falivel, além de dificultar a leitura do teste.
Aperfeigoei a legibilidade deste teste consideravelmente na Listagem 9.4.

Listagem 9-4
‘Environment ControlierTest Java (refatorado)

snOnLeTeephlaraAtThreshold() throws Exception {

wayToocold|) 2
Assortkgun)

Höchtr, hu.gtstata())

É claro que criei uma funçäo wayTooCo1d para ocultar o detalhe da funçäo tic. Mas o que
se deve notar & a estranha string em assertEquals. As letras maiüsculas significam “ligado” e
as minúsculas “desligado”, e clas sempre seguem a seguinte ordem: (hear, blower, cooler, hitemp-
‘alarm, lo-tom-alar)

Mesmo que esse código esteja perto de violar a regra do mapeamento mental”, neste caso
parece apropriado. Note que, uma vez conhecendo o significado, seus olhos deslizam sobre a
string e vocé logo consegue interpretar os resultados. Torna-se quase uma satisfag3o ler o teste.
Dé uma olhada na Listagem 9.5 e veja como é fácil entender esses testes.

Listagem 9-5
EnvironmentControllerTest.java (selegäo maior)
public void rurnônCoolerandBlower1frcohot () throws Exception (

assortiquals(*hachl*, hu. getStatet))

public void turnCnlioaterandBlowerlToocold() throws Exception (

public void turn
vayTookor |)

TespAlaraatThreshola() throws Exceprion |

hu. getscate()

atest
public void turacnLoTengMlarsAtTAreshold() thros on
wayTo0co14 (|
Astertämalsi"HächLt, hw.getstate());

A fungao gotstate está na Listagem 9,6. Note que esse código nio é muito eficiente. Para isso,
provavelmente eu deveria ter usado um StringButer.

‘Testes Limpos 120

himemalara 2 °°: 2h
Tofenpalara 2 Le : "105

As StingBuffer sto um pouco feias. Mesmo no código de produgio irei eviti-las se o custo for
pequeno; e vocé poderia argumentar que o custo do código na Listagem 9.6 € bastante pequeno.
Contudo, esse aplicativo está claramente cm um sistema integrado em tempo real, e € mais
provävel que os recursos do computador e da meméria sejam muito limitados. O ambiente de
teste, entretanto, ndo costuma ler limitagóes alguma.

Essa € a natureza do padräo duplo. Há coisa que vocé talvez jamais faga num ambiente de
produçäo que esteja perfeitamente bem em um ambiente de teste. Geralmente, isso envolve
questdes de eficiéncia de memória e da CPU. Mas nunca de clareza.

Uma confirmaçäo por teste

Há uma escola de pensamento* que diz que cada fungdo de teste em um teste JUnit deve ter um e
‘apenas uma instrugdo de confirmaglo (assert). Essa regra pode parecer perversa, mas a vantagem
pode ser vista na Listagem 9.5. Aqueles testes chegam a uma única conclusño que € fácil e rápida
de entender.

Mas e a Listagem 9.2? Parece ilógico que poderiamos de alguma forma facilmente adicionar

a confirmagäo se a saida está em XML e se ela contém certas substrings. Entretanto, podemos
dividir o teste em dois, cada um com sua própria confirmagaio, como mostra a Listagem 9.7.
Listagem 9-7
SerializedPageresponderrest java (Confirmagio Única)

public void restGet?ageierarchyäskel() throes Excepción (
diventagee(*Pagedne’, "Tageöne.Cnildöne", “Paper

‘whenfequestistssved(*roct", “type:pages"):

thenespenseshouldBent.()

ctyfisaRight Tans!) throws Exception (
ages Pagcones,, "Pages. Ch ones. Page"

vyperpages"):

donee names?

130

Note que mudei os nomes das fungdes para usar a convengdo comum dado-quando-entBo, Isso
facilita ainda mais ciura dos testes. Infelizmente, como pode ver, dividir os testes pode gerar
muito código duplicado.

Podemos usar o padrio Template Method para eliminar a duplicagdo e colocar as partes
dado quando na clase base € as partes entáo em derivadas diferentes. Ou poderiamos criar uma
classe de teste completamente separada colocar as parts dado e quando na funçäo @Betre e
as partes quando em cada fungäo Gest. Mas isso parece muito trabalho para uma questio t20
pequena. No final, prefiro as confirmagócs mltiplas na Listagem 9 2

Acho que a regra da confirmagdo única é uma boa orientagao”. Geralmente tento criar uma
linguagem de teste para um dominio específico que a use, como na Listagem 9.5. Mas ndo tenho
receio de colocar mais de uma confirmagáo em um teste. Acho que a melhor coisa que podemos
dizer que se deve minimizar o número de confirmagdes em um este.

Um único conceito por teste

Talvez a melhor regra seja que desejamos um único conceito em cada funçäo de teste. Näo
queremos fungdes longas que saiam testando várias coisas uma após a outra. A Listagem 9.8
€ um exemplo disso. Esse teste deve ser dividido em rés independentes, pois ele testa us
‘coisas distintas. Juntá-los todos na mesma funçäo obriga 0 leitor comprender o objetivo de cada
unçäo presente e o que ela testa,

Listagem 9-8

+ Miscellaneous testo for the addWonths() method

SerialDate.createinscance(31, 5, 2004);

&2 = SertalDate.adíMenths 2, al);
‘getDsyOtMonthi |
A

2004, &2.9et¥F0¥(N);

43 = Sorialpace.adawonthe(2, dl
getDayotventht))
Sssertequals(7, 6}.gestlonth())
assercequals(2004, 6 .getvver ti}

Serialete Date ada ate.adadonthe(L, Ali):
aeeertguals(30, 34, gotDayOtMonth (|

assertequals(?, 4. gerNonth ))
assertEquals (2004, “a4.get¥7W 1)

As tés füngdes de teste devem seguir provavelmente assi

+ Dado o último dia de um més com 31 dias (como maio):

rd

{Um único conceito por teste BI

1. Quando vocé adicionar um més cujo último dia seja o 30° (como junho), entio a data deverá.
sero dia 30 daquele més, e ndo a 31

2. Quando vocé adicionar dois meses äquela data cujo último dia seja o 31°, entáo a data deverá
sero dia 31.

+ Dado o último dia de um més com 30 dias (como junho):
1. Quando voe# adicionar um més cujo último dia seja o 31°, entäo a data deverä ser día 30 enäo 31

Explicado dessa forma, vocé pode ver que há uma regra geral oculta nos testesdiversos. Ao incrementar
o més, a data no pode ser maior do que o último dia daquele més. Iso implica que incrementar o més
em 28 de feverciroresultaria em 28 de margo. Esse teste está faltando e seria itil criá-o.

Portanto, nio so as confirmages mültiplas em cada socio da Listagem 9.8 que causa o problema.
É o fato de que há mais de um conceito sendo testado. Assim, provavelmente, a melhor regra seja
minimizar o número de confirmagdes por conceito e testa apenas um conceito por fingáo de teste.

ELRS.T:

‘Testes limpos seguem outras cinco regras que formam o acrónimo em inglés acima (Fast
Independent, Repeatable, Self-validating, Timely)

Rapidez os testes devem ser rápidos. Devem executar com rapidez. Quando os testes rodam
devagar, vocé ndo desejari executá-los com frequéncia. E, consequentemente, nflo encontrará
problemas cedo o bastante para conserti-los facilmente. E vocé náo se sentirá livre para limpar
o código, que acabará se degradando.

Independéncia os testes náo devem depender uns dos outros. Um teste ndo deve configurar as
condiçües para o próximo, Vocé deve ser capaz de executar cada teste de forma independente e
ma ordem que desejar. Quando eles dependem uns dos outros, seo primeiro falhar causará um
fito dominó de falhas, dificultando o diagnóstico € ocultando os defeitos abaixo dele.

Repetitividade deve-se poder repetir os testes em qualquer ambiente. Vocé deve ser capaz de
efetuar testes no ambiente de produgäo, no de garantia de qualidade e no seu notebook enquanto
volta para casa de trem sem uma rede disponivel. Caso seus testes ndo possam ser repetidos em
‘qualquer ambiente, entdo vocé sempre terá uma desculpa para o motivo das falhas. E também
perceberá que nâo consegue rodar os testes fora o ambiente adequado.

Autovalidagáo os testes devem ter uma saída booleana. Obtenham ou nio éxito, vocé náo deve
ler um arquivo de registro para saber o resultado, Vocé nio deve ter de comparar manualmente
dois arquivos texto para ver se os testes foram bem sucedidos. Se os testes ndo possuirem
autovalidaçäo, entdo uma falha pode se tomar subjetiva, e executar os testes pode exigir uma
longa validaçäo manual.

12 Capítulo 9: Testes de Unidade

Pontualidade os testes precisam ser escritos em tempo hábil. Devem-se criar os testes de unidade
imediatamente antes do código de produgäo no qual serio aplicados. Se cri-los depois, o teste
do código de produçäo poderá ficar mais dificil. Ou talvez vocé ache que um pouco do código
de produçäo seja complexo demais para testar, Ou talvez vocé no crie o código de produçäo de
maneira que possa ser testado.

Conclusáo

Aqui abordamos apenas 0 inicio deste tópico. De fato, acho que deveria ter um livro inteiro
sobre Testes limpos. Os testes sto to importantes para a saúde de um projeto quanto o código
de produgäo. Talvez até mais, pois eles preservam e aumentam a flexibilidade, capacidade de
manutençäo e reutilizas3o do código de produgdo. Portanto, mantenha seus testes sempre limpos.
Trabalhe para torná-los sucintos e expressivos. Invente APIS de teste que funcionem como uma
linguagem específica a um dominio que Ihe ajude a criar seus testes,

Se deixar os testes de degradarem, seu código também iri, Mantenha limpos os seus testes

Bibliografia

IRSpecl: RSpec: Behavior Driven Development for Ruby Programmers,
Aslak Hellesoy, David Chelimsky, Pragmatic Bookshelf, 2008.

IGOFI: Padrées de Projeto, Solugöes Reutilizäveis de Software Orientado a Objetos, Gamma
tal, Addison-Wesley, 1996.

10

Classes
com Jeff Lange

At agora, este livro se focou em como escrever bem linhas e blocos de código. Mergulhamos
na composiçäo apropriada para funçôes e como elas se interrelacionam. Mas apesar de termos
falado bastante sobre a expressividade das instrugdes do código e as fungdes que o compdem,
ainda náo teremos um código limpo até discutirmos sobre os niveis mais altos da organizagáo do
código. Vamos fala agora sobre classes limpas.