Clean Code – FUNÇÕES
Funções são a primeira linha de organização em qualquer programa.
- Pequenas!: A primeira regra para funções é que elas devem ser pequenas. Antigamente se dizia que uma função não deveria ser maior que o tamanho da tela.
O problema disso hoje em dia, é que temos muitos tamanhos de tela haha. Funções devem ter NO MÁXIMO 20 linhas.
- Blocos e indentação: Blocos dentro de instruções if, else, while e outros devem ter apenas uma linha. Possivelmente uma chamada de função. O nível de indentação deve ser de no máximo um ou dois.
- Faça apenas uma coisa: Se uma função faz apenas aqueles passos em um nível abaixo do nome da função, então ela está fazendo apenas uma coisa. O motivo de termos funções, é para decompor um conceito maior em uma série de passos no próximo nível de abstração.
“As funções devem fazer apenas uma coisa, Devem fazê-la bem. Devem fazer apenas ela.”
Uma forma de saber se uma função faz mais de uma coisa, é se você pode extrair outra função à partir dela, que não seja apenas uma reformulação de sua implementação
- Ler o código de cima para baixo – Regra decrescente: O código deve ser lido de cima para baixo, como uma narrativa. Cada função deve ser seguida pelas outras no próximo nível de abstração de cada vez, conforme percorremos a lista de funções.
Cada função deve descrever o nível atual de abstração, fazendo referência aos parágrafos consecutivos no próximo nível.
- Instruções switch: Apesar de ser difícil criar uma estrutura switch pequena, podemos nos certificar que cada switch está em uma classe de baixo nível e nunca é repetido. Para isso, usamos o polimorfismo. É aceitável que o switch apareça apenas uma vez, escondida atrás de uma relação de herança de forma que o restante do sistema não a enxergue.
- Use nomes descritivos: Um nome extenso mas explicativo é melhor que um nome pequeno e enigmático, bem como, melhor que um comentário extenso e descritivo.
Usar nomes descritivos esclarecerá o modelo de módulo na sua mente. Procure utilizar os mesmos nomes e verbos nos nomes de funções no seu código.
- Parâmetros de funções: O número ideal de parâmetros passados para funções é zero.
- Funções mônades: Um parâmetro de entrada, é a melhor coisa depois de zero parâmetros. Se uma função for transformar o parâmetro de entrada, então a alteração deve aparecer como o valor retornado. Ex.:
StringBuilder transform(StringBuffer in)
ainda é melhor quevoid transform(StringBuffer out)
mesmo que a implementação do primeiro retorne o parâmetro de entrada.
- Parâmetros lógicos: É feio passar um parâmetro booleano para uma função, pois nos mostra que a função faz mais de uma coisa. Uma chamada
render(true)
é muito confusa para um leitor simples. Analisar a chamadarender(Boolean isSuite)
ajuda um pouco, mas mesmo assim, esta função poderia ser dividida emrenderForSuite( )
erenderForSingleTest( )
, por exemplo.
- Parâmetros lógicos: É feio passar um parâmetro booleano para uma função, pois nos mostra que a função faz mais de uma coisa. Uma chamada
- Funções díades: Estas funções são ainda mais difíceis de entender que as mônades. Quando lemos funções com mais de um parâmetro, tendemos a ignorar o primeiro parâmetro quando fazemos uma pausa para entender o segundo.
E é aí que moram os bugs. Há casos é claro, que precisaremos de dois parâmetros, Ex.:Point p = new Point(1,2)
(Claro que, neste caso os dois parâmetros são componentes de um único valor)
Podemos contornar a situação transformando a função em mônades. Ex.:- Tornar o método
writeField
um membro deoutputStream
, de modo que se possa chamaroutputStream.writeField(name)
; - Tornar
outputStream
membro da classe em uso, a fim de não precisar passar por parâmetro
OU
Você poderia ainda extrair uma nova classe, comoFieldWriter
, que receba ooutputStream
em seu construtor e possua o métodowrite( )
.
- Tornar o método
- Funções tríades: Funções que recebem 3 parâmetros são consideravelmente mais difíceis de entender do que as díades. As questões de ordenação, pausa e ignoração apresentam mais do que o dobro de dificuldade.
- Objetos como parâmetros: Procure reduzir o número de parâmetros através da criação de objetos. Quando grupos de variáveis são passadas juntas, como:
Circle make(double x, double y, double radius);
é provável que sejam parte de um conceito que mereça um nome só pra ele. Ex.:Circle make(Point center, double radius);
- Objetos como parâmetros: Procure reduzir o número de parâmetros através da criação de objetos. Quando grupos de variáveis são passadas juntas, como:
- Funções mônades: Um parâmetro de entrada, é a melhor coisa depois de zero parâmetros. Se uma função for transformar o parâmetro de entrada, então a alteração deve aparecer como o valor retornado. Ex.:
- Objetos como parâmetros: Reduza a o número de parâmetros através da criação de objetos. Pode ser que um grupo de parâmetros faça parte de um conceito que mereça um nome só pra ele. Ex.:
Circle make(double x, double y, double radius);
Circle make(Point center, double radius);
- Lista como parâmetro: Se os parâmetros variáveis forem todos tratados da mesma forma, serão equivalentes a um único parâmetro do tipo list. Para que as funções não sejam maiores que funções tríades. Ex.:
void monad(Integer ...args);
void dyad(String name, Integer ...args);
void triad(String name, int count, Integer ...args);
- Parâmetros de saída: A finalidade do
this
é atuar como uma referência de saída. Em geral, é recomendável evitar o uso de parâmetros de saída. Se uma função precisar modificar o estado de algo, é aconselhável alterar o estado do objeto que ela pertence.
- Separação comando-consulta: Uma função deve fazer apenas uma coisa. Ou a função altera o estado de um objeto ou retorna informações sobre ele.
- Prefira exceções a retorno de códigos de erro: Ao retornar um código de erro, criamos um problema para o chamador que deverá lidar imediatamente com o erro.
- Extraia os blocos try/catch: Utilize das estruturas try/catch sempre que necessário, em suas funções. Try deve ser a primeira instrução e nada deve vir após os blocos catch/finally.
- Tratamento de erro é uma coisa só: Uma função que trata erros não deve fazer mais nada.
Como escrever boas funções? Organizar, refinar, dividir funções, trocar nomes, eliminar duplicação, reduzir os métodos e reorganizar. Mas não é necessário fazer desde o começo, pois é praticamente impossível.
Tente pensar que sistemas devam ser como histórias a serem contadas ao invés de programas a serem escritos.