DEV Community

Guilherme Manzano
Guilherme Manzano

Posted on

Guia Clean Code: Da Teoria à Prática (Parte 1)

Recentemente, li o livro Clean Code de Robert C. Martin (conhecido como Uncle Bob), que fala sobre como desenvolver códigos seguindo boas práticas de programação, que visam a facilitar a leitura e refatoração do código. Quando estamos escrevendo um código, pensamos apenas em resolver o problema de negócio da forma mais direta que nosso cérebro encontra naquele momento. Porém, quando vamos ler o código produzido com mais calma, percebemos que alguns métodos e classes acabaram ficando confusas, com muitas linhas de código e muitas finalidades distintas. Se mexermos nesse código apenas depois de algumas semanas a situação é até pior, pois esquecemos detalhes que pensamos durante a construção. 

Vamos imaginar agora que outro desenvolvedor vai pegar precisar alterar esse código. Não conhecendo a fundo a funcionalidade e a lógica desenvolvida, além de tentar entender um código pouco legível, ficará ainda mais difícil fazer alterações no código sem gerar bugs ou quebrar a aplicação.

Capa do livro. Fonte: https://www.amazon.com.br/Clean-Code-Handbook-Software-Craftsmanship-ebook/dp/B001GSTOAM

Por esses motivos, é muito importante aplicarmos boas práticas e refatorações constantes nos nossos códigos, facilitando a sua manutenção e entendimento por parte de outros programadores ou, até mesmo, de nós mesmo. Vou compartilhar agora alguns pontos principais de aprendizado que adquiri na leitura deste livro, mas recomendo que todos leiam este livro, para aprofundarem seu conhecimento técnico. Como complemento, vou postar um artigo desenvolvendo Clean Code em um pequeno programa em Java, nas próximas semanas.

Qualquer um consegue escrever código que um computador entende. Bons programadores escrevem código que humanos entendem - Martin Fowler

IMAGEM CLEAN CODE

Nomes Significativos

Utilize nomes que revelem seu propósito, o nome de uma variável, função ou classe deve responder a todas as grandes questões. Por exemplo, para definir uma variável que indica um tempo percorrido em dias. Observe a seguinte variável:

int d;

O nome da variável d não explica nada para o leitor do código. O que essa variável faz? Quando utilizá-la? Um nome tão vago dificulta a compreensão do código. Por outro lado, um nome explicativo facilita a compreensão da utilidade da variável e do código propriamente dito, como o exemplo a seguir:

int daysSinceCreation;

O nome de Classes e Objetos precisam ser constituídos de substantivos, como AddessCustomer, CustomerPayment, CustomerData, entre outros. Já os nomes de métodos devem conter verbos (devem exprimir ação) mais substantivos, como findUser, saveFile etc. Além disso, é necessário nomear os métodos de acesso, autenticação e alteração com prefixos como get, set ou is.
Para constantes, utilize apenas letras maiúsculas para defini-las. Não utilize Magic Number, se precisar utilizar algum número no código, explique qual a sua utilidade (o que de fato aquele número representa no seu código);

Funções

As funções devem ser pequenas, uma função com centenas ou milhares de linhas fica difícil de ler e toma muito tempo do programador para analisa-lo. De preferência, as funções não devem ter mais de 20 linhas.

Uma função deve fazer uma única coisa apenas (princípio da responsabilidade única). Caso uma função tenha mais de uma responsabilidade, é preciso "quebrá-la" em mais funções, onde cada uma delas exercerá apenas uma função.

Com relação aos parâmetros das funções, o ideal é que elas não tenham nenhum parâmetro. Se não for possível, então utilize um ou dois parâmetros para a função. Em casos excepcionais (de preferência evitáveis), utilize três parâmetros. Não utilize mais parâmetros que isso em sua função. Os parâmetros são complicados, eles necessitam de muitos conceitos e dificultam o entendimento do código por um programador. Além disso, quanto mais parâmetros mais trabalho para escrever os casos de testes da função.
Com relações a exceções, é preferível retornar exceções ao invés de códigos de erros, pois ao retornar um código de erro é criado um problema para o chamador, que deverá lidar com o erro imediatamente. Por outro lado, utilizando exceções o código de tratamento de erro poderá ficar separado do código. Além disso, extraia os blocos try/catch e coloque-os em suas próprias funções, tornando o código mais legível.

