
Присвоение имен объектам считается сложной задачей, поскольку имена, выбираемые для переменных, классов, функций и других элементов кода, должны быть осмысленными, точными и понятными.
Чтобы сохранять кодовую базу чистой и поддерживаемой, необходимо иметь надежные и согласованные шаблоны именования. Однако даже их применение может оказаться проблематичным, поскольку по мере роста системы становится все сложнее обеспечивать бесконфликтность новых имен с уже с существующими.
В этой статье рассмотрим чек-лист из 7 рекомендаций по выбору имен при написании чистого кода. Он основан на личных соображениях автора и идеях авторитетных личностей в мире программирования, таких как Дядя Боб (Uncle Bob) и Эрик Эванс (Eric Evans).
1. Учитывайте назначение имени и единицы измерения
Выбранное имя должно четко передавать назначение и функцию элемента, а также, когда это уместно, соответствующую единицу измерения. Данный подход поможет сделать код более понятным и удобным — как для того, кто его пишет, так и для тех, кому придется читать или изменять его в будущем.
В то же время использование слишком коротких имен переменных или непонятных сокращений может значительно ухудшить читаемость и восприятие кода всеми участниками процесса:
public double calculateBmi(double m, double h) {
double sh = h * h;
double mKg = m * 0.453d;
return mKg / sh;
}
Можно значительно повысить читаемость этого фрагмента кода, просто выбрав более подходящие имена для переменных:
public static final double LBS_TO_KG = 0.453d;
public double calculateBmi(double massInLbs, double heightInMeters) {
double squaredHeight = heightInMeters * heightInMeters;
double massInKg = massInLbs * LBS_TO_KG;
return massInKg / squaredHeight;
}
2. Избегайте шумовых слов
Зайдите на этот забавный мини-сайт, генерирующий имена классов для Java-приложений. Попробуйте пару раз нажать на кнопку «Enterprisify»! Все сгенерированные имена состоят из «шумовых слов» — технических терминов, не выражающих никакого смысла.
Шумовые слова, используемые в именах классов, способны затруднить понимание кода. Примерами шумовых слов могут быть «Data», «Info», «Manager» и т. д. Чем класс с именем «AccountData» отличается от класса «Account»?
AccountData содержит меньше полей?
Тогда, возможно, AccountSummary будет лучшим вариантом.
Или AccountData является частью другого слоя?
Тогда почему не AccountDto или AccountEntity?
Шумовое слово «Data» не добавляет никакой информации. Однако иногда использование технических терминов при именовании классов позволяет указать местоположение или реализацию класса. Например, можно использовать такие суффиксы, как Service, Dto, Entity, Repository и т. д.
3. Избегайте типов данных и ключевых слов
Типы данных также можно считать «шумовыми словами». Их примеры приведены ниже:
record PromotionRecord (
// ...
){}
abstract class AbstarctPromotion {
// ...
}
OffsetDateTime startOffsetDateTime;
List<Account> listOfAccounts;
Кроме того, специальные шаблоны именования интерфейсов и их реализаций можно рассматривать как антишаблоны. Общепринятой практикой является префикс интерфейсов «I» и суффикс «Impl» в имени реализующего класса:
interface ICustomerDetailsProvider {}
class CustomerDetailsProviderImpl implements ICustomerDetailsProvider {}
Что в этом плохого?
Для чего нужен интерфейс? Чаще всего он используется в одном из двух сценариев:
- Инверсия зависимостей: интерфейс является частью доменного слоя, а его реализация действует как адаптер, использующий код из инфраструктурного слоя. Например, интерфейс CustomerDetailsProvider является частью доменного слоя, а его реализация получает конкретные данные о клиенте через сетевой вызов. Если назвать реализацию «CustomerDetailsProviderImpl», то суффикс «Impl» не добавит никакой ценности. Вместо этого имя вроде «CustomerDetailsApiClient» или «CustomerApiAdapter» будет содержать подсказку о том, как происходит получение данных.
- Полиморфизм: если интерфейс используется для полиморфных вызовов, должно быть как минимум две разные реализации с четкими различиями. Например, если есть интерфейс «Payment» («Оплата») и различные реализации для разных методов оплаты. В этом случае, поскольку реализации будут существенно различаться, необходимо дать им имена четко выраженными значениями: «CashPayment» («Оплата наличными»), «VisaPayment» («Оплата через систему Visa»), «OnlinePayment» («Оплата онлайн») и т. д.
4. Учитывайте соотношение длины имени и области видимости
При именовании переменных важно, чтобы длина имени была прямо пропорциональна области видимости переменной. Следовательно, для небольшой области видимости достаточно одного маленького слова или аббревиатуры. Дядя Боб идет еще дальше и говорит, что даже однобуквенного имени может быть достаточно для таких маленьких областей видимости, как однострочные. В качестве примера можно привести лямбда-функции.
// небольшая область видимости
for(Employee employee : employeesEligibleForPromotion) {
// 4-5 строк кода
}
// очень малая область видимости
employeesEligibleForPromotion.stream()
.map(e -> e.getName().toLowerCase())
.forEach(this::sendNotificationToHrDepartment);
Для более длинных областей видимости стоит использовать более длинные имена переменных. Но если имена переменных становятся слишком длинными, это может быть признаком того, что код «с душком»: функции становятся слишком большими.
Например, в приведенном выше фрагменте кода используется довольно длинное имя для списка сотрудников: employeesEligibleForPromotion. Это может означать, что функция имеет большую область видимости или что есть другой список сотрудников в той же области видимости: в любом случае можно извлечь какую-то функциональность:
public void sendNotificationToHrForEmployees(List<Employees> employees) {
emoployees.stream()
.map(e -> e.getName().toLowerCase())
.forEach(this::sendNotificationToHrDepartment);
}
Как видите, извлекая функцию и уменьшая область видимости переменной, можно сократить ее имя.
А что с функцией? Теперь имя функции довольно длинное!
При именовании функций действует правило: чем длиннее область видимости, тем меньше и абстрактнее должно быть имя. Это связано с тем, что функции более высокого уровня не должны передавать никаких деталей реализации. Следовательно, функции с небольшой областью видимости будут иметь более длинное имя.
5. Соблюдайте согласованность шаблонов именования
Согласованные шаблоны именования облегчают разработчикам навигацию по чужому коду и его понимание, что особенно важно в больших совместных проектах. Кроме того, согласованность помогает предотвратить конфликты именований и снизить вероятность внесения в код ошибок.
Избегайте использования разных глаголов для обозначения одного и того же действия: не используйте попеременно fetch, retrieve, get и find, если они реализованы одинаково. В то же время для разных реализаций или концепций следует выбирать разные слова. Например, можно использовать глагол «find» для возврата опциональных типов данных или nullable-данных, в то время как «get» всегда вызывает исключение, если данные отсутствуют.
В информатике есть только две трудные вещи: инвалидация кэша и именование вещей (Фил Карлтон).
6. Используйте технические и деловые термины
Как деловые, так и технические термины могут помочь выбрать подходящее имя. В книге «Чистый код» Дядя Боб говорит о том, что технические термины должны иметь более высокий приоритет, поскольку их легко понять коллегам-разработчикам. Такие термины, как Adapter, Builder, Repository, Dto и им подобные, могут указывать либо на реализацию, либо на местоположение компонента.
С другой стороны, концепция Эрика Эванса о предметно-ориентированном проектировании говорит о том, что использование деловой лексики помогает при работе с большими системами со сложной доменной моделью. Тем самым значительно преодолевается коммуникационный разрыв между разработчиками и бизнесменами.
В целом, как деловые, так и технические термины могут быть хорошими кандидатами при выборе имен для компонентов.
7. Уместно используйте глаголы и существительные
Как правило, глаголы следует использовать для функций, а существительные — для полей и переменных.
Но как быть с именованием классов? А интерфейсов?
Хотя в большинстве случаев при именовании классов используются существительные, возможны и исключительные случаи. Например, при таком подходе к организации кода, как «Vertical Slice» («вертикальный срез»), могут быть классы, представляющие сценарии использования. Они будут иметь такие имена, как CreateAccount, DisableAcount, ApplyPromotion и т. д.
Интерфейсы — это, прежде всего, методы и поведение. При именовании интерфейса следует использовать существительные, но в то же время четко выражать действия, которые они позволяют выполнять. Например: Clonable, Runnable, Executable, DataProvider и т. д.
Заключение
Мы рассмотрели 7-шаговый чек-лист по выбору подходящего названия. В начале обсудили выражение назначения имени, использование в нем единиц измерения и избегание «шумовых слов». Затем узнали о длине имен, их согласованности, типах лексики и частях речи, которые следует использовать при именовании.
Читайте также:
- Как веб-серверы обрабатывают запросы
- Почему typeof null === ‘object’
- Проектирование устойчивых API: постигаем искусство ограничения скорости
Читайте нас в Telegram, VK и Дзен
Перевод статьи Emanuel Trandafir: My 7-Step Cheat Sheet for Choosing Good Names




