CodeGym /Blogue Java /Random-PT /Regras de codificação: o poder dos nomes corretos, coment...
John Squirrels
Nível 41
San Francisco

Regras de codificação: o poder dos nomes corretos, comentários bons e ruins

Publicado no grupo Random-PT
Regras de codificação: o poder dos nomes corretos, comentários bons e ruins - 1Quantas vezes você teve que cavar no código de outra pessoa? Em vez de duas horas, você pode passar dois dias para simplesmente entender a lógica do que está acontecendo. O engraçado é que para quem escreveu o código tudo é claro e totalmente transparente. Isso não é surpreendente: afinal, código perfeito é um conceito muito vago, pois cada desenvolvedor tem uma visão própria do mundo e do código também. Mais de uma vez, estive em uma situação em que um colega de trabalho e eu olhamos para o mesmo código e tínhamos opiniões diferentes sobre sua correção e limpeza.Regras de codificação: o poder dos nomes corretos, comentários bons e ruins - 2Soa familiar, não é? Ainda assim, existem alguns princípios testados pelo tempo que devem ser respeitados. No final, eles serão vantajosos para você, porque se você deixar seu código no estado em que gostaria de recebê-lo, o mundo ficaria um pouco mais feliz e limpo. Em nosso artigo anterior(ou melhor, um pequeno guia) sobre regras de codificação, obtivemos uma pequena noção de recomendações para escrever um sistema como um todo e suas partes constituintes, como objetos, interfaces, classes, métodos e variáveis. Nesse mesmo artigo, mencionei casualmente a nomenclatura correta de certos elementos. Gostaria de falar sobre isso hoje, porque nomes corretos tornam o código muitas vezes mais fácil de ler. Fecharemos o tópico do código correto com algumas reflexões, pequenos exemplos de comentários no código e uma consideração sobre se isso é bom ou não tão bom. Bem, vamos começar.

nomes corretos

Nomes corretos melhoram a legibilidade do código, reduzindo assim o tempo necessário para se familiarizar com o código, porque usar um método é muito mais fácil quando seu nome descreve aproximadamente sua funcionalidade. Tudo no código consiste em nomes (variáveis, métodos, classes, objetos, arquivos, etc.), então esse ponto se torna muito importante ao criar um código limpo e correto. Com base no exposto, o nome deve transmitir um significado, por exemplo, por que a variável existe, o que ela faz e como é usada. Observarei mais de uma vez que o melhor comentário para uma variável é dar a ela um bom nome.Regras de codificação: o poder dos nomes corretos, comentários bons e ruins - 3

da série de TV "Sherlock" (2010-2017)

Nomeando interfaces

As interfaces geralmente têm nomes que começam com letra maiúscula e são escritas em CamelCase. Ao escrever uma interface, costumava ser considerado uma boa prática adicionar o prefixo "I" para designá-la como uma interface (por exemplo, IUserService), mas isso parece muito feio e perturbador. Nesses casos, é melhor omitir o prefixo (UserService) e adicionar "Impl" como sufixo ao nome de sua implementação (por exemplo, UserServiceImpl). Ou possivelmente, como último recurso, adicione um prefixo "C" ao nome da implementação (por exemplo, CUserService).

nomes de classes

Assim como as interfaces, os nomes das classes são maiúsculos e usam CamelCase. Não importa se estamos enfrentando um apocalipse zumbi, não importa se o fim está próximo — nunca, nunca, nunca o nome de uma classe deve ser um verbo! Os nomes de classes e objetos devem ser substantivos ou substantivos compostos (UserController, UserDetails, UserAccount e assim por diante). Você não deve inserir a abreviação do aplicativo no final do nome de cada classe, pois isso só adicionaria complexidade desnecessária. Por exemplo, se tivermos um aplicativo de migração de dados do usuário, não adicione "UDM" a cada classe, ou seja, UDMUserDetails, UDMUserAccount, UDMUserController.

Nomes de métodos

Normalmente, os nomes dos métodos começam com uma letra minúscula, mas também usam o estilo camel case (camelCase). Acima, dissemos que os nomes das classes nunca devem ser verbos. Aqui a situação é exatamente oposta: os nomes dos métodos devem ser verbos ou frases verbais: findUserById, findAllUsers, createUser e assim por diante. Ao criar um método (assim como variáveis ​​e classes), use uma convenção de nomenclatura consistente para evitar confusão. Por exemplo, para localizar um usuário, um método pode ser denominado getUserById ou findUserById. E mais uma coisa: não use humor nos nomes dos métodos, porque os outros podem não entender a piada. Como resultado, eles podem não conseguir entender o que o método faz.

