
— Привет, Амиго! Сегодня я расскажу тебе о стилях кода и важности стилей кода.
Начну с самого главного. Код в 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/setName, getCount/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) Открывающая скобка ставится в конце предыдущей строки, закрывающая – на новой. Такой стиль еще называют «египетскими скобками».
Честно говоря – тебе выбирать, как писать. Многие пишут открывающую скобку на той же строке, многие — на новой. Это как спор – с какого конца разбивать яйцо: с тупого или острого.
Единственное, что могу посоветовать – это придерживайся того же стиля, что и в проекте, в котором ты работаешь. Не стоит менять чужой код, как тебе удобнее. Люди несовершенны, это я тебе как доктор Билаабо говорю.
— Спасибо за интересную лекцию, Билаабо. Пойду обдумывать услышанное.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