JavaRush /Java 博客 /Random-ZH /代码规则:正确命名、好评论和坏评论的力量

代码规则:正确命名、好评论和坏评论的力量

已在 Random-ZH 群组中发布
编写代码的规则:正确命名、好评论和坏评论的力量 - 1 您有多少次需要理解别人的代码?当你花几天而不是几个小时来理解正在发生的事情的逻辑时。有趣的是,对于编写这段代码的人来说,一切都是清晰且非常透明的。这并不奇怪:毕竟,完美或理想的代码是一个非常模糊的概念,因为每个开发人员分别对世界和代码有自己的看法。我不止一次遇到这样的情况:我和我的同事查看相同的代码,对其正确性和简洁性有不同的看法。 编写代码的规则:正确命名、好评论和坏评论的力量 - 2这是一种熟悉的感觉,不是吗?但是,应该遵守一些经过时间考验的要点,这最终将对我们有利,因为如果您将代码保持在您自己希望接收的状态,世界会更快乐一点,清洁工。 编写代码的规则:正确命名、好评论和坏评论的力量 - 3上一篇关于编写代码的规则(或者更确切地说,一个小指南)的文章中,我们触及了编写整个系统及其元素(例如对象、接口、类、方法和变量)的建议。我在那里简要提到了某些元素的正确命名。今天我想讨论的正是这一点,因为正确的名称使代码的可读性变得更加容易。我们将借助代码中的反思和注释的小示例来结束正确代码的主题 - 这是好还是不好。那么让我们开始吧。

正确命名

正确的名称可以提高代码的可读性,从而节省熟悉时间,因为当名称大致描述其功能时,使用方法会更容易。由于代码中的所有内容都由名称(变量、方法、类、文件对象等)组成,因此在创建正确、干净的代码时,这一点变得非常重要。基于以上所述,名称应该传达变量的含义,例如变量存在的原因、它的作用以及如何使用它。我会一次又一次地指出,描述变量的最佳注释是它的正确名称。 编写代码的规则:正确命名、好评论和坏评论的力量 - 4

命名接口

接口通常使用以大写字母开头并以驼峰式大小写 (CamelCase) 书写的名称。在编写接口时,在其前面加上 I 前缀以将其指定为接口(例如 IUserService),这曾经是一个很好的做法,但这非常丑陋且分散注意力。在这种情况下,最好不写它(UserService),并在其实现中添加-Impl(UserServiceImpl)。好吧,或者作为最后的手段,将前缀 C (CUserService) 添加到其实现中。

类名

就像接口一样,名称大写并使用驼峰风格(CamelCase)。无论发生什么样的灾难,无论截止日期有多快,但永远不要记住,类的名称永远不应该是动词!类和对象名称必须是名词及其组合(UserController、UserDetails、UserAccount 等)。您不应该为每个类的名称提供该应用程序的缩写,因为这只会增加不必要的复杂性(例如,我们有一个用户数据迁移应用程序,我们将向每个类添加一个 UDM - UDMUserDeatils、UDMUserAccount、UDMUserController )。

方法名称

通常方法的名称以小写字母开头,但也使用驼峰式(CamelCase)。上面我们讨论了类名永远不应该是动词的事实。这里的情况是截然相反的:方法的名称必须是动词或其与动词的组合:findUserById、findAllUsers、createUser 等。创建方法(以及变量和类)时,为避免混淆,请使用一种命名方法。例如,要查找用户,该方法可以写为 getUserById 或 findUserById。还有一件事:不要在方法名称中使用幽默,因为他们可能不明白这个笑话,以及这个方法的作用。

变量名

在大多数情况下,变量名称以小写字母开头,也使用驼峰式命名,除非变量是全局常量。在这种情况下,名称的所有字母均以大写形式书写,并且单词之间用下划线“_”分隔。命名变量时,为了方便起见,您可以使用有意义的上下文。换句话说,当有一个变量作为较大对象的一部分时(例如,firstName、lastName、status),在这种情况下,您可以添加一个前缀,指示该变量所属的对象。例如:用户名字、用户姓氏、用户状态。当变量具有完全不同的含义时,您还需要避免使用类似的名称。变量的常见反义词:
  • 开始/结束
  • 第一个/最后一个
  • 锁定/解锁
  • 最小/最大
  • 下一个/上一个
  • 老新
  • 打开/关闭
  • 可见/不可见
  • 源/目标
  • 源/目的地
  • 上/下

短变量名

