NamingConventions, CodeConventions - 1

— Привет, Амиго! Сегодня я расскажу тебе о стилях кода и важности стилей кода.

Начну с самого главного. Код в Java должен быть легко читаем. Общий подход к коду такой – код пишется один раз, а читается сто.

Например, ты и еще 10 программистов занимаетесь написанием программы. Программа пишется три года, с промежуточными релизами каждые три месяца.

— Так долго?

— Это Java, детка! Как насчет Enterprise-системы, которая работает на десятке серверов и которую пишут более 6 лет около 100 человек? Бывает и такое.

— Ничего себе.

— Так вот, главный критерий, главное требование к коду – он должен легко читаться другими разработчиками.

В других языках программирования люди часто работают маленькими командами над маленькими задачами, поэтому у них может быть другой главный критерий, например – «Это работает? Вот и отлично».

За пару лет в написанный тобой код успеют по нескольку раз внести изменения все члены команды. И каждый раз им придется разбираться, а как же работает этот код?

И если код просто отлично делал свое дело, но был непонятен, то поменять его трудно. Его выкинут и напишут по-своему. Поэтому пиши код, понятный другим. Если можешь улучшить код – улучши его. Если его можно улучшить, значит, его нужно улучшить!

Если ты написал код за 15 минут и два часа улучшаешь его – ты все делаешь правильно. Сколько времени ты экономишь команде?

«2 часа на разбор твоего кода» x «100 раз, когда люди будут в нем разбираться» = 200 часов.

Эти цифры взяты с потолка, но я хочу, чтобы ты понял проблему и ее масштаб. Твой код создается для чтения другими программистами. Все остальное – вторично.

Код плохо работает? Исправим. Не оптимально? Оптимизируем. Не документирован? Прокомментируем.

Код плохо читаем? Выкинуть его нафиг и написать все заново!

— Не думал, что это такая проблема.

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

Теперь перейдем к вопросу номер два – «А как писать максимально читаемый код?».

Любому понятно, когда с ним говорят на родном языке, родными знакомыми словами. Так и тут. Код легко читаем, когда программист, читающий его, с легкостью догадывается:

А) Что делает каждый метод;

Б) Какой цели служит каждый класс;

В) Что именно хранится в каждой переменной.

Все это определяется по названию: имени класса, имени метода, имени переменной. Кроме того, есть еще стиль именования переменных. И стиль кода.

— Я готов слушать.

— В основе программирования лежит… английский язык! Хорошо написанная программа читается, как обычная техническая документация на английском языке.

Начнем с имен.

Имя метода должно кратко описывать, что этот метод делает. Тогда программист может читать программу, как простой текст.

Программа
public String downloadPhoto(String url)
{
 String resultFileName = TempHelper.createTempFileName();

 Downloader downloader = new SingleFileDownloader(new Url(url));
 downloader.setResultFileName(resultFileName) 
 downloader.start();
 while(downloader.isDone())
 {
  Thread.sleep(1000);
 }

 if (downloader.hasError())
  return null;

 return resultFileName;
}

Вот как читается такая программа.

Строка 1.

Метод называется «downloadPhoto», похоже, что он загружает файл с фотографией из интернета. Куда загружает? Пока неизвестно. Откуда? В параметрах метода передан url – скорее всего это и есть ссылка для загрузки.

Строка 3.

Объявляется переменная resultFileName, в нее ложится значение, отданное методом TempHelper.createTempFileName();

Имя переменной resultFileName дословно переводится как «имя-файла-результата». Значит это локальный путь к файлу на диске, куда мы будем сохранять наш скачиваемый файл.

Название TempHelper – ничего не говорит. Суффикс Helper говорит, что это разновидность утилитных классов, которые не содержат в себе серьезной бизнес логики, а используются для упрощения часто возникающих рутинных задач.

Имя метода createTempFileName говорит о том, что этот метод создает и возвращает нам имя временного файла (temp-file). Temp file – это временный файл, который создается на время, и обычно удалятся после закрытия программы или раньше.

Строка 5.

Создается объект типа SingleFileDownloader и кладется в переменную downloader.

Название переменной downloader переводится как загрузчик. Этот объект и будет загружать наш файл из интернета.

В переменную downloader кладется объект типа SingleFileDownloader. Из названия можно предположить, что в программе есть несколько видов классов-загрузчиков файлов. Один из них написан для загрузки единственного файла (single file), и скорее всего можно ожидать встретить в коде и загрузчики для группы файлов с названиями вроде: MultiFileDownloader, GroupFileDownloader или DirectoryDownloader

Строка 6.

Объекту downloader устанавливаем в свойство resultFileName значение переменной resultFileName. Т.е. мы говорим загрузчику, куда сохранять скачиваемый им файл. Что и следовало ожидать. Т.е. мы уже практически предсказываем код!

Строка 7.

Вызов метода start. Начало загрузки. Логично. Интересно, как будет происходить загрузка: кусочками, в отдельной нити или сразу до конца. Если сразу, то это может быть долго и иметь последствия.

Строки 8-11.

Ага. Тут мы видим обычный цикл, который ждет завершения загрузки. Объект downloder имеет свойство done («готово») и метод, который его возвращает isDone(). Т.к. метод называется isDone(), а не getDone(), то переменная done имеет тип boolean, ну или Boolean.

Строки 13-14.

Если в процессе скачивания произошла ошибка, то метод downloadPhoto возвращает null. Хорошо, что он обрабатывает состояние ошибки. Плохо, что он просто возвращает null – что за ошибка – не ясно. Лучше бы кинул исключение с информацией об ошибке.