Caso precise utilizar realmente o nome de métodos ou variáveis que não sejam intuitivos, crie uma documentação explicando o que ela está fazendo de fato (por exemplo, utilizando o Javadoc com @param, @return, entre outros - para programas em Java).

Imagem Ilustrativa

Tratamento de Erro

Utilize exceções ao invés de retornar códigos de erros, pois vai evitar que o chamador verifique os erros imediatamente após a chamada, gerando problemas caso ele se esqueça de tratá-los.
Utilize exceções não verificadas, pois verificar exceções fere o Princípio de Aberto-Fechado. Por exemplo, se você lançar uma exceção a ser verificada a partir de um método no seu código e o catch estiver alguns níveis acimas, será necessário declará-lo na assinatura de cada um dos métodos entre você e o catch, ou seja, significa que uma modificação em um nível mais baixo do software pode forçar a alteração de assinaturas em muitos níveis mais altos.

Não passe ou retorne null, pois isso exige que façamos tratamento de NullPointerException em muitas partes do código, e esquecer de tratar um dos casos com null irá lançar uma exceção indesejada no seu programa.

Quando for lançar exceções, evite código de erro, lance mensagens claras para quem vai ver aquele código. Extraia o código contigo no try/catch para funções separadas, para melhorar a sua legibilidade.

Classes

Seguindo a convenção padrão Java, uma classe deve começar com as variáveis públicas, seguidas das estáticas, depois as constantes, variáveis estáticas privadas e, por fim, as variáveis privadas. Quanto aos métodos, primeiro devem ser declarados os públicos, depois as privadas.

As classes devem ser pequenas, devem ter uma única responsabilidade. Para saber se sua classe está fazendo muita coisa, nomeie-a descrevendo o que ela faz. Se for necessário usar palavras como "se", "e', "ou" ou "mas" no nome da classe, significa que ela possui muitas responsabilidades e deve ser dividida em classes separadas (Princípio da Responsabilidade Única).

Evite passar parâmetros na mesma classe entre os métodos, utilize o this quando for preciso utilizar o valor de uma variável em vários métodos.

Imagem Ilustratriva DRY