当我们有像 x 或 n 之类的变量时,我们不会立即看出编写代码的人的意图。方法 n 的作用并不明显:它需要更多深思熟虑的思考(那就是时间,时间,时间)。例如,我们有一个字段 - 负责用户的 id,我们将调用此变量 ResponsibleUserId,而不是像 x 或只是 id 这样的名称,这会立即增加可读性和意义。然而,像 n 这样的短名称在小方法的本地更改中占有一席之地,其中包含该更改的代码块只是几行代码,并且方法名称完美地描述了那里发生的情况。开发人员看到这样的变量,就会明白它的次要重要性和非常有限的范围。因此,变量名称的长度存在一定依赖性:变量名称越长,变量就越全局,反之亦然。例如,按日期查找最后保存的用户的方法:
public User findLastUser() {
   return findAllUsers().stream()
           .sorted((x, y) -> -x.getCreatedDate().compareTo(y.getCreatedDate()))
           .findFirst()
           .orElseThrow(() -> new ResourceNotFoundException("Any user doesn't exist "));
}
这里我们使用短名称 x 和 y 来设置流排序,然后忘记它们。

最佳长度

让我们继续名字长度的话题。最佳名称长度介于 MaximumNumberOfUsersInTheCurrentGroup 名称长度和 n 之间。也就是说,太短的代码会缺乏意义,而太长的代码会拉伸程序而不增加可读性,而且他们只是懒得每次都写它们。不考虑上述情况,对于像 n 这样的短名称变量,您需要将长度保持在大约 8-16 个字符。这不是一个严格的规则:更多的是一个指导方针。

小差异

我不能忽视名称中的细微差异,因为这也是一种不好的做法,因为您可能会感到困惑或花费大量额外时间来注意名称中的细微差异。例如,InvalidDataAccessApiUsageException 和 InvalidDataAccessResourceUsageException 之间的区别很难一眼看出。此外,使用小 L 和 O 时经常会出现错误信息,因为它们很容易与 1 和 0 混淆:在某些字体中,差异更明显,而在其他字体中则不那么明显。

语义部分

我们需要将语义部分放入名称中,但不要过度使用同义词,因为例如 UserData 和 UserInfo 实际上具有相同的含义,我们必须更深入地研究代码以了解我们需要什么特定对象。避免使用无信息的单词,例如,firstNameString:为什么我们需要单词 string?名称可以是日期类型对象吗?当然不是:因此,简单地说 - 名字。作为一个例子,我想提到布尔变量,例如 flagDelete。“标志”一词不具有任何语义含义。将其称为“isDelete”会更合理。

虚假信息

我还想就错误的命名说几句话。假设我们的名称为 userActivityList,并且名为 userActivityList 的对象不是 List 类型,而是用于存储的其他容器或自定义对象。这可能会让普通程序员感到困惑:最好将其称为 userActivityGroup 或 userActivities 之类的名称。

搜索

短而简单的名称的缺点之一是它们很难在大量代码中找到,因为什么更容易找到:名为 name 或 NAME_FOR_DEFAULT_USER 的变量?当然是第二种选择。有必要避免名称中经常出现的单词(字母),因为这只会增加搜索过程中找到的文件数量,这是不好的。我们想提醒您,程序员花在阅读代码上的时间比编写代码的时间多,因此请注意应用程序元素的命名。但如果你没能成功命名怎么办?如果方法的名称不能很好地描述其功能怎么办?这就是它发挥作用的地方,我们的下一个项目是评论。

评论

编写代码的规则:正确命名、好评论和坏评论的力量 - 5没有什么比相关的评论更好的了,但没有什么比无意义的、过时的或误导性的评论更能让模块变得混乱了。这是一把双刃剑,不是吗?尽管如此,你不应该将评论视为明确的善:相反,应将其视为较小的恶。毕竟,注释本质上是对代码中未成功表达的思想的补偿。例如,如果结果太令人困惑,我们会使用它们以某种方式传达该方法的本质。在这种情况下,最好正确地重构代码,而不是编写描述性注释。注释越旧,情况就越糟糕,因为代码往往会增长和演变,但注释可能保持不变,而且越深入,这些注释就变得越可疑。不准确的评论比没有评论更糟糕,因为它们会造成混乱和欺骗,给人错误的期望。即使我们有一个非常棘手的代码,仍然值得不对其进行评论,而是重写它。

