Комментарии в программировании – это специальные заметки или объяснения, которые добавляются к коду программистом, чтобы улучшить читаемость написанного. Комментарии никак не влияют на алгоритм программы – она их игнорирует. Они созданы исключительно для чтения человеком. В Java существуют три основных типа комментариев: однострочные, многострочные и документирующие комментарии (javadoc). Но давайте поговорим не только о том, как их писать, но и о том, почему комментарии могут быть злом, и как их использование связано с качеством кода.
Почему комментарии это зло
Хороший код должен быть понятным без комментариев. Это достигается путем использования понятных имён переменных, методов и разделением кода на логические блоки. Также комментарии могут стать устаревшими, если код меняется, но комментарии не обновляются. Это может привести к путанице. Ну и наконец: слишком много комментариев может усложнить код и затруднить его понимание.
Лучше всего использовать только документирующие комментарии в своём коде. И то – с умом, чтобы они не повторяли очевидное. Да, бывают исключения. Но они редки. Крайне редки. Они случаются гораздо реже, чем среднестатистический программист пишет комментарий, который причисляется ко злу.
Ниже мы с вами рассмотрим, какие есть типы комментариев, и как можно избежать их использования.
Однострочные комментарии
Однострочные комментарии начинаются с двойного слэша // и используются для добавления кратких пояснений к коду. Такой комментарий может занимать только одну строку. Например:
1 |
int age = 25; // инициализируем переменную age значением 25 |
Давайте рассмотрим целую программу, где есть однострочный комментарий, и попробуем убрать его, не уменьшив понятность написанного.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import java.util.Scanner; public class MyProject { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("Введите число -> "); int number = scanner.nextInt(); //считаем факториал числа number for (int i = 2; i <= number; i++) { number *= i; } System.out.println("Получилось: " + number); } } |
Комментарий в этом случае поясняет операцию цикла – зачем она и что она делает. Как можно убрать этот комментарий, чтобы понятность кода сохранилась? Очень легко. Дать куску кода с циклом собственное содержательное имя – вынести его в метод.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
import java.util.Scanner; public class MyProject { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("Введите число -> "); int number = scanner.nextInt(); System.out.println("Получилось: " + calcFactorial(number)); } private static long calcFactorial(int num) { for (int i = 2; i <= num; i++) { num *= i; } return num; } } |
Посмотрите: это же прекрасно. Никакие пояснения не нужны. А если попытаться втиснуть сюда тот комментарий, он будет избыточным – он просто будет переводить на русский язык название метода, которое и так говорит за себя.
Многострочные комментарии
Многострочные комментарии начинаются с /* и заканчиваются на */. Каждая строка блочных комментариев (кроме самой первой) должна по-хорошему начинаться со звёздочки. Они могут содержать несколько строк текста и обычно используются для описания более крупных блоков кода. Пример:
1 2 3 4 5 |
/* * Этот блок кода предназначен для выполнения * сложных математических вычислений. */ int result = calculateComplexValue(); |
Теперь посмотрим на программу, использующую многострочный комментарий. Спойлер: его надо будет оттуда безболезненно вытащить.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
import java.time.LocalDateTime; import java.time.format.TextStyle; import java.util.Locale; import java.util.Scanner; public class MyProject { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.print("Введите код действия -> "); int number = scanner.nextInt(); processMenuCode(number); } private static void processMenuCode(int code) { /* 0 - выход 1 - вывести текущее время 2 - вывести текущую дату */ if (code == 0) { System.out.println("Производится выход..."); } else if (code == 1) { LocalDateTime dateTime = LocalDateTime.now(); System.out.println(dateTime.getHour() + ":" + dateTime.getMinute()); } else if (code == 2) { LocalDateTime dateTime = LocalDateTime.now(); System.out.println(dateTime.getDayOfMonth() + ", " + dateTime.getMonth().getDisplayName(TextStyle.FULL_STANDALONE, Locale.ENGLISH) + ", " + dateTime.getYear()); } else { System.out.println("Неизвестный код"); } } } |
Это некрасиво. Это неэлегантно. Это моветон! Как это можно исправить? Можно вынести значения 0, 1 и 2 в именованные константы.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
private static void processMenuCode(int code) { final int EXIT = 0; final int CURRENT_TIME = 1; final int CURRENT_DATE = 2; if (code == EXIT) { System.out.println("Производится выход..."); } else if (code == CURRENT_TIME) { LocalDateTime dateTime = LocalDateTime.now(); System.out.println(dateTime.getHour() + ":" + dateTime.getMinute()); } else if (code == CURRENT_DATE) { LocalDateTime dateTime = LocalDateTime.now(); System.out.println(dateTime.getDayOfMonth() + ", " + dateTime.getMonth().getDisplayName(TextStyle.FULL_STANDALONE, Locale.ENGLISH) + ", " + dateTime.getYear()); } else { System.out.println("Неизвестный код"); } } |
Видите, как преобразился метод? Но он всё ещё слишком нагромождённый. Методы лучше писать как можно короче. Не то, что меньше одного экрана – методы должны быть гораздо, гораздо меньше. Тогда они будут удобными, легко читаемыми и чистыми. И хотя комментарий мы уже спокойно убрали без потери читаемости кода, душа поэта не позволит оставить этот код таким грязным. Вынесем операции по получению текущей даты и времени в отдельные методы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
private static void processMenuCode(int code) { final int EXIT = 0; final int CURRENT_TIME = 1; final int CURRENT_DATE = 2; if (code == EXIT) { System.out.println("Производится выход..."); } else if (code == CURRENT_TIME) { System.out.println(getCurrentTime()); } else if (code == CURRENT_DATE) { System.out.println(getCurrentDate()); } else { System.out.println("Неизвестный код"); } } private static String getCurrentTime() { LocalDateTime dateTime = LocalDateTime.now(); return dateTime.getHour() + ":" + dateTime.getMinute(); } private static String getCurrentDate() { LocalDateTime dateTime = LocalDateTime.now(); String monthName = dateTime.getMonth().getDisplayName(TextStyle.FULL_STANDALONE, Locale.ENGLISH); return dateTime.getDayOfMonth() + ", " + monthName + ", " + dateTime.getYear(); } |
Красивее? Красивее. Я бы сказала, намного элегантнее. Конечно, можно сделать ещё элегантнее, но я пока не придумала, как. Но знаю: можно.
Javadoc-комментарии
Документирующие комментарии используются для автоматической генерации документации к коду. Это полезно, однако я редко (почти никогда) не генерирую такие документы. Наоборот, я использую документирующие комментарии для того, чтобы пояснить свои намерения относительно класса или метода, если такое пояснение требуется.
Документирующие комментарии в Java называются javadoc. Они начинаются с /** и обычно располагаются перед классами, методами и иногда полями. У javadoc-комментариев также есть специальные “теги“, начинающиеся с @, которые позволяют более явно указать смысл параметров или возвращаемого значения, если это, к примеру, метод. Как это выглядит в коде:
1 2 3 4 5 6 7 8 9 |
/** * Этот метод вычисляет сумму двух чисел. * @param a Первое число. * @param b Второе число. * @return Сумма a и b. */ public int sum(int a, int b) { return a + b; } |
Хотя я писала, что документирующие комментарии – единственное добро среди зла, однако они тоже могут становиться вредными – когда они избыточны. Посмотрите на следующий javadoc-комментарий:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
/** * рассчитать число Фибоначчи по индексу этого числа * * @param index индекс числа в последовательности Фибоначчи * @return рассчитанное число Фибоначчи */ private static int calcFibonacciNumberByIndex(int index) { int prevPrevNumber = 0; int prevNumber = 1; for (int i = 0; i < index; i++) { int newValue = prevPrevNumber + prevNumber; prevPrevNumber = prevNumber; prevNumber = newValue; } return prevNumber; } |
Это какой-то страх. Разве имя метода calcFibonacciNumberByIndex() не объясняет, что будет вестись подсчёт числа Фибоначчи по заданному индексу (порядковому номеру)? Мало того, что это повторяется в javadoc-коментарии сразу, так ещё и в тегах @param и @return. Это не дело. Но ещё больше не дело – это вставлять исторические сводки, описания алгоритмов, целые эссе в javadoc-и:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
/** * Леонардо из Пизы, более известный как Фибоначчи, был итальянским математиком, * жившим в 13 веке. Он приобрел широкую известность благодаря своей книге * "Liber Abaci" (Книга об абаке), где впервые представил западному миру * последовательность чисел, названную впоследствии числами Фибоначчи. * Эта последовательность, где каждое число равно сумме двух предыдущих * (0, 1, 1, 2, 3, 5, и так далее), стала важным объектом изучения * и нашла множество приложений в математике и компьютерных науках. * Фибоначчи нашел эту последовательность, исследуя прирост * численности кроликов в идеальных условиях размножения, * что послужило отправной точкой для его открытия. * * @param index индекс числа в последовательности Фибоначчи * @return рассчитанное число Фибоначчи */ private static int calcFibonacciNumberByIndex(int index) { int prevPrevNumber = 0; int prevNumber = 1; for (int i = 0; i < index; i++) { int newValue = prevPrevNumber + prevNumber; prevPrevNumber = prevNumber; prevNumber = newValue; } return prevNumber; } |
Никогда так не делайте. Помните: краткость – сестра таланта. Чем больше программист тратит времени на чтение бесполезных комментариев, тем меньше он реально занимается разработкой. Не нужно забивать голову кроликами. Подумайте о методах!
Как же стоило поступить в этом конкретном случае? Да убрать javadoc в принципе! Этот метод не требует пояснений. Его название сообщает всё необходимое. Объём метода небольшой. Так зачем его пояснять в javadoc-е?
В заключение, комментарии задумывались как инструмент для улучшения понимания кода, но их следует использовать с умом. Лучше всего создавать чистый и самообъясняющийся код, который не требует избыточных пояснений, а из трёх типов комментариев использовать только один – документирующий (для Java это javadoc). Помните, что хороший код говорит сам за себя, и умение писать такой код – важный навык для каждого программиста.