Não se repita (DRY - Don't Repeat Yourself)

Este princípio fala diz para reduzirmos ao máximo a duplicação de código em nossos projetos. Quando temos código duplicado, além de aumentarmos desnecessariamente a complexidade do mesmo (com mais linhas de código, com mais testes e com mais chances de gerar bugs), se precisarmos alterar um dos blocos de código que se repetem, temos que ficar procurando e alterar também os outros blocos repetidos em nosso programa. Como vocês podem imaginar, é a receita perfeita para esquecermos de alterar um dos blocos repetidos e causarmos alguns bugs e problemas desnecessário.
Através da Orientação a Objeto, podemos simplesmente isolar esses códigos que utilizamos em vários lugares em métodos e classes específicas, chamando-a toda vez que se fizer precisarmos utilizá-la, evitando facilmente a duplicidade do código.

Deixe o código mais limpo do que você encontrou

É comum termos que modificar um código desenvolvido por outro programa, seja para corrigir um bug, ou para adicionar uma nova funcionalidade. Quando tiver que alterar um código, mesmo que não seja seu, aplique tudo o que sabe de boas práticas e código limpo, facilitando a refatoração e entendimento para os próximos desenvolvedores.

Como a refatoração pode afetar diretamente a lógica da aplicação, é vital que a aplicação tenha testes com uma cobertura, que vão garantir que a aplicação não vai quebrar durante as alterações. Caso ela não possua testes, crie-os! Mesmo que não dê tempo de criar os testes necessários e depois fazer a refatoração, os testes vão preparar o terreno para que os próximos programadores façam a melhoria no código.

IMAGEM COMPUTADOR

Comentários

NUNCA utilize códigos comentados. Eles poluem o código e geram confusão em outros desenvolvedores que precisam alterar aquele código. Afina, aquele código comentado era uma versão antiga? O desenvolvedor estava fazendo alguns testes e esqueceu de apagar os códigos comentários? Eles representam sugestões de refatorações futuras? Representam códigos para serem implementados no futuro? Representam algum tipo de lembrete que ajudam o desenvolvedor a entender o código que escreveu na classe?

Como podemos ver, eles acabam atrapalhando bastante na compreensão do código. Além disso, hoje em dia podemos (e devemos) utilizar as ferramentas de versionamento de código (Github, Gitlab, Bitbucket, entre outros), onde podemos facilmente consultar o histórico de alterações no código.
Evite também utilizar comentários de linhas, as funções e variáveis precisam ter nomes autoexplicativos. Além disso, os testes também são uma forma de documentar e que podem deixar um código bem mais explicito que comentários.

Evite a utilização de condicionais negativos

Condicionais negativos aumentam a complexidade do código, pois o desenvolvedor pode simplesmente não perceber o símbolo de negação na frente (!), além de aumentar a dificuldade de compreensão do código por um desenvolvedor.

Dicas para programas Java

Apesar do livro Clean Code ter sido escrito utilizando a linguagem Java para a construção dos exemplos, resolvi separar todos as dicas que falam de programas Java propriamente dito neste tópico.

Caso você precise utilizar muitas constantes em seu programa, extrai-as para ENUMS, para armazená-las. Isso deixará o código das Classes mais enxutas, além de permitir reutilizar algumas dessas constantes em outras Classes.

As interfaces definem o que uma classe deve fazer, mas não como. Ou seja, elas possuem declaram os conjuntos de métodos, o comportamento que um conjunto de classes deve ter. A classe deve então implementar uma interface, utilizando todos os métodos presentes nela. Basicamente, uma interface estabelece um contrato a ser seguido por um conjunto de classes, a implementação dos métodos será feita pelas classes que a implementarem. É recomendado utilizar interfaces na maior parte dos casos, pois elas evitam que ocorram alguns problemas de ambiguidade de métodos e de desempenho. Além disso, elas podem ser utilizadas para especificar o comportamento de um tipo, implementar múltiplas heranças (pois uma classe pode herdar apenas uma classe, mas pode implementar várias interfaces simultaneamente) e compartilhar métodos entre várias classes.

No caso de métodos mais complexos, se for necessário acrescentar comentários, utilize o Javadoc para isso, pois eles possuem algumas marcações que facilitam a leitura e busca de informações, além de serem mais facilmente encontrados e reunidos que os tradicionais códigos de linhas.

Durante o desenvolvimento, utilize ferramentas para analisar a quantidade de code smells no seu código, pois elas vão apontar os erros e má prática no seu código sempre que você fizer uma nova alteração. Um plugin muito utilizado para isso é o SonarLint, que está disponível tanto para o Eclipse, quanto para o IntelliJ.

IMAGEM KEEP SIMPLE

Outras dicas 

· Evite, sempre que possível, o uso de if ou while, pois eles aumentam a complexidade do código. É preferível trocá-los por um método com um nome bem explicativo;
· Não utilizar "wild cards" (coringas) nos imports das bibliotecas, importe cada método de forma separada. Isso evita uma sobrecarrega na memória desnecessária com métodos que não serão utilizados;
· Code smell são "mau cheiro" no código, significa que tem algo no seu código que pode não estar seguindo as boas práticas e código limpo;
· Um dos piores code smells seria a distância das linhas do seu código da margem, que simbolizam que aquele código possui muitos if, else, for, que deixam o código mais complexo;

Top comments (0)