评论类型

  • 法律注释是出于法律原因留在每个源代码文件开头的注释,例如:

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

  • 信息性注释- 提供代码解释的注释(提供给定代码部分的附加信息或意图。

    举个例子:

    /*
    * Объединяет пользователя из бд и пришедшего для обновления
    * Когда в requestUser поле пустое, оно заполняется старыми данными из 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()
           );
           }

    在这种情况下,您可以不加注释,因为方法的名称及其参数,再加上非常透明的功能,可以很好地描述自己。

  • 警告注释- 其目的是警告其他开发人员某些操作的不良后果的注释(例如,为什么测试被标记为 @Ignore):

    // Слишком долго отрабатывает
    // Не запускайте, если не располагаете избытком времени
    @Ignore
    @Test
    public void someIntegrationTest() {
           ……
           }
  • TODO - 注释是未来需要完成的注释,但由于某种原因现在无法完成。这是一个很好的做法,但仍然需要定期审查它们以删除不相关的内容以避免混乱。

    Примером послужит:

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

    Тут мы помечаем, что нужно добавить проверку юзера, который скачивает (id которого мы вытащим из security контекста) с тем, кто сохранил.

  • усorвающий комментарий — комментарий, подчеркивающий важность Howого-то обстоятельства, что на первый взгляд может показаться несущественным.

    Как пример, кусочек метода, заполняющий тестовую БД, некими скриптами:

    Stream.of(IOUtils.resourceToString("/fill-scripts/" + x, StandardCharsets.UTF_8)
           .trim()
           .split(";"))
           .forEach(jdbcTemplate::update);
    // Вызов trim() очень важен, убирает возможные пробелы в конце скрипта
    // чтобы при считке и разбивке на отдельные requestы не было пустых

  • javaDoc — комментарии, которые описывают API определенного функционала для общего пользования. Наверное, самые полезные комментарии, так How с documentированным API в разы легче работать, но они также могут устаревать, How и любые другие. Поэтому не забываем, что главный вклад в documentацию вносится не комментариями, а хорошим codeом.

    Пример вполне обычного метода обновления пользователя:

    /**
    * Обновляет передаваемые поля для пользователя по id.
    *
    * @param id  id обновляемого пользователя
    * @param user пользователь с заполненными полями для обновления
    * @return обновленный пользователь
    */
           User update(Long id, User user);

Плохие сценарии комментариев

编写代码的规则:正确命名、好评论和坏评论的力量 - 7
  • бормочущий комментарий — комментарии, которые обычно пишут на скорую руку, смысл которых понятен только разработчику, писавшего их, так How только он видит ту ситуацию с теми нюансами, на которые он и ссылается.

    Рассмотрим данный пример:

    public void configureSomeSystem() {
           try{
           String configPath = filesLocation.concat("/").concat(CONFIGURATION_FILE);
           FileInputStream stream = new FileInputStream(configPath);
           }  catch (FileNotFoundException e) {
           //В случае отсутствия конфигурационного file, загружается конфигурация по умолчанию
          }
    }

    Кто загружает эти настройки? Были ли они загружены ранее? Метод предназначен для перехвата исключений и вызова дефолтных настроек? Слишком много вопросов возникает, ответы на которые можно получить лишь углубившись в изучение других частей системы.

  • избыточный комментарий — комментарий, который не несёт смысловой нагрузки, так How и так понятно что происходит в заданном участке codeа (он читается не проще, чем code).

    Смотрим пример:

    public class JdbcConnection{
    public class JdbcConnection{
       /**
        * Журнальный компонент, связанный с текущим классом
        */
       private Logger log = Logger.getLogger(JdbcConnection.class.getName());
    
       /**
        * Создаёт и возвращает connection с помощью входящих параметров
        */
       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;
       }

    Какой смысл таких комментариев, если мы и так всё прекрасно видим

  • недостоверные комментарии — комментарии, не соответствующие истине и лишь вгоняющие в заблуждение (дезинформирующие). Как например:

    /**
    * Вспомогательный метод, закрывает соединение со сканером, если isNotUsing истинно
    */
    private void scanClose(Scanner scan, boolean isNotUsing) throws Exception {
       if (!isNotUsing) {
           throw new Exception("The scanner is still in use");
       } scan.close();
    }

    What в этом комменте не так? А то, что он немножко врёт нам, ведь соединение закрывается, если isNotUsing = false, но ниHow не наоборот, How нам вещает пометка.

  • обязательные комментарии — комментарии, которые считают обязательными (Javadoc), но кои по факту иногда бывают излишне нагромождающими, недостоверными и ненужными (нужно задуматься, а нужны ли здесь такие комментарии).

    Пример:

    /**
    *  Creation пользователя по переданным параметрам
    * @param firstName Name созданного пользователя
    * @param middleName среднее Name созданного пользователя
    * @param lastName фамorя созданного пользователя
    * @param age возраст созданного пользователя
    * @param address addressс созданного пользователя
    * @return пользователь который был создан
    */
    User createNewUser(String firstName, String middleName, String lastName, String age, String address);

    Смогли бы вы понять, что делает метод без этих комментариев? Скорее всего да, поэтому комментарии в этом случае стают бессмысленными.

  • журнальные комментарии — комментарии, которые иногда добавляют в начало модуля, при каждом его редактировании (что-то вроде журнала вносимых изменений).

    /**
    *  Записи ведутся с 09 января 2020;
    **********************************************************************
    *  09.01.2020  : Обеспечение соединения с БД с помощью Jdbc Connection;
    *  15.01.2020  : Добавление интерфейсов уровня дао для работы с БД;
    *  23.01.2020  : Добавление интеграционных тестов для БД;
    *  28.01.2020  : Имплементация интерфейсов уровня дао;
    *  01.02.2020  : Разработка интерфейсов для сервисов,
    *  согласно требованиям прописанным в user stories;
    *  16.02.2020  : Имплементация интерфейсов сервисов
    *  (реализация бизнес логики связанной с работой БД);
    *  25.02.2020  : Добавление тестов для сервисов;
    *  08.03.2020  : Празднование восьмого марта(Миша опять в хлам);
    *  21.03.2020  : Рефакторинг сервис слоя;
    */

    Когда-то этот проход был оправдан, но с появлением систем управления исходным codeом (например — Git), это стало лишним нагромождением и усложнением codeа.

  • комментарии ссылки на авторов — комментарии, преднаmeaningм которых является, указание человека, писавшего code, чтобы можно было связаться и обсудить, How что и зачем:

    * @author  Bender Benderovich

    Опять же, системы контроля версий прекрасно запоминают, кто и когда добавил данный code, и подобный подход излишен.

  • 注释代码是由于某种原因被注释掉的代码。最糟糕的习惯之一,因为你注释掉了它然后忘记了,而其他开发人员根本没有勇气删除它(如果它是有价值的东西怎么办)。

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

    结果,它像垃圾一样堆积起来。在任何情况下都不应该留下这样的代码。如果您确实需要它,请不要忘记版本控制系统。

  • 非显而易见的注释是以不必要的复杂方式描述某事物的注释。

    /*
        * Начать с массива, размер которого достаточен для хранения
        * всех byteов данных (плюс byteы фильтра) с запасом, плюс 300 byte
        * для данных заголовка
        */
    this.dataBytes = new byte[(this.size * (this.deep + 1) * 2)+300];

    注释应该解释代码,而不需要解释本身。这里是什么?什么是“过滤字节”?+1 跟它有什么关系?为什么正好是300?

如果您决定撰写评论,这里有一些使用它们的提示:
  1. 使用易于维护的样式:维护过于花哨和异国情调的样式可能会很烦人和耗时。
  2. 不要在引用单行的行末尾使用注释:这会产生大量注释,并且很难为每一行提供富有表现力的注释。
  3. 创建评论时,尝试回答“为什么”而不是“如何”的问题。
  4. 避免缩写。正如我上面所说,我们不需要对评论进行解释:评论就是解释。
  5. 您可以使用注释来标记测量单位和可接受值的范围。
  6. 将注释放在靠近它们描述的代码的地方。
因此,我仍然想提醒您:最好的注释是没有注释,而不是在应用程序中正确命名。一般来说,大多数时候我们已经在使用现成的代码,维护和扩展它。当代码易于阅读和理解时,就会方便得多,因为糟糕的代码会妨碍你,就像在轮子上插了一根辐条,而匆忙是它忠实的伴侣。而糟糕的代码越多,性能下降的就越多,所以我们需要时不时地进行重构。但是,如果从一开始你就尝试编写后续开发人员不想找到你并杀死你的代码,那么你将需要更少地重构它。但这仍然是必要的,因为产品的条件和要求在不断变化,并通过添加额外的连接来补充,这是不可避免的。最后,我将在这里、这里这里留下一些有趣的链接供您熟悉这个主题, 我想这就是我今天的全部内容,感谢所有阅读的人)) 编写代码的规则:正确命名、好评论和坏评论的力量 - 8
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION