Dicas de Clean Code: Funções pequenas e concisas
Escrever funções pode ser um trabalho trivial, mas escrever funções legíveis e com apenas uma responsabilidade é uma boa prática que todos deveriam adotar. Ser apenas funcional não é suficiente para tornar um código bom.
Escrever funções é na maioria das vezes um trabalho trivial. As vezes criamos funções com 10 linhas, 30 linhas e até algumas abominações com mais de 300 linhas entrando outros níveis de abstração e fazendo muito mais do que elas deveriam fazer. Um código bom precisa ser legível e não apenas funcional.
Algumas práticas ao escrever funções podem ajudar a criar um código mais legivel com uma linguagem natural como a de um texto.
Menor do que pequeno
Como dito antes, as vezes nós criamos funções abomináveis com dezenas ou centenas de linhas e isso pode ser um problema. Funções pequenas não garantem que seu código vá conter menos bugs, mas certamente vão garantir maior legibilidade.
O código abaixo roda no contexto de um Jogo da Velha em JavaScript. Tente ler e entender alguma coisa, mas não se esforce muito pois o objetivo é apenas ver a dificuldade de lê-lo. Sugiro que dedique apenas 3 minutos para ver o quanto ele é RUIM, mesmo que seja totalmente funcional.
var executarTurno = function(linha, coluna) {
if( tabuleiro[linha][coluna] !== VAZIO ) {
alterarMensagemDeStatus("Jogada Inválida!");
return;
}
tabuleiro[linha][coluna] = jogadorAtual;
numeroDeJogadas++;
document.querySelector('#tabuleiro').innerHTML = '';
document.querySelector('#status').innerHTML = '';
for(var linha = 0; linha < TAMANHO_MAXIMO_LINHA; linha++) {
for(var coluna = 0; coluna < TAMANHO_MAXIMO_COLUNA; coluna++) {
var button = document.createElement('button');
button.textContent = tabuleiro[linha][coluna].simbolo;
button.classList.add('botaoTabuleiro');
button.onclick = function(){
executarTurno(linha, coluna)
};
document.querySelector('#tabuleiro').appendChild(button);
}
var br = document.createElement('br');
document.querySelector('#tabuleiro').appendChild(br);
}
if( verificarLinhas() || verificarColunas() || verificarDiagonais() ) {
document.querySelector('#status').innerHTML = 'Jogador ' + jogadorAtual.simbolo + ' venceu!';
}
else if (numeroDeJogadas === 8) {
document.querySelector('#status').innerHTML = 'Empate!';
}
if(jogadorAtual === jogadorX){
jogadorAtual = jogadorO;
} else {
jogadorAtual = jogadorX;
}
}
Não vou dizer que é impossivel ler o código acima, entretando será necessário ler muito para entender o que a função executarTurno(linha, coluna)
faz. Além dela estar muito grande, também está lidando com muitas outras responsabilidades como manipular o DOM, fazer loops e adicionar eventos. E isso é muito ruim para a legibilidade do código.
Agora tente acompanhar essa versão:
var executarTurno = function(linha, coluna) {
if( tabuleiro[linha][coluna] !== VAZIO ) {
alterarMensagemDeStatus("Jogada Inválida!");
return;
}
efetuarJogadaNaPosicao(linha, coluna);
desenharTabuleiro();
verificaVencedor();
alternarJogador();
}
Dessa forma, fica muito mais fácil de entender o que a função executarTurno(linha, coluna)
está fazendo. Muitos detalhes da implementação estão ocultos dentro das outras funções, mas o importante é exibir quais as principais atividades que a função executarTurno(linha, coluna)
está tomando. Apenas quebrando nosso código em pequenas funções com nomes descritivos é que podemos melhorar a legibilidade. Logo, a dica é sempre tentar extrair uma nova função com um nome bem descritivo de um bloco de código.
Funções devem fazer APENAS UMA COISA. Elas devem fazer isso BEM e elas devem fazer APENAS isso
Curly Washburn: "One thing. Just One Thing!"
Não basta escrever funções pequenas. Nós precisamos escrever funções com apenas uma responsabilidade - que façam apenas uma coisa e nada mais. Originalmente essa é a dica de Curly Washburn sobre o segredo da vida no filme City Slickers, mas podemos aplicar a mesma dica no Desenvolvimento de Software. Precisamos que as funções façam apenas uma coisa e que façam isso muito bem.
O primeiro passo para isso é o nome da função. O nome da função deve descrever a ação que irá realizar e sua implementação deve ser coerente com a ação descrita em seu nome. Vamos ver novamente o exemplo da função executarTurno(linha, coluna)
:
var executarTurno = function(linha, coluna) {
if( tabuleiro[linha][coluna] !== VAZIO ) {
alterarMensagemDeStatus("Jogada Inválida!");
return;
}
efetuarJogadaNaPosicao(linha, coluna);
desenharTabuleiro();
verificaVencedor();
alternarJogador();
}
O nome da função usado no exemplo anterior era executarTurno
. Levando em consideração que a aplicação é um jogo da velha, ao que você remete o nome dessa função? No jogo da velha, o que significa executar um turno? Quais ações são tomadas durante a execução ou tomada de um turno? Bem, basicamente durante um turno um jogador escolhe uma posição válida, efetua a jogada e passa o turno para o próximo jogador caso o jogo não tenha chegado ao fim.
Podemos dizer que a função desenharTabuleiro
não faz parte da responsabilidade da execução do turno dependendo da nossa abstração. Pode-se então considerar mover a função desenharTabuleiro
para dentro da efetuarJogadaNaPosicao
ou criar uma nova função contendo a função chamada completarJogada(linha, coluna)
executando ambas as funções em sequencia.
Uncle Bob também sugere que blocos de if
, else
, try-catch-finally
, while
devam ter APENAS UMA LINHA, o que acarreta na criação de funções para comportar esses blocos. Seguindo essa recomendação teríamos o seguinte resultado:
var executarTurno = function(linha, coluna) {
if( tabuleiro[linha][coluna] !== VAZIO ) {
alterarMensagemDeStatus("Jogada Inválida!");
return;
}
completarJogada(linha, coluna);
}
var completarJogada = function(linha, coluna) {
efetuarJogadaNaPosicao(linha, coluna);
verificaVencedor();
alternarJogador();
}
var efetuarJogadaNaPosicao = function(linha, coluna) {
tabuleiro[linha][coluna] = jogadorAtual;
numeroDeJogadas++;
desenharTabuleiro();
}
Dessa forma vamos criar diversas funções com nenhum propósito computacional? Sim, mas criar funções com um nome descritivo (expondo a intenção de uma chamada de função ou de um conjunto de chamada de funções) é também uma das recomendações de Kent Beck para aumentar a legibilidade do código.
var exibirMenuDeConfiguracao = function() {
document.querySelector('#menu-configuracao').classList.toggle('menu-open');
}
Esse tipo de prática deixa o código mais autoexplicativo. O próprio código está exercendo um papel de documentação sem a necessidade de utilizar comentários. Então, outra dica é, caso você sinta a necessidade de adicionar um comentário para apenas uma linha de código ou um pequeno bloco de código, considere a criação de uma função com um nome descritivo que revele suas intenções.
Garantindo que as funções tenham apenas uma responsabilidade, ou seja, façam apenas uma coisa e nada mais, nós também podemos reaproveitar essa função em outros lugares do nosso código.
Conclusão
Nesse artigo vimos algumas dicas para melhorar a legibilidade do nosso código. O objetivo de seguir essas dicas é para melhorar a legibilidade do nosso código para que possamos lê-lo em linguagem natural como em um texto. Manter uma linguagem natural é importante pois passamos muito mais tempo lendo código do que escrevendo, então quanto mais fácil for ler e entender melhor.
Para aprender mais sobre boas práticas de desenvolvimento e técnicas de refatoração recomendo a leitura dos livros Clean Code ( Robert C. Martin ), Implementation Patterns ( Kent Beck ) e Refactoring ( Martin Fowler ). Esses livros foram escritos por renomados desenvolvedores com décadas de desenvolvimento e muitas de suas dicas são muito difundidas por outros desenvolvedores. O ideal é que você leia esses livros e entenda os motivos que levaram eles a adotarem tais práticas pois o mais importante é entender o Por quê e o Para quê eles utilizam tais práticas.
Essas práticas também são utilizadas nos cursos de Lógica de programação, Orientação a Objetos e todos os outros cursos daqui da Triadworks.
Se você gostou desse artigo ou se tiver qualquer critica, deixe um comentário. Ficarei feliz em responde-lo. :]
You might also be interested in these articles...
Desenvolvedor na TriadWorks - Email