nomes de variáveis

Na maioria dos casos, os nomes das variáveis ​​começam com uma letra minúscula e também usam camelCase, exceto quando a variável é uma constante global. Nesses casos, todas as letras do nome são escritas em maiúsculas e as palavras são separadas por sublinhado ("_"). Por conveniência, você pode usar um contexto significativo ao nomear variáveis. Em outras palavras, quando uma variável existe como parte de algo maior, por exemplo, firstName, lastName ou status. Nesses casos, você pode adicionar um prefixo que indique o objeto ao qual essa variável pertence. Por exemplo: userFirstName, userLastName, userStatus. Você também deve evitar nomes semelhantes para variáveis ​​quando elas tiverem significados completamente diferentes. Aqui estão alguns antônimos frequentemente encontrados em nomes de variáveis:
  • começo/fim
  • primeiro último
  • bloqueado/desbloqueado
  • mínimo máximo
  • próximo/anterior
  • velho/novo
  • aberto/fechado
  • visível/invisível
  • origem/destino
  • fonte de destino
  • cima baixo

Nomes curtos de variáveis

Quando temos variáveis ​​como x ou n ou algo assim, não vemos imediatamente a intenção da pessoa que escreveu o código. Não é óbvio o que n faz. Descobrir isso requer uma contemplação mais cuidadosa (e isso significa tempo, tempo, tempo). Por exemplo, suponha que temos um campo que representa o id do usuário responsável. Em vez de algum nome de variável como x ou simplesmente id, vamos nomear essa variável como "responsibleUserId", o que melhora imediatamente a legibilidade e o conteúdo das informações. Dito isso, nomes curtos como n têm um lugar como variáveis ​​locais em métodos pequenos, onde o bloco de código envolvendo essa variável tem apenas algumas linhas e o nome do método descreve perfeitamente o que acontece lá. Vendo tal variável, um desenvolvedor entende que ela é de importância secundária e tem um escopo muito limitado. Como resultado, o escopo tem uma certa dependência do comprimento de um nome de variável: quanto maior o nome, mais global é a variável e vice-versa. Como exemplo, aqui está um método para encontrar o último usuário salvo por data:

public User findLastUser() {
   return findAllUsers().stream()
           .sorted((x, y) -> -x.getCreatedDate().compareTo(y.getCreatedDate()))
           .findFirst()
           .orElseThrow(() -> new ResourceNotFoundException("No user exists"));
}
Aqui, usamos variáveis ​​de nome curto x e y para classificar o fluxo e depois as esquecemos.

comprimento ideal

Vamos continuar com o tópico do tamanho do nome. O tamanho ideal do nome está entre n e maximumNumberOfUsersInTheCurrentGroup. Em outras palavras, nomes curtos sofrem de falta de significado, enquanto nomes muito longos alongam o programa sem adicionar legibilidade, e simplesmente temos preguiça de escrevê-los todas as vezes. Além do caso descrito acima para variáveis ​​com um nome curto como n, você deve manter um comprimento de cerca de 8 a 16 caracteres. Esta não é uma regra rígida, apenas uma diretriz.

pequenas diferenças

Não posso deixar de mencionar diferenças sutis nos nomes. Essa também é uma prática ruim, pois essas diferenças podem ser simplesmente confusas ou exigir muito tempo extra para percebê-las. Por exemplo, a diferença entre InvalidDataAccessApiUsageException e InvalidDataAccessResourceUsageException é difícil de detectar rapidamente. Muitas vezes, também pode surgir confusão ao usar L e O minúsculos, porque eles podem ser facilmente confundidos com 1 e 0. Em algumas fontes, a diferença é mais óbvia, em outras é menor.

O significado

Precisamos tornar os nomes significativos, mas não criar ambigüidade por meio de sinônimos, pois, por exemplo, UserData e UserInfo realmente têm o mesmo significado. Nesse caso, teríamos que nos aprofundar no código para entender qual objeto específico precisamos. Evite palavras que não transmitam informações úteis. Por exemplo, em firstNameString, por que precisamos da palavra String? Isso poderia realmente ser um objeto Date? Claro que não. Portanto, simplesmente usamos firstName. Eu também gostaria de mencionar variáveis ​​booleanas. Como exemplo, pegue um booleano chamado flagDeleted. A palavra bandeira não tem significado. É mais razoável chamá-lo de isDeleted.

