Concrete | Código Limpo e a Importância de Nomear
Para desenvolver qualquer produto, é necessário estar constantemente nomeando. Concorda? Temos que criar nomes para variáveis, funções, argumentos, classes… Neste post, vamos falar sobre a importância de criar bons nomes e como eles influenciam positivamente no seu código. Vamos lá?
Primeiramente, devemos ter em mente o quão grande é a importância de nomear no desenvolvimento de software.
Bons nomes podem economizar muito tempo na leitura do seu código, e é por isso que você pode gastar sem problemas um tempinho a mais para garantir que está nomeando da melhor maneira possível. Vamos ver um exemplo?
```java List<int[]> lista = new ArrayList<>(); ... public List<int[]> obtem() { List<int[]> tmp = new ArrayList<>(); for (int[] x : lista) if (x[0] == 4) tmp.add(x); return tmp; } ```
O código acima não é complexo, é pequeno, mas ainda assim temos dificuldade em dizer o que ele está fazendo. O que é *lista* e o que estamos armazenando nela? Qual é a importância do primeiro elemento, zero, de um item da *lista*? O que significa o valor *4*? Como posso usar a *List<int[]>* que está sendo retornada?
É muito difícil responder essas perguntas sem conhecimento prévio do ambiente em que o código está inserido, e isso é um problema, pois em times ágeis e multidisciplinares qualquer desenvolvedor deve entender claramente o seu código, não importa se é novo ou antigo em um projeto. Concorda? Vamos ver esse mesmo código melhorado:
```java private final int STATUS = 0; private final int STATUS_MARCADA = 4; ... List<int[]> tabuleiroDoJogo = new ArrayList<>(); ... public List<int[]> obtemPecasMarcadas() { List<int[]> pecasMarcadas = new ArrayList<>(); for (int[] peca : tabuleiroDoJogo) if (peca[STATUS] == STATUS_MARCADA) pecasMarcadas.add(peca); return pecasMarcadas; } ```
Este código não está limpo, mas elimina quase tudo o que antes estava implícito. Com isso, é fácil enxergar que o problema não está na complexidade de um código, e sim na quantidade de coisas implícitas. Quanto mais coisas implícitas seu código possui, mais difícil é a sua leitura; seja para qualquer outro desenvolvedor, ou para você mesmo – o próprio autor – um tempo depois.
Podemos falar sobre código limpo com mais detalhes ao longo de outros posts, mas para começarmos a desenvolver um olhar crítico sobre o nosso próprio código, segue um exemplo de código limpo:
```java ... List<Peca> tabuleiroDoJogo = new ArrayList<>(); ... public List<Peca> obtemPecasMarcadas() { List<Peca> pecasMarcadas = new ArrayList<>(); for (Peca peca : tabuleiroDoJogo) if (peca.estaMarcada()) pecasMarcadas.add(peca); return pecasMarcadas; } ```
```java ... private String rm, cd, ls; private int nulo = -1; ... ```
```java class PROConversorDeObjetoSeralizadoEmXmlParaString { ... } ... class PROConversorDeObjetoSeralizadoEmJsonParaString { ... } ```
```java ... private String[] nomesList; ... ```
Ao ler a palavra *List* em *nomesList*, imediatamente associamos essa variável a alguma implementação da interface List. O ideal é não utilizar tipos ao nomear uma variável, a não ser que seja bem necessário e útil, como é o caso das instâncias de *View* no desenvolvimento mobile. Nesse caso, utilizar simplesmente *nomes* seria bom; melhor ainda seria algo mais explícito como *nomesDosAlunos*.
Vamos começar com um exemplo simples, uma função que copia dois vetores de char:
```java public static void copia(char[] c1, char[] c2) { ... } ```
Nossa primeira dúvida ao olhar essa função é saber quem é o vetor base e quem é o destino da função. Em seguida, tentamos fazer atribuições sem certeza, pensando que o padrão foi seguido e o primeiro argumento é a base e o segundo é o destino. O último passo é assumir a insegurança e parar o fluxo do nosso trabalho só para testar o comportamento dessa função.
```java public static void copiaChars(char[] base, char[] destino) { ... } ```
```java obtemContas(); obtemContasAtivas(); obtemContaAtiva(); obtemInfoDeContaAtiva(); ```
O fato dos escopos das funções estarem reduzidos e os retornos omitidos não possui uma relevância tão significativa. O que realmente importa é que embora tenhamos nomes diferentes nas quatro funções, não temos de fato uma diferença de sentido entre elas. É como nomear duas classes, uma chamada *ContaInfo* e outra *ContaDado*; ambas possuem nomes diferentes, mas qual é a diferença prática?
Caso você não tenha se convencido ainda por causa da falta de retorno das funções, deve lembrar que no meio do código não teremos sempre o escopo disponível e teríamos que consultar sua declaração toda hora. Mesmo que as IDEs ajudem nessa consulta, é improdutivo ter que fazer isso várias vezes ao longo do desenvolvimento, principalmente se a definição real está em outra camada do sistema.
Abreviar nomes contribui para o aumento de coisas implícitas no código, ou seja, aquelas que necessitam de conhecimento prévio do leitor para que haja um perfeito entendimento. Exemplo:
```java public class Documento{ private Long cDtMdhms; private Long mDtMdhms; ... } ```
private Long cDtMdhms;
private Long mDtMdhms;
O que podemos dizer sobre as variáveis *cDtMdhms* e *mDtMdhms*? Como podemos pronunciar os seus nomes para qualquer tipo de discussão sobre esse código?
```java class DocumentoInscricao{ private Long criacaoTimestamp; private Long ultimaModificacaoTimestamp; ... } ```
class DocumentoInscricao{
private Long criacaoTimestamp;
private Long ultimaModificacaoTimestamp;
Agora ficou muito mais fácil de entender, mesmo sem conhecimento prévio do sistema, que existe uma classe *DocumentoInscricao* relativa a algum documento de inscrição e que esse documento possui um carimbo de data de criação e da sua última modificação. Isso tudo foi possível apenas nomeando de maneira correta e significativa, tornando explícito qualquer sentido antes escondido.
Antigamente, os compiladores não checavam os tipos das variáveis e os programadores precisavam lembrar constantemente o tipo das suas variáveis declaradas. Para ajudar, era muito comum nomear variáveis e utilizar o seu tipo como sufixo. Hoje em dia, existem diversas IDEs que podem nos ajudar com esse tipo de problema, então não vale a pena utilizar esse tipo de nomenclatura e correr o risco de prejudicar o nosso código, concorda? Vamos ver um exemplo de nome inconsistente:
```java ... private NumeroTelefone telefoneString; ... ```
private NumeroTelefone telefoneString;
Certamente a variável correspondente a um número de telefone foi escrita inicialmente com o tipo String, porém – em algum momento – a mudança foi necessária e o nome permaneceu o mesmo, o que pode causar confusão no código.
Outro ponto importante é que não precisamos de qualquer prefixo para diferenciar atributos da classe de outras variáveis. Prefixos como *m* só poluem o código e rapidamente aprendemos a ignorá-los, pois não trazem nenhum benefício, principalmente com o avanço das IDEs e suas diferentes cores que nos ajudam no desenvolvimento. Olha só um exemplo de código bagunçado:
```java ... private ScrollListener mScrollListener; ... ```
private ScrollListener mScrollListener;
Conforme aprendemos a ignorar o prefixo *m*, a leitura do código é prejudicada, já que a tendência é lermos *ScrollListener* tanto para a classe quanto para a variável. Vale a pena ressaltar que nossas classes devem ser pequenas, então não vamos perder o controle de suas variáveis e nem dos seus tipos.
Existe um prefixo famoso para interfaces que nem sempre passa informação com clareza para os leitores do código. Por exemplo, o que podemos dizer sobre o nome *IAnimal*?
Até onde sabemos, esse prefixo com a letra *i* maiúscula pode significar *Interface* ou *Implementation*. É legal esse tipo de codificação, mas caso julgue necessário diferenciar a interface da sua classe de implementação, utilize algum prefixo ou sufixo que não gere dúvidas e que seja escrito na classe de implementação, como por exemplo
*AnimalImpl*. É melhor deixar suas interfaces livres de codificação nos seus nomes, evitar distrações ou informações desnecessárias e não influenciar outros desenvolvedores a criar um padrão de prefixo ou sufixo.
Outro problema com prefixos são os prefixos de projeto. Imagine um sistema chamado "Sistema de Gambiarras Predominantes", dado pela sigla SGP. Se começarmos a criar classes como *SGPMensagem*, *SGPCliente*, *SGPRota*… com o mesmo padrão de prefixo, nosso sistema fica poluído e surgem vários efeitos negativos. Entre eles podemos citar a perda de produtividade ao usar uma das melhores funcionalidades de uma IDE, o *autocomplete*, simplesmente por adicionar às classes um prefixo que não agrega nenhum valor.
É bastante antiprodutivo termos que digitar sempre a sigla SGP para, então, localizar a classe que queremos utilizar. Vale também ressaltar o aumento da dificuldade de identificar a classe que desejamos utilizar em uma lista em que a ordem alfabética é significativa somente a partir da quarta letra.
Entretanto, como toda regra, essa também tem sua exceção. Existem casos nos quais sufixos são recomendados, já que facilitarão a vida do desenvolvedor que vai ler o nosso código. Quando utilizamos algum padrão de projeto, é muito mais fácil para o entendimento – e até construção – do código quando nomeamos interfaces ou classes com sufixos que representam um padrão. Vamos ver um exemplo de nomenclatura para design patterns:
```java public interface CliqueListener { ... } public interface FormasGeometricasFactory { ... } public interface AlertaBuilder { ... } ```
public interface CliqueListener { ... }
public interface FormasGeometricasFactory { ... }
public interface AlertaBuilder { ... }
Apenas lendo os nomes acima, conseguimos entender sem conhecimento prévio do sistema que existem três interfaces: uma que funciona como ouvinte de cliques, uma que funciona como uma fábrica de formas geométricas e um construtor por etapas específicas de um alerta.
```java public String getNome() { ... } public String recebeNome() { ... } public String obtemNome() { ... } ```
```java public class ProtocoloController { ... } public class DispositivoManager { ... } ```
*Manager* e *Controller* são duas palavras genéricas no meio de desenvolvimento de sistemas. Nem sempre é trivial explicar o que um dos dois faz, e ter esses dois nomes juntos pode complicar ainda mais, fazendo surgir dúvidas como "O que faz um Controller e um Manager?" ou "Por que as duas classes não são um Controller ou um Manager?".