Este artigo é uma sinopse do livro de código limpo de Robert Martin e minha compreensão de como deveria ser o código limpo. Não há seções sobre testes, TDD, qual arquitetura deve ser etc. Aqui tudo é exatamente como deveria ser o Código Limpo.

Sim, talvez o tópico do Código Limpo já esteja hackeado, mas, no entanto, nem todo mundo está familiarizado com ele e, além disso, eu não conheci análogos do conteúdo contido no meu artigo.
Geral
Não há caminho e solução verdadeiros. Existe um que é mais adequado para uma tarefa específica.
Ao resolver um problema, tente reproduzir absolutamente todos os casos que possam afetar esta tarefa e implemente a tarefa, levando em consideração absolutamente todos os casos.
Além disso, ao resolver o problema, tente ir pelo contrário. Entenda que tipo de resultado você deseja obter no final e faça com base nisso o algoritmo pelo qual a tarefa será executada.
Antes de enviar uma tarefa para liberação, verifique se ela funciona corretamente. Há algum erro nele. Isso se aplica mesmo às confirmações enviadas para sua filial. O cenário mais ideal é aquele em que ninguém pode encontrar erros na funcionalidade que você desenvolveu.
Sempre pense em como você pode tornar seu código mais simples, mais limpo e mais legível.
- Que casos uma tarefa pode ter?
- Eu levei tudo em consideração?
- O que poderia dar errado?
- O que pode ser combinado?
- Existe alguma funcionalidade semelhante?
- O que é supérfluo aqui?
- Como tornar isso mais fácil?
- Como torná-lo mais legível?
- Como deixar isso mais claro?
Código limpo
Como escrever um código limpo e bom? É como escrever um livro. Primeiro você faz um rascunho e, em seguida, penteia para o estado em que gostaria de lê-lo. Lembre-se sempre de que seu código deve contar uma história para que o leitor possa entendê-la.
Entende-se uma entidade - uma interface, uma classe, um método, uma variável, um objeto etc.
- O código limpo é simples, expressivo e focado em uma tarefa específica.
- Código limpo é fácil de ler, como prosa. Se não for esse o caso, vale a pena refatorar.
- Código limpo é fácil de modificar. Não deve estar rigidamente ligado a um monte de entidades. Qualquer entidade pode ser facilmente alterada.
- O código limpo passa por uma revisão muito melhor. Se a revisão for acompanhada de muitos comentários, ela não está limpa e precisa ser refatorada.
- O código limpo sempre parece ter sido trabalhado por muito tempo. Qualquer que seja a maneira como você procura aprimorá-lo, ainda chegará à conclusão de que esse código é o melhor. Assim, o código limpo é pensado nos mínimos detalhes.
- Regra do escoteiro: Deixe a vaga de estacionamento mais limpa do que estava antes de você. Isso muda facilmente para a programação. Veja o código sujo? Torne-o mais limpo enquanto você resolve seu problema. Você não deve se deixar levar por isso, e se o código sujo estiver muito sujo, aloque uma tarefa e tempo separados para limpá-lo.
- Não tenha medo de fazer alterações. Se você quiser criá-los, terá razões para isso, o que significa que você tornará o código melhor e mais limpo. Além disso, os testes mostrarão se há algum erro no seu código (desde que eles existam).
- Qualquer entidade deve ser responsável por um funcional e somente por ele. E ela deve executar bem. Responsabilidade Única.
- Se uma entidade for responsável imediatamente por duas ou mais ações, sua funcionalidade deverá ser separada.
- O código deve ser lido de cima para baixo.
- Em uma arquitetura boa e competente, fazer alterações sem custos e esforços significativos.
- Excluir código morto. Um código morto é um código que não será chamado sob nenhuma condição ou que não é usado em nenhum lugar.
Nomes e divisões
- Use nomes claros e fáceis de pronunciar para qualquer entidade. Eles devem descrever por que essa entidade existe, o que faz e como é usada.
- Não tenha medo de perder tempo escolhendo o nome melhor e mais amigável. Você vencerá no futuro trabalhando ou lendo este código.
- Se o nome da entidade não corresponder à sua funcionalidade ou o nome não entender o que a entidade faz, será necessário renomear o nome para o nome mais compreensível. Se isso for impossível, algo está errado com seu funcionamento e precisa ser refatorado.
- A entidade, que tem o nome "And", "With" - viola a responsabilidade única. Vale a pena compartilhar a funcionalidade dessa entidade. Mas essa regra às vezes é negligenciada.
- Textos incompreensíveis, as linhas devem ser divididas em variáveis e fornecer nomes claros.
- Os nomes dos métodos devem conter um verbo que descreva o que esse método faz e a palavra-chave com a qual ele trabalha. Se o nome do método não tiver um verbo, essa entidade não deve ser um método ou deve receber o nome correto.
- Os mesmos nomes devem ser evitados para dois propósitos diferentes.
- Se uma entidade tiver um nome semelhante ao de outra entidade, provavelmente sua funcionalidade é muito semelhante e elas precisam ser combinadas? Caso contrário, seus nomes precisam ser alterados para que não sejam semelhantes.
- Se você renomear mentalmente uma entidade ao ler o código para entender melhor sua funcionalidade, renomeie-a para esse nome mental.
- Escolha uma palavra para um conceito. Será difícil entender a funcionalidade quando você buscar, recuperar e entrar nos nomes. Melhor chegar a todos os lugares.
- Um nome longo e compreensível é melhor que um nome curto, mas incompreensível.
Funções
- As funções devem ser curtas e compactas.
- As funções devem ser muito curtas e muito compactas.
- Um máximo aproximado de 20 linhas e 150 caracteres em uma linha, se não couber, será necessário separar.
- Uma função deve executar apenas uma operação.
- Ela deve fazê-lo bem e não deve fazer mais nada.
- Se uma função executar apenas as ações que estão no mesmo nível de abstração, a função executará uma operação.
- Para determinar se uma função executa mais de uma operação, tente extrair outra função, o que não será uma simples reformulação da implementação.
- Quaisquer instruções condicionais com seleções longas por meio de caso de opção, caso contrário, devem ser divididas ou combinadas sem duplicação, possivelmente em classes com implementações, e transferir a opção de implementação para a classe base, fábrica ou outra pessoa.
- Se, mais, enquanto, etc. deve conter uma chamada para uma função. Será mais legível, mais claro e mais fácil.
- O número ideal de argumentos de entrada para uma função é 0. Se houver mais de três argumentos de entrada, considere como se livrar deles, por exemplo, crie uma classe para esses argumentos.
- Quanto mais argumentos de entrada, mais difícil a função é entendida.
- A função para a qual o argumento argumento é passado, do qual depende a operação da função, indica que a função executa mais de uma operação. Tais funções devem ser divididas em duas e denominadas de nível superior.
- Uma função que modifica o argumento de entrada deve fornecer uma referência ao objeto alterado e não apenas modificá-lo sem retornar.
String transform(String text)
- Se a função precisar alterar o argumento de entrada, altere o estado do objeto proprietário.
- Se o argumento de entrada da função não for alterado (e for usado mais no código), copie o valor do argumento e trabalhe com a cópia dentro da função.
- Em vez de retornar nulo, é melhor usar um objeto vazio -
Collection.empty()
ou um objeto nulo - EmptyObject()
. - Sempre tente usar funções não estáticas. Se isso não for possível, use estático.
- Se houver um código que deve seguir um após o outro, passe os resultados da primeira função para a segunda, para que alguém não altere a sequência de chamadas.
- Use polimorfismo em vez de if / else ou switch / case ou when.
- Evite condições negativas.
Comentários
- Não use comentários se você puder usar uma função ou variável.
- Não comente o código incorreto - reescreva-o. Não vale a pena explicar o que acontece no código incorreto, é melhor torná-lo explícito e compreensível.
- Os comentários podem ser usados para transmitir algumas informações, avisos sobre as consequências, mas não para explicar como o código funciona.
- Use TODO e FIXME nos casos em que você precisa observar que o código precisa ser aprimorado, mas agora não há recursos para isso.
- Use
//region REGIONNAME //endregion REGIONNAME
e, se você usar, pense se é possível dividir a região em entidades. - Código de documento que é complexo, mas limpo.
- Não deixe o código antigo comentado. Você pode encontrá-lo no histórico de consolidação, se necessário.
- Os comentários devem ser concisos e claros. Os comentários de informações não devem ter muita informação. Tudo deve ser breve e direto ao ponto.
- Siga o estilo de código adotado no projeto.
- Siga as regras aceitas na equipe.
- Sujeito a formatação e estilo de código, o código será mais fácil de ler e melhor. Não é em vão que o livro é entregue ao editor antes de publicá-lo.
- Você precisa ter ferramentas automáticas que irão formatar o código para você.
- O arquivo de origem deve ser como um artigo de jornal. Há um cabeçalho, uma breve descrição na forma de parâmetros e conteúdo na forma de funções. Se não for esse o caso, altere a formatação.
- As entidades relacionadas entre si devem estar próximas, por exemplo, em um pacote, para que seja mais fácil navegar pelo código.
- Variáveis de classe (campos) devem estar no topo da classe.
- As variáveis de método devem estar mais próximas do local de uso.
- As funções devem estar na ordem da chamada. Se um chama o outro, a função de chamada deve estar acima do chamado. Por outro lado, funções privadas de nível inferior podem estar na parte inferior do arquivo e não interferir no entendimento do código de alto nível. Mas eu prefiro o primeiro caminho.
Objetos e estruturas de dados
- Você precisa trabalhar com abstrações para que a implementação possa ser facilmente alterada.
- Você deve trabalhar com abstrações, porque o cliente que usa a funcionalidade não deve conhecer os detalhes da implementação, ele deve saber qual implementação usar nesse caso.
- Você deve fornecer uma API com a qual você deve trabalhar e ocultar os detalhes da implementação, a estrutura. Portanto, será mais fácil trabalhar com essas entidades e adicionar novos tipos de comportamentos, funcionalidades e implementações.
- DTO - Objeto de Transferência de Dados. Uma classe que contém apenas dados e nenhuma funcionalidade. É necessário para transferir alguns dados. Um objeto desta classe deve ser imutável.
Aulas
- As aulas devem ser compactas.
- As aulas devem ser ainda mais compactas.
- O nome da classe deve descrever sua responsabilidade. A partir daqui, você pode calcular o tamanho da turma.
- O funcional da classe deve corresponder claramente e se encaixar no nome da classe.
- Divida a conexão em turmas pequenas. Não deve haver coesão rígida e abundante - isso complica o apoio e o desenvolvimento do projeto.
- Lembre-se de responsabilidade única. Uma entidade deve ter um e apenas um motivo para a mudança.
- Observe o encapsulamento. O encapsulamento enfraquecido deve sempre ser o último recurso.
- Geralmente declaramos variáveis e funções auxiliares privadas, mas às vezes elas precisam ser declaradas protegidas e poder acessá-las a partir do teste.
- Se um grupo de funções pertence a uma determinada funcionalidade, esse grupo de funções pode e deve ser alocado para uma classe separada e usar sua instância.
Tratamento de erros
- Use Exceções em vez de retornar códigos de erro.
- O tratamento de erros é uma operação. Se houver uma palavra-chave
try
na função, depois dos blocos catch/finally
não haverá mais nada na função. - Se você tem uma enumeração que lista erros, é melhor se livrar dela e usar exceções.
- Use exceções não verificadas para indicar explicitamente o local em que há problemas. Esses erros não precisam ser detectados; você precisa escrever um código para que esse erro nunca exista.
- Passe informações suficientes, além de gerar uma exceção, para que posteriormente os usuários do código possam entender o que realmente aconteceu.
- Em vez de instruções condicionais com tratamento de erros, é melhor lançar exceções e manipulá-las.
- Não passe nulo em nenhum lugar. Tente evitar isso o máximo possível.
- A manipulação de erros é uma tarefa separada e não se aplica à lógica principal do programa.
Fronteiras
- Sempre usamos algumas bibliotecas que geralmente nos oferecem uma funcionalidade muito ampla, muito pequena ou conflitam com a funcionalidade esperada, o que torna o código mais confuso em seu uso final. Você pode evitar isso simplesmente aplicando padrões como Decorator, Adapter, Facade ou outros.
- Há situações em que você precisa trabalhar com a funcionalidade que está em desenvolvimento ou ainda não foi adaptada para uso no código de produção. Nesse caso, você deve imaginar o que espera da biblioteca / essa funcionalidade e escrever sua interface ou criar uma entidade com a qual trabalhará no seu projeto da maneira que você precisa. Quando a biblioteca é concluída e se torna estável, você a adapta às suas estruturas prontas e usa a funcionalidade pronta.
Posfácio
Este artigo fornece apenas diretrizes para escrever um código limpo. Claro, eles podem ser negligenciados. Você só precisa entender que qualquer uma de suas decisões deve ter argumentos a favor.