Observe o seguinte código:
const randomInt = max => Math.round(Math.random() * max);
const randomElArray = arr => arr[randomInt(arr.length - 1)];
function randomName (takenNames = []) {
const factorName = 0.8 < Math.random();
const factorSurname = 0.4 < Math.random();
const factorNick = 0.4 < Math.random();
let name = factorName ? randomElArray(names) : '';
if (factorNick) {
name = `${name} ${randomElArray(nicks)}`;
}
if (factorSurname && !(factorNick && factorName)) {
name = `${name} ${randomElArray(surnames)}`;
}
name = name === '' ? randomElArray(nicks) : name;
return takenNames.includes(name) ? randomName(takenNames) : name;
};
Tem muita coisa “errada” acontecendo aí. Talvez você possa me dizer que os ifs deveriam ser alvo de um refactor. Talvez não usar a variável mutável name
e criar uma abordagem mais funcional. Talvez mais soluções “one liners” como as funções randomInt
e randomElArray
. E minha Nossa Senhora do Código Limpo, uma recursão!!! Que absurdo!!. E bem, tecnicamente você estaria certo. De fato, ifs dificultam a manutenção futura de um código. Objetos ou funções que gerenciam cada um uma condição são mais recomendáveis. Variáveis mutáveis – principalmente as que mudam de escopo – são potencial para problema… E existem muitos materiais ótimos por aí para estudar paradigmas e design patterns.
Mas, volta no código acima um minuto. Tem realmente algum problema muito sério ali? Nas 3 primeiras linhas, é criado um “input” de aleatoriedade para determinar como o nome será gerado. Nas linhas seguintes, as regras são bem explícitas ao concatenar ou não mais informações ao nome gerado. No final, dois fallbacks: caso o nome esteja vazio, um apelido; caso o nome já exista numa lista anterior, gere o nome de novo. Não tem nenhuma questão séria de performance ou falta de legibilidade.
É bom lembrar que paradigmas – principalmente orientação a objetos – são coisas abstratas e muitas vezes distantes do cotidiano do programador iniciante.
Isso vem da base matemática da computação. Aprende-se primeiro as definições e teoremas abstratos para depois ver a aplicação na prática. E os exemplos ficam em coisas bem raras de se ver na prática. Classe Cachorro
que herda de classe Animal
. Levante a mão, por favor, quem já usou isso no mundo real. Lembro da alegria que foi, quando ao programar meu primeiro jogo, saiu a linha de código: snake.eat(food)
. Parecia um exemplo bobo desses porém com aplicação prática. O jogo funciona (algum dia posto ele aqui no site, afinal está em javascript). Esse tipo de sentimento é uma coisa rara no dia a dia da profissão. Menos raro se você usa Ruby on Rails, mas isso é uma discussão para outro dia.
No livro/talk “Confident Code”, Avdi Grimm propõe que funções (base de todo código) sejam pequenas histórias. Que tenham início, meio e fim. A estrutura proposta por ele é que toda linha de código pode ser classificada como:
- Coleta de input
- Performance de trabalho
- Entrega de resultados
- Tratamento de erro
Ao colocar as linhas de código de qualquer função nesta ordem acima, a história que está sendo contada fica mais legível e compreensível. Mais a frente, são listados vários design patterns existentes que auxiliam no uso desta estrutura. No fim das contas, não é um paradigma formal, mas uma orientação geral. Afinal, o uso de um design pattern tem como objetivo facilitar a sua vida e das suas camaradas no trabalho.
Fatores como conhecimento da equipe, rotatividade da empresa, prazos bizarros, precisam ser levados em conta. Sempre haverá um refactor a ser feito. Não existe código perfeito. É necessário lembrar que código é feito para seres humanos. Tanto para os que usam, quanto para os que leem para alterá-lo depois. Ter em mente os objetivos finais do que se está fazendo é mais importante que o martelo que você está usando para pregar algo na parede. Saber a hora de começar e parar um refactor é um exercício diário. Código é uma história que você conta para o computador. E para outra pessoa também.