Desinformação

Também gostaria de dizer algumas palavras sobre convenções de nomenclatura incorretas. Digamos que temos uma variável chamada userActivityList, mas em vez de ser uma lista, esse objeto é algum outro tipo de contêiner ou objeto de armazenamento personalizado. Isso pode confundir o programador médio: é melhor chamá-lo de algo como userActivityGroup ou userActivities.

Procurar

Uma das desvantagens de nomes curtos e simples é que eles são difíceis de encontrar em um grande corpo de código — Qual seria mais fácil de encontrar: "name" ou "NAME_FOR_DEFAULT_USER"? A segunda opção, claro. Devemos evitar palavras (letras) encontradas com frequência nos nomes, pois elas apenas aumentarão o número de arquivos correspondentes durante uma pesquisa, o que não é bom. Gostaria de lembrá-lo de que os programadores gastam mais tempo lendo o código do que escrevendo, portanto, seja esperto ao nomear os elementos do seu aplicativo. Mas e se um bom nome simplesmente não puder ser encontrado? E se o nome de um método não descrever bem sua funcionalidade? É aqui que os comentários entram em cena.

Comentários

Regras de codificação: o poder dos nomes corretos, comentários bons e ruins - 4Não há nada melhor do que um comentário pertinente, mas nada sobrecarrega um módulo como comentários vazios, desatualizados ou falsos. Eles podem ser uma faca de dois gumes, não? Ainda assim, você não deve tratar os comentários como inequivocamente bons, mas sim como um mal menor. Afinal, um comentário é essencialmente uma forma de compensar o pensamento que não aparece claramente no código. Por exemplo, nós os usamos para transmitir de alguma forma a essência de um método, se o método em si for muito confuso. Nessa situação, é melhor refatorar corretamente o código do que escrever notas descritivas. Quanto mais antigo o comentário, pior o comentário, pois o código tende a crescer e evoluir, mas os comentários podem permanecer os mesmos. Quanto mais tempo se passou desde que um comentário foi criado, mais questionável ele pode ser. Comentários imprecisos são muito piores do que nenhum comentário, porque são confusos e enganosos, gerando falsas expectativas. E mesmo que tenhamos um código muito complicado, devemos reescrevê-lo em vez de comentá-lo.

Tipos de comentários

  • Comentários legais — Comentários no início de cada arquivo de origem por motivos legais, por exemplo:

    
    * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
    * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
    

  • Comentários informativos — Comentários que representam uma explicação do código (fornecendo informações adicionais ou expondo a intenção de uma determinada seção do código).

    Por exemplo:

    
    /*
    * Combines the user from the database with the one passed for updating
    * When a field in requestUser is empty, it is filled with old data from foundUser
    */
    private User mergeUser(User requestUser, User foundUser) {
           return new User(
           foundUser.getId(),
           requestUser.getFirstName() == null ? requestUser.getFirstName() : foundUser.getFirstName(),
           requestUser.getMiddleName() == null ? requestUser.getMiddleName() : foundUser.getMiddleName(),
           requestUser.getLastName() == null ? requestUser.getLastName() : foundUser.getLastName(),
           requestUser.getAge() == null ? requestUser.getAge() : foundUser.getAge()
           );
           }
    

    Nesse caso, você pode prescindir de comentários, pois o nome do método e seus parâmetros, aliados a uma funcionalidade bastante transparente, se descrevem bem.

  • Warning comments — Comentário destinado a alertar outros desenvolvedores sobre as consequências indesejáveis ​​de uma ação (por exemplo, alertando-os sobre o motivo pelo qual um teste foi marcado como @Ignore):

    
    // Takes too long to run
    // Don't run if you don't have a lot of time
    @Ignore
    @Test
    public void someIntegrationTest() {
           ……
           }
    

  • TODO — Comentários que são uma nota sobre algo que precisa ser feito no futuro, mas por algum motivo não pode ser feito agora. Essa é uma boa prática, mas esses comentários devem ser revisados ​​regularmente para remover os irrelevantes e evitar confusão.

    Um exemplo seria:

    
    // TODO: Add a check for the current user ID (when the security context is created)
    
    @Override
    public Resource downloadFile(File file) {
           return fileManager.download(file);
           }
    

    Aqui notamos o fato de que precisamos adicionar uma comparação do usuário que realizou a operação de download (cujo ID extrairemos do contexto de segurança) com aquele que realizou a operação de salvamento.

  • Comentários reforçadores — Comentários enfatizando a importância de uma circunstância que à primeira vista pode parecer insignificante.

    Como exemplo, considere uma parte de um método que preenche um banco de dados de teste com alguns scripts:

    
    Stream.of(IOUtils.resourceToString("/fill-scripts/" + x, StandardCharsets.UTF_8)
           .trim()
           .split(";"))
           .forEach(jdbcTemplate::update);
    // The trim() call is very important. It removes possible spaces at the end of the script
    // so that when we read and split into separate requests, we don't end up with empty ones
    

  • Comentários Javadoc — Comentários que descrevem a API para determinada funcionalidade. Provavelmente existem os comentários mais úteis, já que a API documentada é muito mais fácil de trabalhar. Dito isso, eles também podem estar desatualizados como qualquer outro tipo de comentário. Portanto, nunca esqueça que a principal contribuição para a documentação não é feita por comentários, mas por um bom código.

    Aqui está um exemplo de um método bastante comum para atualizar um usuário:

    
    /**
    * Updates the passed fields for a user based on its id.
         *
    * @param id id of the user to be updated
    * @param user user with populated fields for updating
    * @return updated user
    */
           User update(Long id, User user);
    

