Tópicos relevantes do livro Código Limpo: Clean Code A Handbook of Agile Software Craftsmanship
O livro Código Limpo (Clean Code - A Handbook of Agile Software Craftsmanship) é basicamente um atalho pra você aprender as boas práticas dos grandes programadores poupando dores de cabeça e, principalmente, tempo e dinheiro. Eu mesmo, durante minha carreira relativamente curta de desenvolvimento de software já colecionava uma série de mandingas, cacoetes e práticas que poderiam ser (algumas) consideradas boas e outras eufêmicamente "teimosas".
Basicamente, quando leio um livro, gosto de ter à mão um lápis e uma caneta de grifar para poder destacar e anotar o que julgo importante. Também tenho um Kindle™ que me permite fazer isso de forma “ecologicamente correta”. Entretanto, desta vez o livro não era meu, então, tive que anotar minhas considerações em blocos de papel e, posteriormente, no documento que aqui compartilho convosco. Todavia, antes de começar, recomendo a todos que leiam o livro na íntegra, pois realmente vale cada um dos 60 reais que o meu brother William Lopes (conhecido pelas alcunhas Maica, Capi, Non, entre outros) pagou (acabei de consultar a Amazon.com e vi que está custando 170 hoje: http://www.amazon.com.br/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882).
A organização desse post segue-se mais ou menos assim: o título é o capítulo de onde retirei a informação e os tópicos são as práticas que considerei relevantes seguidas de algum comentário (in-) oportuno meu. Também vale a pena frisar que há muito mais no livro do que eu aqui vos transcrevo, no entanto, anotei os pontos que mais chamaram a atenção pra minha insignificante vida de programador.
Nomes
- Nomes significativos, independente do tamanho, o nome deve expressar o que a variável/método/classe se propõe a fazer. É melhor um nome do tipo somarOTotalPagoAFornecedores do que um método chamado somar e um comentário gigantesco, perecível e ignorável dizendo o que o dissilábico método faz. E é isso que a anotação seguinte expressa:
- Melhor um nome extenso e significativo do que um nome curto e confuso. Isso quer dizer que você não deve se preocupar em economizar caracteres para descrever o comportamento do seu método enquanto o nomeia. No entanto, cautela e bom-senso devem sempre predominar nas escolhas.
- Nem sempre é (ou não é) possível começar pelo ótimo, o ideal é fazer um brainstorming inicial e refatorar usando-se as ferramentas (IDE). Em outras palavras, você “vomita” o código funcional e depois faz o refactoring. Tentar começar pelo "código limpo" em muitas ocasiões me custou muito tempo, simplesmente pela minha vaidade de não colocar uns ifs dentro dos outros.
- O código deve contar uma história. Quem vai lê-lo deve entendê-lo na integra, sem auxílio de comentários e documentações. Aqui a dica vai além da nomenclatura dos artefatos. "Contar uma história facilmente compreensível" é basicamente a dica do livro.
- Os nomes devem ser refatorados com o tempo. Uma classe/função/método/variável pode perder o significado original, portanto, é sempre importante manter a nomenclatura em acordo com a intenção real e atual do método. Aqui cabe ressaltar o trabalho extra que os comentários geram. Eu, particularmente, até ler o livro, era um cara que gostava de enfeitar as classes com belos textos, às vezes até rebuscados, detalhando o funcionamento das minhas rotinas. Percebi (leia-se: fui convencido) mais tarde que esses comentários só serviam para ficar desatualizados.
- Nomes pronunciáveis são sempre melhores que nomes acrônomicos. AgentConsoleLoginRequest é melhor que ACLRequest. Obvio, não?.
- Classes → substantivos, métodos → verbos. Básico de qualquer curso de programação; classe define coisas e, os métodos, como essas coisas se comportam.
- Nomes devem ser significativos dentro do contexto (domínio) do software. A palavra "nota" pode ter significados diferentes em um sistema de vendas, um acadêmico e um sistema de anotações pessoais, por isso é sempre bom estar de acordo com o vocabulário do seu cliente. Vai por mim, já passei muita raiva porque o cliente mencionava uma funcionalidade ou artefato usando uma palavra e no software estar representada por outra. Usar um vocabulário comum é essencial.
Funções
- Devem ser pequenas! 5 ou 10 linhas é mais que o necessário. É isso! O Joshua Kerievsky fala no seu livro "Refatoração para Padrões" que a média de linhas dos métodos do seu sistema não devem ser maior do que 5 (“Your methods can be no longer than five lines of code”).
- Devem fazer somente o que se propõem a fazer.
- Devem fazer somente 1 tarefa. A 2 e a 3 se complementam. Um método denominado somar(a, b) deve somar a e b (ponto). Não tem que incrementar outras variáveis, não tem que validar se o usuário pode somar a com b, não tem redirecionar para outra página, não tem que fazer mais nada. Tem que somar a com b! Outra dica interessante é que se você usar “e” ou “ou” na nomenclatura do seu método tem grandes chances dele estar acumulando responsabilidades; na dúvida: refatore.
- Quanto menos parâmetros, melhor:
- zero parâmetros: o ideal;
- 1 parâmetro: bom;
- 2 parâmetros: aceitável;
- 3 parâmetros: ruim;
- 4 parâmetros: melhor repensar;
- Exceções são melhores do que códigos de erro. Concordamos que não devemos exibir um UnauthorizedException, na tela do usuário, mas ainda assim é melhor do que exibir um "error 403".
- Uma função que trata códigos de erro não deve fazer mais nada além de tratar os códigos de erro. Basicamente as anotações 1, 2 e 3 juntas.
Comentários
Essa foi pra mim a parte mais dolorosa: parar de comentar. Quando eu aprendi a fazer meu primeiro if no saudoso Clipper 5, mais ou menos no verão de 1996, comentários eram o que diferenciavam o bom programador do ruim; o cara organizado do porco relaxado. Acabei carregando esse hábito comigo para as outras linguagens: C, C++, PHP e mais tarde pro Java. Até pouco tempo atrás eu ficava bravo se alguém me falasse que comentar era ruim.
- Comentários são ruins. Isso é um fato, aceite.
- Javadoc é aceitável, mas também é ruim. Geralmente são gerados pelas IDEs aqueles cabeçalhos de métodos entre “/**” e “*/” com as meta-variáveis @param, @see, @author, etc. Isso é de certa forma, segundo o autor, aceitável, porém desencorajado. Você acaba refatorando o método e se esquece de alterar a quantidade de parâmetros, tipos, etc; é justamente isso que diz a a notação seguinte:
- O programador acaba alterando a função mas não a documentação.
- A regra básica é: sempre que precisar comentar, refatore. Resumidamente: "se você se sentir tentado a colocar um comentário para explicar seu código, é porque ele não está compreensível" (coloquei entre aspas porque não me lembro quem disse isso).
- Um tipo de comentário válido é aquele que expressa algo que não pode ficar claro no código: uma decisão de negócio, por exemplo:
// desculpe, me obrigaram a fazer isso
Formatação
- Melhora a legibilidade.
- Deve ser empregada por toda a equipe de maneira consensual. “Ao ler um código, não ser possível identificar quem o escreveu”. Todos devem praticar de forma igual. Eu costumo usar o padrão da IDE para evitar entrar numa luta recursiva toda vez que alguém usar o recurso de auto-formação antes de commitar uma classe.
- Espaçamentos devem ser padronizados. Entre métodos, entre declarações de variáveis, entre parênteses, entre chaves, enfim, reúna a equipe e entre em um acordo sobre o que deverá ser empregado como padrão de espaçamento, redija um documento e disponibilize à todos.
- Separar contextos dentro da classe. Declaração de variáveis estáticas privadas de públicas; declaração de variáveis dos métodos e por aí vai. No meu caso, uso somente uma quebra de linha simples para separar os contextos, sem traços, pontos ou aquelas horrorosas série de sinais de igualdade (ex: “=======”).
- Variáveis devem ser declaradas próximo de onde serão usadas (dentro do método). Eu aprendi a declarar as variáveis nos inícios dos métodos, quando comecei a programar em C. Não tenho certeza se isso era um requisito da linguagem, mas sei que me acostumei a fazer isso e acabou sendo outra mania que migrei para as demais linguagens. Não o faça! Declare suas variáveis próximas aos pontos onde você irá utilizá-las.
- Métodos devem ser posicionados imediatamente abaixo dos métodos que os chamam. Se um método A chama um método B, posicione o método B imediatamente abaixo do método A. Isso facilita a leitura de quem pegar seu código no futuro. Geralmente as ferramentas de refactoring já fazem isso automaticamente quando você vai extrair um bloco de código para um método.
- Linhas devem ser quebradas em qualquer posição entre 100 e 120 caracteres (eu uso 128... porque sim). Tanto no Netbeans quanto no Eclipse, eu coloco a margem da direita na posição 128 e NUNCA, repito, NUNCA deixo meu código ultrapassá-la. Acho que isso é uma prática que veio das telas 80x24 (tipo Clipper) que a galera usava para programar antigamente; 80 colunas por 24 linhas. Assim, para uma melhor visualização do código, os programadores recomendavam não ultrapassar a coluna 80 para não gerar rolagem horizontal. Hoje, com as telas alta resolução isso não se faz mais necessário, mas o autor acha importante quebrar a linha em algum ponto entre o começo e o fim do seu “range” visual.
- Aninhamento horizontal são ruins porque geralmente as IDEs não os respeitam. Aquelas declarações de variáveis, enums e qualquer outra coisa que você tenta alinhar em colunas, colocando os sinais de igual exatamente um abaixo do outro, não funcionam. O primeiro aloprado que usar a formatação automática vai destruir em milésimos de segundos os minutos preciosos que você gastou tentando alinhá-las. Eu adotei uma regra simples: não brigue com a IDE.
Estrutura de dados
- Um objeto não deve expor seus atributos internos. A primeira ideia que vem a mente ao ler isso é gerar métodos get e set para todos os atributos, o que é de certa forma válido, mas ainda devemos considerar a observação seguinte:
- Getters e setters acabam quebrando o encapsulamento quando indiscriminadamente usados. A lição aqui é ser cauteloso no que sua classe vai expor. Talvez não haja a necessidade da sua classe externar atributos como precoCusto e margemDeLucro quando você pode simplesmente expor um método que retorna o preço final (eu sei que foi um exemplo medíocre, desculpa).
- Lei de Demeter: o módulo não deve enxergar o interior do objeto que manipula. Uma função f de uma classe c só deve chamar
- métodos de c;
- objetos usados por f;
- objetos passados por parâmetro para f;
- variáveis de instância de c.
Esse assunto por si só, rende um post inteiro, portanto, se quiser se aprofundar mais, dá uma olhada nesse post aqui.
- Train Wrecks (acidentes ferroviários) ocorrem quando há encadeamento de chamada de métodos. Quando há encadeamento, melhor separá-los em variáveis locais conhecidas:
a.getB().getC();
deve ficar:
B b = a.getB();
C c = b.getC();
C c = b.getC();
No entanto, o ideal é que a classe A retorne o objeto esperado a partir de um método adequadamente nomeado e especificado (comportamento estaria em a):
a.getQualquerCoisaQueBFazComC();
a.getQualquerCoisaQueBFazComC();
- Objetos DTO não devem possuir comportamentos. São burros, ou como o pessoal gosta de dizer: anêmicos.
Exceções e erros
- Sempre preferir exceções ao invés de retornar códigos de erro. Como foi mencionado em uma nota anterior, é melhor lançar uma UnauthorizedException do que um “Error 403”.
- Sempre optar por exceções não verificadas. Exceções verificadas propagam alterações para níveis que não deveriam, por exemplo: um método que passa a lançar uma nova exceção verificada deverá alterar todas as estruturas de outros níveis que o usa.
- Contextualize as exceções. Geralmente um tipo de exceção por módulo é mais do que o suficiente.
- Nunca retorne null. Retornar null implica em verificação if-null por todo o código, sem contar as atormentadoras NullPointerExceptions que fazem qualquer programador se sentir um newbie.
- Null deve ser encarado como uma condição anormal. Você deve considerar que um null em qualquer ponto do código é algo anormal e deve ser verificado.
- Nunca passar ou receber null como parâmetro.
Classes
- Devem ser pequenas. Pode ser difícil definir “pequena” aqui, mas considere a dica do Joshua Kerievsky no "Refatoração para Padrões" sobre o tamanho médio dos métodos para mensurar suas classes.
- Possuir um único propósito e ter a responsabilidade bem definida (coesão). Uma dica: geralmente quando a descrição (Javadoc, por exemplo) de uma classe usa “e” para descrevê-la, significa que ela possui mais de uma responsabilidade.
- Escolher um bom nome pode ser a forma exata de separar adequadamente as responsabilidades.
- Recomenda-se começar declarando variáveis públicas e estáticas (constantes) seguidas pelas privada e estáticas, seguidas pelas, seguidas pelas variáveis de instância privadas.
E é isso. As práticas acima, como eu disse, são só um amontoado de anotações que eu considerei importantes para usar no meu dia a dia enquanto eu lia; nem sequer abrange o conteúdo todo do livro. Me arrependi muito de ter negligenciado tal material por tanto tempo e se há um livro de TI que eu recomendaria para alguém que está iniciando sua carreira, não tenho dúvidas que seria esse.
Nenhum comentário:
Postar um comentário