Хороший, качественный код. Комментарии

· На чтение уйдёт 4 минуты · (782 слова)

Очень часто, и в книгах, и в Интернет, и (возможно) от начальства, можно слышать про необходимость комментариев. Давайте посмотрим, насколько написание комментариев является необходимым.

Во-первых, чем большее количество разработчиков работают над проектом, тем более необходимо наличие комментариев. Если разработчиков немного, возникает другой фактор - фактор человеческой памяти. Если еще три-четыре месяца спустя написания кода можно прекрасно вспомнить, зачем он нужен, как писался, что и почему он делает - вряд ли это удастся вспомнить спустя несколько лет. Особенно, если ведется интенсивная работа над различными проектами.

С другой стороны, комментарии внутри методов - очень часто излишни. Например, напишем (с использованием псевдокода, подозрительно похожего на Java) небольшой метод для магазина по торговле электронными товарами:

[geshi lang=Java] 
public class ElectronicStore { 
  /**  
   * Приобретение электронного товара 
   * @param buyerId покупатель товара 
   * @param itemId идентификатор товара 
   * @return true, если покупка была успешна 
   */ 
  public final boolean buyItem(int buyerId, int itemId) throws SQLException { 
    // Проверим на валидность покупателя товара: 
    String sql = "SELECT * FROM accounts WHERE userid = ?"; // выборка из таблицы пользователей 
    PreparedStatement stmt = conn.prepareStatement(sql); // готовим запрос 
    stmt.setInt(1, buyerId); // userid = buyerId 
    ResultSet res = stmt.executeQuery(); // Что у нас получилось? 
    if (res.next()) { // мы получили какие-то данные 
      String sql2 = "SELECT * FROM items WHERE itemid = ?"; // выборка из таблицы товаров 
      PreparedStatement stmt2 = conn.prepareStatement(sql2); // создали 
      stmt2.setInt(1, itemId); // itemid = itemId 
      ResultSet res2 = stmt2.executeQuery(); // Что у нас получилось? 
      if (res2.next()) { // мы получили какие-то данные 
        float itemPrice = res2.getFloat(2); // сколько стоит товар 
        float userMoney = res.getFloat(2); // сколько денег на счету 
        if (userMoney >= itemPrice) { 
          String sql3 = "UPDATE accounts SET money = money - ?"; // запрос на снятие денег 
          String sql4 = "INSERT INTO orders VALUES(?, ?, NOW())"; // запрос на запись в лог 
          //... 
          return true; 
        } 
      } 
    } 
  } 
}
[/geshi]

Закроем глаза на то, что написан пример не вполне так, как обычно пишут на Java. Закроем глаза на то, что не используются транзакции. Закроем глаза на SQLException. Казалось бы - откомментирована буквально каждая строчка кода. Пробежавшись по исходникам, вроде бы все также становится ясно. Правда же? Стоп-стоп-стоп! Что же все-таки не так в этом коде? В этом коде "не так" то, что он нуждается в комментировании.

Грамотно написанный код может и должен быть понятен безо всяких комментариев. Например, разве здесь нужны комментарии?

[geshi lang=Java]
window.setTitle("Hello world");
window.moveTo(100, 100);
window.setSize(200, 200);
[/geshi]
[geshi lang=Java]
  /**  
   * Приобретение электронного товара 
   * @param buyerId покупатель товара 
   * @param itemId идентификатор товара 
   * @return true, если покупка была успешна 
   * @throws ElectronicStoreException в случае, если приобрести товар невозможно 
   */
public final boolean buyItem(int buyerId, int itemId) throws ElectronicStoreException { 
  UserAccount user = loadUser(buyerId); 
  ElectronicItem item = loadItem(itemId); 
  if (user.hasEnoughMoney(item.getPrice()) { 
    createItemOrder(buyerId, itemId); 
    // ... 
  }
  return false;
}[/geshi]

Имеем: код написан на почти нормальном английском языке, в излишних комментариях не нуждается. javadoc для методов loadUser, loadItem, hasEnoughMoney, createItemOrder,конечно же, необходим. Но не надо там писать трактаты, вполне достаточно написать какие-либо особенности реализации метода:

[geshi lang=Java]
  /**  
   * Загрузка пользователя из БД
   * @param userId уникальный идентификатор пользователя
   * @throws ElectronicStoreException в случае, когда пользователь не найден
   */
protected final UserAccount loadUser(int userId) throws ElectronicStoreException {
  // ...
}[/geshi]

Такие небольшие методы "документируют" сами себя. Если метод состоит из десятка строк, и этот десяток строк написан на английском языке, и при этом не содержит или почти не содержит ветвлений, документировать его, на мой взгляд, необязательно, хотя и допустимо. Но... если недокументированный код способен понять даже не-программист, лишь взглянув на него, зачем писать комментарии?..

Комментировать или не комментировать? Решать, разумеется, вам. Лично я считаю, что в целях недопущения хаоса и в целях экономии времени, следует придерживаться таких правил:

  • Базовая часть комментария к методу или переменной должен быть однострочной. Если в одну строчку описание не умещается, возможно, метод просто слишком громоздкий?
  • Обязательно должны присутствовать какие-либо особые требования или примечания. Например, что будет, если передать в метод null.
  • Весьма желательно, чтобы в комментарии к методу присутствовал список Exception'ов, которые могут возникнуть во время работы метода, и в каких случаях они возникают
  • Хорошо, когда подобные комментарии есть у всех public методов.
  • Очень хорошо, когда подобные комментарии есть у всех public методов, и у всех полей класса
  • Отлично, если подобный комментарий присутсвует у всех protected, public и package local методов.
  • Обязательно рядом с пометками TODO, должен находиться подробный комментарий, с целью описания необходимости реализации пункта TODO.
  • Весьма желательно, чтобы внутренние комментарии (если они необходимы), были оформлены в одном стиле. Спасибо Росту Flash-Ripper'у за этот пункт. Например, так (перед блоком, к которому относится комментарий):
    [geshi lang=Java]
    // сделать раз
    doOne();
    // сделать два
    doTwo();[/geshi]
    или, наоборот, так (после строки кода, к которой относится комментарий):
    [geshi lang=Java]
    doOne(); // сделать раз
    doTwo(); // сделать два
    [/geshi]
Полезное