comentários ruins

  • comentário resmungão — Comentários que geralmente são escritos às pressas e cujo significado é compreensível apenas para o desenvolvedor que os escreveu, pois somente ele percebe a situação nuançada a que o comentário se refere.

    Considere este exemplo:

    
    public void configureSomeSystem() {
           try{
           String configPath = filesLocation.concat("/").concat(CONFIGURATION_FILE);
           FileInputStream stream = new FileInputStream(configPath);
           } catch (FileNotFoundException e) {
           // If there is no configuration file, the default configuration is loaded 
          }
    }
    

    Quem carrega essas configurações? Já foram carregados? Este método deve capturar exceções e carregar as configurações padrão? Surgem muitas questões que só podem ser respondidas investigando outras partes do sistema.

  • Comentários redundantes — Comentários que não carregam nenhuma carga semântica, pois o que está acontecendo em uma determinada seção do código é abundantemente claro. Em outras palavras, o comentário não é mais fácil de ler do que o código.

    Vejamos um exemplo:

    
    public class JdbcConnection{
    public class JdbcConnection{
       /**
        * The logger associated with the current class
        */
       private Logger log = Logger.getLogger(JdbcConnection.class.getName());
    
       /**
        * Creates and returns a connection using the input parameters
        */
       public static Connection buildConnection(String url, String login, String password, String driver) throws Exception {
           Class.forName(driver);
           connection = DriverManager.getConnection(url, login, password);
           log.info("Created connection with db");
           return connection;
       }
    

    Qual é o sentido de tais comentários? Tudo o que eles explicam já está perfeitamente claro.

  • Comentários não confiáveis ​​— Comentários falsos e apenas enganosos (desinformação). Por exemplo, aqui está um.

    
    /**
    * Helper method. Closes the connection with the scanner if isNotUsing is true
    */
    private void scanClose(Scanner scan, boolean isNotUsing) throws Exception {
       if (!isNotUsing) {
           throw new Exception("The scanner is still in use");
       } scan.close();
    }
    

    O que há de errado com este comentário? O fato de mentir um pouco para nós, em que a conexão é fechada se isNotUsing é falso, não vice-versa, como nos informa o comentário.

  • Comentários obrigatórios — Comentários considerados obrigatórios (por exemplo, comentários Javadoc), mas que, na verdade, às vezes se acumulam excessivamente e são pouco confiáveis ​​e desnecessários (você precisa pensar se esses comentários são realmente necessários).

  • Exemplo:

    
    /**
    * Create a user based on the parameters
    * @param firstName first name of the created user
    * @param middleName middle name of the created user
    * @param lastName last name of the created user
    * @param age age of the created user
    * @param address address of the created user
    * @return user that was created
    */
    User createNewUser(String firstName, String middleName, String lastName, String age, String address);
    

    Você conseguiria entender o que o método faz sem esses comentários? Provavelmente, sim, então os comentários se tornam inúteis aqui.

  • Comentários de log — Comentários que às vezes são adicionados ao início de um módulo cada vez que ele é editado (algo como um log de alterações).

    
    /**
    * Records kept since January 9, 2020;
    **********************************************************************
    * 9 Jan 2020: Providing a database connection using JDBC Connection;
    * 15 Jan 2020: Adding DAO-level interfaces for working with the database;
    * 23 Jan 2020: Adding integration tests for the database;
    * 28 Jan 2020: Implementation of DAO-level interfaces;
    * 1 Feb 2020: Development of interfaces for services,
    * in accordance with the requirements specified in user stories;
    * 16 Feb 2020: Implementation of service interfaces
    * (implementation of business logic related to the work of the database);
    * 25 Feb 2020: Adding tests for services;
    * 8 Mar 2020: Celebration of International Women's Day (Terry is drunk again);
    * 21 Mar 2020: Refactoring the service layer;
    */
    

    Essa abordagem já foi justificada, mas com o advento dos sistemas de controle de versão (por exemplo, Git), tornou-se uma confusão desnecessária e uma complicação do código.

  • Comentários de autoria — Comentários cujo objetivo é indicar a pessoa que escreveu o código, para que você possa contatá-la e discutir como, o quê e por quê, por exemplo:

    
    * @author Bender Bending
    

    Mais uma vez, os sistemas de controle de versão lembram exatamente quem adicionou qualquer trecho de código e quando, então essa abordagem é supérflua.

  • Código comentado — Código que foi comentado por um motivo ou outro. Esse é um dos piores hábitos, porque o que acontece é você comentar algo e esquecer, e outros desenvolvedores simplesmente não tem coragem de deletar (afinal, e se for algo valioso?).

    
    //    public void someMethod(SomeObject obj) {
    //    .....
    //    }
    

    Como resultado, o código comentado se acumula como lixo. Em nenhum caso você deve deixar esse código. Se você realmente precisar, não se esqueça do sistema de controle de versão.

  • Comentários não óbvios — Comentários que descrevem algo de forma excessivamente complicada.

    
    /*
        * Start with an array large enough to store
        * all the data bytes (plus filter bytes) with a cushion, plus 300 bytes
        * for header data
        */
    this.dataBytes = new byte[(this.size * (this.deep + 1) * 2)+300];
    

    Um comentário deve explicar o código. Ele próprio não deveria precisar de uma explicação. Então, o que há de errado aqui? O que são "bytes de filtro"? O que é esse "+ 1"? Por que exatamente 300?

Se você já decidiu escrever comentários, aqui vão algumas dicas:
  1. Use estilos fáceis de manter: manter estilos muito extravagantes e exóticos é chato e demorado.
  2. Não use comentários de fim de linha que se refiram a linhas únicas: o resultado é uma grande pilha de comentários. Além do mais, é difícil pensar em um comentário significativo para cada linha.
  3. Ao redigir um comentário, tente responder à pergunta "por que", não "como".
  4. Evite informações resumidas. Como eu disse acima, não precisamos de uma explicação para um comentário: o próprio comentário é a explicação.
  5. Você pode usar comentários para anotar unidades e intervalos de valores.
  6. Coloque comentários perto do código que eles descrevem.
Por fim, ainda quero lembrá-lo de que o melhor comentário é não comentar, mas sim o uso de nomenclatura hábil em todo o aplicativo. Via de regra, na maioria das vezes trabalharemos com código existente, mantendo-o e estendendo-o. É muito mais conveniente quando esse código é fácil de ler e compreensível, pois um código ruim é um obstáculo. É como jogar uma chave inglesa nas obras, e a pressa é sua fiel companheira. E quanto mais código ruim tivermos, mais o desempenho cairá. Isso significa que precisamos refatorar de tempos em tempos. Mas se desde o início você tentar escrever um código que não fará com que os próximos desenvolvedores queiram encontrá-lo e matá-lo, você não precisará refatorá-lo com tanta frequência. Mas ainda será necessário, pois as condições e requisitos do produto mudam constantemente com a adição de novas dependências e conexões. Bem, acho que isso é tudo para mim hoje. Obrigado a todos que leram até aqui :)
Comentários
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION