— Им помешать — задача главная. Зрелый только джедай, чей союзник Сила, сразится с Вейдером и Императором. Обучение прервешь — путь выберешь быстрый и легкий, как Вейдер, слугой зла станешь ты. (Йода, Эпизод V. Империя наносит ответный удар)
Здравствуйте! Продолжаем цикл моих статей. В первой частимы уже обсудили основы Java. Перед тем, как начать говорить о computer science, EE, вопросы мотивации и много всего, мне бы хотелось остановиться на куда более приземленной задаче.
Содержание
Intro
Теория
Практика
Рефакторинг
Паттерны
Best Practices
Clean Code Tools
Заключение
Intro
Я всегда восхищался профессионалами. Вы знаете, они как алмазы: их редко встретишь, но ни с кем не спутаешь. Они выделяются среди серой массы, с ними легко говорить и они по натуре очень открытые люди. Они невероятно легко делятся своим тех. опытом, не пытаются язвить и подкалывать и конечно не бывают навязчивыми. Ты твердо знаешь, что если придешь к нему со своей таской/багой, которую не мог решить уже который день, он обязательно поможет.
Работая джуном на первом проекте, я впервые повстречал такого человека. К счастью, ему не требовалось для этого делать большие усилия, он просто был самим собой. И, вы знаете, он мог быть обычным тихим задротом, который закрылся от всех в наушниках и капюшоне, но все равно остался бы тем самым. Вы спросите меня, как?
Я отвечу: за него говорил его код. В это было сложно поверить, но я мог с легкостью определить, где писал он, а где — другие: чистота, простота, универсальность, и, что самое главное, невероятная читабельность. Я хотел, чтобы он писал везде, я хотел просто наблюдать за ходом его мысли, просто стоя сзади за спиной и впитывать-впитывать всё, как губка. Мне было сложно понять: как это ему удается? Конечно же, в тот миг я понял, что то, что он делает — это искусство в каком то смысле. Но начинать обучаться этому можно уже сейчас!
Я провожу время от времени курсы для своих коллег и всегда, как бы мой студент не усвоил материал, присылая свое домашнее задание, в 90% случаев я наблюдаю одну и туже картину: низкое качество кода на первых уроках. Да, его домашка работает, и, может быть, он упорно старался и лучше всех усвоил материал, но читать это не хотелось ни при каких условиях — хотелось просто скомпилировать, проверить результат и забыть. Было искренне жаль: ну как так, ну светлая голова, а пишет так безобразно. Внезапно ко мне пришло небольшое озарение: а ведь этому никто не учит.
Ни в одной главе никакой книги, которую я порекомендовал вам в прошлой части, нет информации о том, как научиться писать красиво. Ну серьезно, где этому обучают перед перед тем, как вы станете работать? В примерах из тех книг? В университете за лекциями по Pascal? Признайтесь, вы слышали конкретные советы от своих друзей-программистов и будущих наставников, что перед тем, как поступить на работу, нужно писать аккуратно и красиво? Если да, я вас официально поздравляю и с нескрываемой завистью жму вашу руку.
Теория
А пока ваша IDE автоматически форматирует код за вас, есть вещи куда поважнее. К счастью (особенно для тех, кто ленится учить английский), вам достаточно ознакомиться всего с двумя неплохо переведенными на русский язык книгами.
«Чистый код» Роберта Мартина является его лучшей книгой на данный момент. В нем собрано очень много размышлений по поводу того, как код должен выглядеть в финальной стадии. С таким филигранным подходом к, казалось бы на первый взгляд, второстепенной задаче я бы сам занервничал, если бы Роберт был в списке ревьюеров моей работы. Небольшая книга, и читается она на одном дыхании. Я бы рекомендовал купить ее и положить на полку, время от времени возвращаться к ней каким-то вечером, когда вам скучно.
Кстати, два года назад вышла книга-продолжение Clean Coder. Если кто имел честь прочитать ее, будьте добры, напишите отзыв в комментариях.
Куда более фундаментальная работа — это «Совершенный Код» Стива Макконнелла.
У автора немного сухой, «стандартизированный» слог. В целом, на мой взгляд, «Совершенный код» выглядит больше как университетский справочник, но, тем не менее, для меня это вторая книга после чистого кода. Здесь я бы даже посоветовал вам сохранить порядок прочтения.
На мгновенье остановимся и отложим рекомендации (на мгновенье, не больше). Знаю, хочется зрелищ, а не просто тупых ссылок с картинками, поэтому давайте с вами сядем за стол и попробуем вместе попрактиковаться.
Практика
Это 15 строк говн...кхм.. кода низкого качества. Если у вас есть такое в проекте, тем более на работе, я вас умоляю, созовите всех ваших коллег и читайте дальше эту статью громко вслух. Это unacceptable, за такое нужно бить по рукам, даже если оно работает. Если постараться присмотреться, то эти 15 строчек кода претендуют на то, чтобы 15 раз атомарно его улучшить. Перед тем как я буду разбирать его шаг за шагом, возьмите листочек и бумажку и попробуйте сами отыскать возможные места. Поехали?
1. Невнятное название package
Что еще за proj.service? Учитывая, что Sun/Oracle рекомендуетназывать package по достаточно прагматичному шаблону, а именно, domain.company.product.module, cам proj.service никак не вписывается в эти рамки. Представим, что компанию зовут KLM, а проект — Boxer. Но этого тоже мало: необходимо четко отнести будущий контракт сервиса к конкретному layer’у проекта. Я вижу, что сервис имеет отношение к базе данных, поэтому думаю, что ему самое место в модуле persistence. Как итог у нас получилось com.klm.boxer.persistence:
2. Неиспользованный импорт
Обратите внимание, CharArrayReader не используется нигде в проекте, но тем не менее он присутствует. Обычно IDE после форматирования удаляет (или предлагает удалить) неиспользованные импорты, но все равно нужно понимать, что лишнему коду в классе не место, да еще, если он никому не нужен. Прощай, CharArrayReader!
NB! Ваша IDE при отпимизации импортов может заменить много классов на обычный wildcard (package.*). Шепну вам на ухо, что на палубе уже ходят сплетни о том, насколько действительно это «удобно». Здесь нет внятного правильно решения: для кого-то wildcard является аккуратным, а для кого то import секция перестает быть информативной. Подумайте немного об этом.
3. Автоматическая генерация Javadoc template вашей IDE
Нет, конечно же, вы спешили, да и кто вообще на это смотрит, верно? Нет, не верно, сударь! Этот комментарий не имеет никакого информационного посыла, а ivanivanovполучит на ежедневном standup’е за такие выходки! Вы же, когда берете бензопилу и распиливаете пол-капота своей машины, не кричите, что бензопила сама сказала это сделать? Соответственно, вы несете полную ответственность за те инструменты, которыми пользуетесь. Поэтому, устанавливая тулзы, убедитесь что они не стараются «улучшить» ваш код.
4. Применение различных типов комментирования в неправильных местах
Согласен, это, может, не самый идеальный пример, но такое встречается. Убедитесь, что вы правильно используете каждый тип комментирования: не разбрасывайтесь ими и не вставляете, где не надо. Исправим.
5. Использование Native языка в javadoc
Это довольно скользкая дорожка. Знакомый из Берлина рассказал мне, что многие проекты имеют javadoc’и на немецком. Я же отношусь к этому скептично и считаю, что английская документация подходит для любого проекта, будь то Украина или Пакистан.
Бенефиты от english javadoc куда больше, чем вам кажется: код станет максимально открытым для широкой аудитории, многие специалисты, привлеченные извне, могут вам помочь, если потребуется, проект будет выглядеть более унифицированным и т.д.. Но даже если вы после этих слов не поменяете свое мнение, обратите внимание,что в этом примере в конце javadoc используются английские слова, поэтому не усложняйте другим жизнь, переведите всё на один конкретный язык. В этом случае — русский либо английский. Позвольте лично мне выбрать второе.
6. Содержания javadoc не соответствует действительности
Бытует мнение, что код сам по себе должен быть понятным и без документаций. Во первых, скажите об этом на кухне Scala разработчикам. Во вторых, если это и правда, то будет ею только до тех пор, пока оригинальные dev’ы будут в штате. Я не хочу догадываться о том как вы именно открываете user сессию с помощью openSessionWithDefaultImpl(), я хочу просто зайти в ваш класс и прочитать на нормальном языке, какие есть нюансы при работе с написанным вами функционалом.
Согласно документации примера, наш сервис не только возвращает какие-то результаты, но еще и может чистить базу. Ничего себе, какой умный!? Тем не менее, я вижу всего один рабочий метод getResultList(), и мне абсолютно невдомек: как же ты умеешь его чистить, и будет ли вообще это зависеть от тебя, myservice?
Я пообщался с нашим Иваном Ивановым, и он мне сказал, что перед тем как в хлам напиться на летнем корпоративе и отрезать свою длинную косичку на глазах у скрам мастера, он точно возвращал список аккаунтов наших юзеров и никакую базу сервис не чистит с ноября прошлого года. Вежливо поблагодарив нашего сотрудника, я срочно обновил javadoc на другой вариант (хотя это конкретно Иванов должен был делать после моего «подзатыльника» asap).
Иванов может собой гордиться: с тегом @author он — рок-звезда, самый популярный в селе. Куда более важное изменение — это то, что теперь понятно, что конкретно возвращает сервис и при каких условиях. Обратите внимание: я намекнул будущим реализациям, что сервис не должен хранить список после очистки сессии. Такие слова как SHOULD, MAY или MUST могут играть колоссальную смысловую нагрузку, так как дают шанс освобождать от ответственности что-либо делать нисходящим реализациям.
Я бы наверное расписал интерфейс куда побогаче, так как именно они должны быть очень тщательно задокументированы, но прошу прощения за такую халатность, не хочу загромождать статью. Напомню, Иванов сказал, что сервис не чистит базу, поэтому я посчитал нужным поставить @since как намек на то, что сервис уже новый, а также указал с помощью @see конкретную модель UserAccount, на которую этот сервис опирается. Кстати, свое вдохновение я черпаю из исходного кода самого JDK (не поленитесь прочитать прошлую часть, если не читали)
7. Неинформативность названия интерфейса
Ага, myservice написан не в стиле camelCase, но ведь переименование на MyService не решит ситуацию, нужно назвать его максимально корректно, и не только потому что он что-то там делает интересное, но еще и ориентироваться на то, в каком package пространстве он находится. Кручу палец у виска и удостоверяюсь, что myservice находится в модуле persistence. Назову его UserTransactionService, так как UserAccountTransactionService слишком большое, а TransactionService не информативное. Нужно подчеркнуть, что классы, реализуя данный интерфейс, обязуются проводить транзакцию, связанную с user’ом, поэтому, слегка не попадая в яблочко, я останавливаюсь на этом варианте. Переименую класс и попутно javadoc.
8. Неинформативность вспомогательных интерфейсов
Интрефейс Workers мало того что не Workable, как по фен-шую, так еще и в множественном числе (я встречал такие в либах, но это не значит, что если какой-то John в apache так сделал, то это можно делать). Постойте-ка! Иванов кричит, что без интерфейса-маркера Workers сервис не сможет сделать то, что ему нужно. Если нашему UserTransactionService нужен он, так давайте его назовем хотя бы AccountAccess, чтобы было ясно — сервис имеет право стучаться за UserAccount, потому что он имеет непосредственный доступ от AccountAccess.
9. Забытый код
Как мы видим, кто-то оставил openconnection(). Я бы не акцентировал на этом внимание и просто сказал бы вам, что не нужно оставлять подобные вещи на очень долгое время. Естественно, если подобное задержится в течение одного-двух спринтов — не страшно, но этого уже достаточно, чтобы усомниться в адекватности разработчика. Этот код закомментирован, выключен, а значит, если это не документация и оно не менялось с ноября, то просто-напросто мусор. В нашем конкретном случае — убираем.
10. Custom форматирование
Добавляйте/убавляйте отсупы и пробелы, чтобы максимально сделать вашу работу читаемой. Конкретно в данном случае я могу предложить:
— Убрать лишную строчку в строке
— Добавить пробел после AccountAccess
— Добавить лишную пустую строчку в 13.
— Отделить константу от метода
— Убрать лишние пустые строчки в
Вот что у нас из этого вышло.
Это было настолько элементарно, но согласитесь, у каждого в душе запищал от радости перфекционист. Старайтесь привнести такую эстетику в каждый ваш класс, метод и не бойтесь вообще выходить за пределы Java. Помните, границы только у вас в голове.
11. Константы
Я считаю, что у public int i здесь три проблемы. Первая — это то, что она не с большой буквы, как надо. Второе, i не имеет смысловой нагрузки: это не USER_INDEX и не ACCOUNT_ID. Ну, а самое главное, мне кажется, константе не место вообще в интерфейсе. Я отдаю отчет в том, что это может вызвать бурление сферического коня в комментариях, но думаю, что многие со мной согласятся: ООП Design более изысканней смотрится, если использовать Enum классы или, на крайняк, private class только с константами. Поверьте, это выглядит куда более приятно и понятно. Вы изолируете контракт от вбитых в камень констант, которых можно, по большому счету, оставить в реализации.
Повторюсь, бывают много ситуаций, когда константы нужны в этом случае. Это лишь моя оценка касательно этого примера судя по моему опыту в проектах. Но если вы не согласны со мной, может, стоит прислушаться к Joshua Bloch, который в книге Effective Java намекает, что, например, интерфейс из констант — это вообще антипаттерн.
Убираю.
12. Ненужные элементы
Зачем писать public abstract если по default любой метод в интерфейсе такой. Компилятор как бы намекает, мол — да пиши, мне что, жалко? Но а смысл? В том-то и дело, что никакого смысла, я только начинаю подозревать, что человек слабо разбирается в таких поверхностных вещах. Мелочь, а неприятно.
13. Несовершенная архитектура
Можно поверить в автора класса и представить что он возвращает list UserAccount’ов как сериализированый XML/JSON в String’e, что в принципе тоже поддается вопросу, но что, если нет? Уже с высоты птичьего полета видно, что можно использовать List<UserAccount> вместо String, да и название метода какое-то абстрактное (только если не специально так задумано). Давайте представим, что действительно лучше поменять возвращаемый тип и название метода. Обратите внимание, я не трогаю сигнатуру метода — скорее всего, она бы тоже была бы переделана, так как имеем дело с транзакциями.
14. Расширенная документация
Нам нужна информация о том, что делает метод getUserAccounts()и что он конкретно возвращает. И здесь наступает один из самых главных моментов:мы с вами допустили грубейшую ошибку (намеренно) — после разговора с Ивановым мы описали класс, который что-то возвращает. Внимание, КЛАСС, который что то возвращает — не метод, а класс, Карл! Класс может только описать приблизительный сценарий его использования, поэтому то, что сейчас находится в шапке, должно плавно переместиться в javadoc метода, а сам сервис должен быть описан со стороны общей функциональности. Давайте попробуем это сделать.
Слышите этот звук? Это плачет QA Team, которые по долгу службы должны создавать автотесты. Раньше они использовали только догадки. Но теперь у них есть замечательная документация, а у нас — переделанный во благо интерфейс.
Вот мы и с вами и попытались повысить качество кода до более вменяемого варианта. Конечно, это всего лишь пример, и не так-то просто в проекте убрать/переделать многое. Свою главную миссию я выполнил: вам нужно ответственно относиться к тому, что вы пишите каждый день. От этого многое зависит. Надеюсь, вы возьмете на заметку большинство возможных подходов, описанных выше.
На этом маленьком уроке остановимся и перейдем обратно в теорию.
Рефакторинг
Тема рефакторинга сидит прочно в голове у каждого программиста. Бытует мнение, что рефакторинг — это занятие по времени близкое к бесконечности. Я не раз замечал, что как только ты начинаешь оптимизировать код, процесс уходит до глубокой ночи. Но тем не менее, такие навыки нужны абсолютно любому уважающему себя разработчику.
Золотая и во многом единственная книга в этом роде — всеми любимого Мартина Фаулера (считаю его аккаунт в твиттере очень интересным). Вашему вниманию «Рефакторинг. Улучшение существующего кода.», не нуждающегося в никакой презентации.
Также могу порекомендовать курс рефакторинга на SourceMaking — специальный сервис, который призван улучшить понимание как известных паттернов, так и антипаттернов. Курс «Рефакторинг» не единственный.
Паттерны
Раз уже говорим начали говорить про шаблоны проектирования, можно выделить две книги. Первая для тех, кто только начинает свой путь в изучении паттернов.
«Паттерны проектирования» Эрика Фримена. Обратите внимание, что эта книга не покрывает все известные шаблоны проектирования. Останавливаясь только на самых популярных, автор выделяет внимание остальным только на последних страницах.
В конце концов, полное руководство по шаблонам — это книга «Банды Четырех». «Приемы объектно-ориентированного проектирования» от Гаммы, Джонсона и так далее :)
Вообще есть 3 известных стадии применения шаблонов проектирования в жизни каждого dev’а:
1. Я ничего не знаю о шаблонах проектирования или просто их не применяю.
2. Я прочитал книгу и пихаю их везде, где только можно.
3. Применяю шаблоны проектирования, где нужно, четко и аккуратно.
Где здесь вы?
Можно еще выделить книгу от упомянутой уже O’Reilly «Object Oriented Analysis and Design» by David West
На примере плохого ООП дизайна онлайн-магазина по продажам гитар автор постепенно улучшает проект, применяя рефакторинг, заменяя плохие решения на более подходящие.
Best Practices
Конечно, вам нужно не только знать про рефакторинг, но еще и перенимать опыт best practices у профессионалов мирового уровня. Какая книга в мире Java самая популярная? Чьи советы являются самыми важными в мире?
«Effective Java» by Joshua Bloch
Ее нужно прочитать каждому. Всё.
Идем дальше. «Well-Grounded Java Developer» by Benjamin Evans. Она же естьи в русском варианте. Автор пробегается по JDK и подымает важные темы, чтобы «прокачать» разработчика до нового уровня.
Честно сказать, книг, подобных «Well-Grounded Java Developer», много, а еще куда больше блогов и туториалов от ребят, которые не стесняются делиться опытом. Я остановился на самых главных.
Clean Code Tools
Я не буду сканировать тупо все тулзы, рассмотрю только две, дабы незасорять статью.
Checkstyle.Это слишком простая вещь, чтобы объяснять вам ее подробно. Скажу лишь, что checkstyle помогает вам подгонять ваш код под общие стандарты. Если вы думаете, что вашу legacy систему очень дорого преобразовать в старую, но уже ухоженную, то скажу, что вы очень заблуждаетесь: вы можете точечно выключать checkstyle, где вам этого захочется. Также для Ant/Maven/Gradle можно использовать Checkstyle Plugin , где можно отключать целые модули, и более того, практически заставить других разработчиков писать грамотно. Новый код без использования техник, описанных в checkstyle, билдиться не будет, к тому же, вы можете прикрутить его в profile и опрокидывать Jenkins/TeamCity если какой то разработчик «забудет» писать красиво. Еще мне очень нравится Checkstyle Plugin в Intellij Idea.
Sonar.Одним из самых главных решений для инспектирования кода является Sonar от SonarQube. Мне кажется, подобные системы должны стоять в каждом проекте.
Помимо детальной статистики по каждому компоненту Sonar предлагает решать их в порядке приоритета: критические дефекты решать первыми, а обычные ляпы — в самом конце, и под шумок вас замеряет coverage. Тем более, Sonar легок в установке и стоит отдельно от проекта.
Заключение
Осваивая родной язык, вы же не пытались писать прозу и стихи — вам бы просто научится разговаривать складно. Как говорится, нет предела совершенству. Старайтесь писать элегантный код. Вы будете учиться этому всю свою жизнь — мир стремительно меняется, и вместе с ним меняются и подходы с методологиями. Удачи!
В следующей части я буду погружать вас в Enterprise, будем рассматривать как Java EE, так и всемирные фреймворки, которые стали обязательной галочкой в каждом резюме java разработчика.
Большое спасибо за внимание. Продолжение следует.