Строка 16.

Возвращаем путь к локальному файлу, который содержит скачанный файл.

— Ничего себе!

— Из текста этой программы абсолютно ясно, что она делает. Можно даже делать предположения, как устроена программа и какие еще классы/методы мы будем встречать.

— Теперь я понял, как важны имена.

— Еще насчет имен. Очень часто можно угадать, какие у объекта/класса есть методы. Вот например, если объект – коллекция, то скорее всего у него будут методы size() или count(), чтобы получить количество его элементов. Так же, вероятно, методы add(добавить) или put(вставить). Методы get/getItem, getElement используются для получения элементов у классов-коллекций.

Если переменная называется i, j, k – скорее всего, это счетчик цикла.

Если переменная называется m, n – скорее всего, это размер массива/коллекции.

Если переменная называется name то, скорее всего, она имеет тип String и содержит чье-то имя.

Если класс называется FileInputStream, то он одновременно является файлом (так и есть) и потоком для чтения – InputStream (так и есть).

Чем больше ты видел кода, тем легче читать чужой код.

Но иногда есть код, который читать очень сложно. На этот случай есть один очень дельный совет:

Совет
Пишите свой код так, как будто поддерживать его будет склонный к насилию психопат, который знает, где вы живете.

— Смешно и не смешно одновременно.

— Теперь немного расскажу о стилях именования переменных.

В Java переменным и методам стараются давать максимально информативные имена. Поэтому такие названия часто состоят из нескольких слов. Есть 4 стиля написания составных имен.

1) Lowercase (нижний регистр) – все слова с маленькой буквы. Пример:

Green house превращается в greenhouse

Hollywood girl превращается в hollywoodgirl

В таком стиле пишутся названия пакетов (package).

2) Uppercase (верхний регистр) – все слова пишутся с большой буквы, разделенные знаком подчеркивания. Примеры:

Max value превращается в MAX_VALUE

Cats count превращается в CATS_COUNT

В таком стиле пишутся названия переменных-констант (final static).

3) CamelCase (верблюжий стиль) – все слова пишутся с маленькими буквами, первая буква каждого слова – большая. Примеры:

Green house превращается в  GreenHouse

Hollywood girl превращается в HollywoodGirl

В таком стиле пишутся названия классов и интерфейсов.

4) Lower CamelCase (смешанный стиль) – все слова пишутся маленькими буквами, первая буква каждого слова – большая, первая буква первого слова — маленькая. Примеры:

Get width превращается в  getWidth

Get Hollywood girl name превращается в getHollywoodGirlName

В таком стиле пишутся названий переменных и методов.

Т.е. правил не так уж и много.

1) Всё пишется в Lower Camel Case.

2) Имена классов и интерфейсов – всегда с большой буквы.

3) Имена пакетов – всегда маленькими.

4) Константы — всегда большими.

Есть еще пара нюансов, но в целом так и есть.

Теперь насчет методов. Имена методов практически всегда начинаются с глагола! Имя метод count – плохое имя. Лучше назвать getCount(). Метод выполняет какое-то действие над объектом: startDownload (начать загрузку), interrupt (прервать), sleep (спать), loadPirateMusic(загрузить пиратскую музыку).

Для работы со свойствами/полями объекта, как ты уже знаешь, есть getter’ы и setter’ы. getName/setNamegetCount/setCount и т.д.

Единственное исключение делается для типа boolean. Для него getter пишется не через get, а через is: isDone, isEmpty. Так ближе к английскому языку.

— Т.е. знание английского обязательно для умения программировать?

— Не обязательно, но зная английский и имея пару лет опыта за плечами, ты сможешь очень быстро разбираться в чужом коде. Как насчет тратить на работу не 8 часов в день, а два? Заманчивое предложение?

— Ага!

— То-то и оно. Основное требование у Java Junior — это отличное знание основ Java – Java Core. Но чем опытнее ты становишься, тем сильнее тебе нужен английский. Чем раньше начнешь его учить – тем лучше.

— У меня еще вопрос. Почему такие разные методы для получения количества элементов?

Класс Метод для получения количества элементов
String метод length()
Array свойство length
ArrayList метод size()
ThreadGroup метод activeCount()

Во-первых, Java была придумана более 20 лет назад, когда требований вроде setCount/getCount еще не было, и в ходу был подход из языка С++ «сделай это как можно короче»

Во-вторых, тут играет роль семантика английского языка. Так уж получилось, что когда говорят про массив, употребляют length, а когда про коллекцию – size.

В русском языке тоже есть такие устоявшиеся выражения:

1) Тарелка на столе стоит, а на полу – лежит.

2) Ботинок на столе лежит, а на полу – стоит.

— Какая интересная лекция.

— Хотелось бы рассказать больше, но боюсь, что всего ты все равно сразу не запомнишь. Лучше давать снова по чуть-чуть.

Еще хотел бы коснуться стиля скобок {}. Есть два подхода:

1) Скобка каждый раз ставится на новой строке

2) Открывающая скобка ставится в конце предыдущей строки, закрывающая – на новой. Такой стиль еще называют «египетскими скобками».

Честно говоря – тебе выбирать, как писать. Многие пишут открывающую скобку на той же строке, многие — на новой. Это как спор – с какого конца разбивать яйцо: с тупого или острого.

Единственное, что могу посоветовать – это придерживайся того же стиля, что и в проекте, в котором ты работаешь. Не стоит менять чужой код, как тебе удобнее. Люди несовершенны, это я тебе как доктор Билаабо говорю.

— Спасибо за интересную лекцию, Билаабо. Пойду обдумывать услышанное.