O documento apresenta um resumo de dois capítulos do livro "Clean Code" de Robert Martin sobre funções e comentários em código. O apresentador André Justi discute princípios como: funções devem ser pequenas e fazer uma coisa; usar nomes descritivos; evitar comentários quando possível explicando-se no código; usar Javadocs em APIs públicas.
1. Grupo de Estudos
SAJ-ADV
Tema: Livro Clean Code (Código Limpo) de Robert Martin
Capítulos 3 e 4
Apresentado por: André Justi
Grupo de Estudos
SAJ-ADV
Tema: Livro Clean Code (Código Limpo) de Robert Martin
Capítulos 3 e 4
Apresentado por: André Justi
08/01/2014
3. Funções
“Nos primórdios da programação, formávamos nossos sistemas
com rotinas e sub-rotinas. Já na era do Fortran e do PL/1,
usávamos programas, subprogramas e funções. De tudo isso,
apenas função prevaleceu. As funções são a primeira linha de
organização em qualquer programa.”
4. Funções: Pequenas
A primeira regra para funções é que elas devem ser pequenas. A
segunda é que a primeira regra nunca deve ser quebrada.
Com pouca experiência podemos perceber que é muito mais
fácil dar manutenção, refatorar, documentar e aplicar testes em
funções pequenas.
Segundo Robert Martin funções devem ter entre 1 a 5 linhas.
5. Funções: Blocos e endentação
Entro de instruções como if, else, while, for e outros. Devem ter
apenas uma linha. Possivelmente uma chamada de outra função.
Além de manter a função pequena, isso adiciona um valor
significativo, pois a função chamada de entro do bloco pode
receber um nome descritivo, isso também implica que que as
funções não devem ter estruturas aninhadas. Portanto, o nível
de endentação de uma função deve ser de, no máximo, um ou
dois níveis. Isso, é claro, facilita a leitura e compreensão da
funções.
6. Funções: Blocos e endentação
Exemplo ruim:
private List<String> convertePessoasJaxbParaXml(List<Pessoa> pessoas) {
List<String> pessoaXmls = new ArrayList<>();
for (Pessoa pessoa : pessoas) {
Writer writer = new StringWriter();
try {
JAXB.marshal(pessoa, writer);
pessoaXmls.add(writer.toString());
} catch (DataBindingException dataBindingException) {
throw new HttpErroAoConverterObjetoJaxbParaConteudo(dataBindingException);
} finally {
IOUtils.closeQuietly(writer);
}
}
return pessoaXmls;
8. Funções: Faça apenas uma coisa
As funções devem fazer apenas uma coisa e devem cumprir bem
o objetivo para o qual são destinadas.
O problema dessa declaração é que é difícil saber o que é “uma
coisa”.
9. Funções: Faça apenas uma coisa
Pense no seguinte cenário:
1. Determina se a página é de teste;
2. Se for, inclui o setUps;
3. Exibe a página em HTML.
Essa descrição é uma ou são três coisas?
Se uma função faz apenas uma série de passos de forma
declarativa, então ela está fazendo uma só coisa. Apesar de tudo
o motivo para criarmos função consiste em decompor um
conceito maior (em outras palavras, o nome da função) em uma
série de passos no próximo nível de abstração.
10. Funções: Um nível de abstração por função
A fim de certificarmos que nossas funções estão fazendo "uma
coisa", precisamos ter certeza de que toda lógica dentro da
nossa função são todos no mesmo nível de abstração.
É fácil ver como o próximo exemplo viola essa regra. Existem
conceitos de lá que estão em um nível muito alto de abstração,
como “converter o conteúdo dos todos objetos em uma lista de
String em XML” outros que estão em um nível baixo de
abstração, como: “fazer a conversão em si e tratar os possíveis
erros”.
11. Funções: Um nível de abstração por função
Misturar níveis de abstração dentro de uma função torna as
coisas confusas. Quem está lendo o código não é capaz de dizer
se uma determinada expressão é um conceito essencial ou um
detalhe.
private List<String> convertePessoasJaxbParaXml(List<Pessoa> pessoas) {
List<String> pessoaXmls = new ArrayList<>();
for (Pessoa pessoa : pessoas) {
Writer writer = new StringWriter();
try {
JAXB.marshal(pessoa, writer);
pessoaXmls.add(writer.toString());
} catch (DataBindingException dataBindingException) {
throw new HttpErroAoConverterObjetoJaxbParaConteudo(dataBindingException);
} finally {
IOUtils.closeQuietly(writer);
}
}
return pessoaXmls;
12. Funções: Ler o código de cima para baixo: Regra
Decrescente
Uma função bem implementada pode ser lida de cima para
baixo, como uma narrativa ou conjunto de tópicos.
private List<PessoaJaxb> consultarPessoasPorNome(String nomePessoa, ParametrosConsulta parametrosConsulta) {
validarParametrosConsultas(parametrosConsulta);
List<Pessoa> pessoas = this.pessoaDao.consultarPessoasPorNome(nomePessoa, parametrosConsulta);
List<PessoaJaxb> pessoasJaxb = converterPessoasParaPessoasJaxb(pessoas);
return pessoasJaxb;
}
13. Funções: Use nomes descritivos
Não devemos ter medo de fazer uma função com nome longo.
Um nome longo e descritivo é melhor do que um curto e
enigmático. Um nome longo também é melhor do que um
comentário longo descritivo.
Seja consistente em seus nomes. Use as mesmas frases (palavras
conceito/chave) em outros módulos para criar uma convenção.
Isso permite que tenhamos uma de fácil dedução da função.
14. Funções: Parâmetros de funções
Sempre devemos usar um numero pequeno de parâmetros
tentando não ultrapassar 3 parâmetros. Um numero menor de
parâmetro também facilita o entendimento da função e testes
em cima dela. Funções com muitos parâmetros exigem inúmeros
testes, por existem inúmeras possibilidades.
15. Funções: Parâmetros lógicos
Essa forma é ruim. Passar um booleano para um função
certamente é uma má pratica, isso mostra explicitamente que a
função faz mais de uma coisa. Normalmente uma se o
parâmetro for verdadeiro e outra se o parâmetro for falso.
16. Funções: Evite efeitos colaterais
Os efeitos colaterais são mentiras. Sua função promete fazer
uma coisa, mas ele também faz outra.
Às vezes ele vai fazer mudanças inesperadas em variáveis ??da
própria classe.
Às vezes ele vai fazer mudanças inesperadas em parâmetros
passados na função ou parâmetros globais da aplicação.
Em ambos os casos elas são "verdades" enganosas e prejudiciais,
que geralmente resultam em comportamentos estranhos.
17. Funções: Separação comando/consulta
As funções devem fazer ou responder algo, mas não ambos. Sua
função ou altera o estado de um objeto ou retorna informações
sobre ele. Efetuar as duas tarefas costuma gerar confusão.
18. Funções: Evite repetição
Evite sempre a repetição. Sempre que uma função ou parte se
repetir, torne elas comuns ou extraia apenas o bloco repetido
para outra função comum.
19. Comentários
“Não insira cometários em um código ruim, reescreva-o”.
- Brian W. Kernighan
Nada pode ser tão útil quanto um comentário bem colocado,
nada consegue ser tão prejudicial quanto um velho comentário
mal feito e que fala mentiras e informações incorretas.
20. Comentários: Comentários compensam um código ruim
Uma das motivações mais comuns para criar cometários é um
código ruim, construímos um módulo e sabemos que está
confuso e desorganizado. Estamos cientes da bagunça. Nós
mesmos dizemos “Oh é melhor inserir um comentário!”. Não...
acredite é melhor para e limpar a bagunça.
21. Comentários: Explique-se no código
Certamente há vezes em que não é possível expressar-se direto
no código. Infelizmente, devido a isso, muitos desenvolvedores
assumiram que o código raramente é, se é que possa ser, um
bom meio para se explicar. Evidentemente isso é falso.
22. Comentários: Explique-se no código
Oque você preferiria ver?
private void editarPessoa(Pessoa pessoa) {
if (pessoa == null
|| pessoa.getCodigo() == null
|| pessoa.getCodigo() < 1
|| this.pessoaDao.pessoaExiste(pessoa.getCodigo())) {
//Lança erro informando que pessoa não existe.
}
this.pessoaDao.salvarPessoa(pessoa);
}
23. Comentários: Explique-se no código
Ou isso?
private void editarPessoa(Pessoa pessoa) {
if (this.verificaExistenciaPessoa(pessoa)) {
//Lança erro informando que pessoa não existe.
}
this.pessoaDao.salvarPessoa(pessoa);
}
private boolean verificaExistenciaPessoa(Pessoa pessoa) {
if (pessoa == null) {
return false;
}
Long codigoPessoa = pessoa.getCodigo();
if (pessoa.getCodigo() == null || pessoa.getCodigo() < 1) {
return false;
}
return this.pessoaDao.pessoaExiste(pessoa.getCodigo());
}
24. Comentários: Comentários informativos
Às vezes é pratico fornecer informações básicas em um
comentário.
/** Regex para identificação do parâmetro 'import' da tag page com case insensitive. */
private static final String REGEX_PARAMETER_IMPORT = "(?i)import=["'].+?(["'])";
25. Comentários: Alerta sobre consequências
Às vezes é útil alertar outros desenvolvedores sobre certas
consequências. Por exemplo. o comentário abaixo explica
porque um caso de teste em particular está desabilitado.
Mesmos assim sempre existem outras possibilidades.
// Teste está comentado porque demorar muito para ser testado, só execute se tiver tempo.
//
// @Test
// public void verificaPessoasCpfInvalidoTest() {
// //Implementação
// }
@Test
@Ignore("Só execute se tiver tempo")
public void verificaPessoasCpfInvalidoTest() {
//Implementação
}
26. Comentários: Comentários TODO
Às vezes é cabível deixar notas “TODO” em comentários. TODO
explica por que a função tem uma implementação degradante
ou um ponte de revisão. Vale lembrar que existem outras formas
de prever isso como ferramentas de revisão de código.
27. Comentários: Javadocs em APIs públicas
Não há nada tão prático e satisfatório como uma API pública
bem descrita. Os Javados para a biblioteca Java padrão são um
exemplo. Mesmo com um código ruim é possível usar seus
artefatos.
28. Comentários: Comentários ruins
A maioria dos comentários está nessa categoria. Geralmente
eles são suportes ou desculpas para um código de baixa
qualidade ou justificativa para a falta de decisões.
29. Comentários: Comentários enganadores
Às vezes, com as melhores intenções, um desenvolvedor faz uma
afirmação não muito clara em seus comentários. Lembre-se: se
vai escrevê-los, invista tempo e escreva-os bem.
30. Comentários: Comentários imperativos
É basicamente tolo ter uma regra dizendo que todo artefato
deve ter um Javadoc. Isso muitas vezes é redundante, torna o
código mais complexo e desorganizado e muitas vezes aumenta
em muito o custo do desenvolvimento.
31. Comentários: Comentários ruidosos
Às vezes você vê comentários que nada são além de “chiados”.
Eles dizem o óbvio.
/**
* Recupera o código da pessoa.
*
* @return Código da pessoa.
*/
public Long getCodigoPessoa(){
this.codigoPessoa;
}
32. Comentários: Evite comentários se é possível usar uma
função ou uma variável
Às vezes podemos usar funções ou variáveis bem nomeadas ao
invés de comentários.
Oque você preferiria ver?
private void editarPessoa(Pessoa pessoa) {
//Verifica se pessoa existe
if (pessoa.getCodigo() == null
|| pessoa.getCodigo() < 1
|| this.pessoaDao.pessoaExiste(pessoa.getCodigo())) {
//Lança erro informando que pessoa não existe.
}
this.pessoaDao.salvarPessoa(pessoa);
}
33. Comentários: Evite comentários se é possível usar uma
função ou uma variável
Ou isso?
private void editarPessoa(Pessoa pessoa) {
if (this.verificaExistenciaPessoa(pessoa)) {
//Lança erro informando que pessoa não existe.
}
this.pessoaDao.salvarPessoa(pessoa);
}
34. Comentários: Créditos e autoria
Os sistemas de controle e versionamento de código são muito
bons para lembrar quem alterou ou adicionou algo ao código.
Não há necessidade de poluir o código com isso.
/* Adicionar por André Justi */