[Из песочницы] Длинные имена слишком длинные
Привет, Хабр! Представляю вашему вниманию перевод статьи «Long Names Are Long» автора Bob Nystrom.
Одно из умных вещей которые делает Google это строгие code review. Каждое изменение, прежде чем вам разрешат его внести в основную ветку, рассматривается как минимум в двух направлениях. Во-первых, кто-то в команде делает обычную проверку, чтобы убедиться, что код выполняет то, что должен.
Но затем происходит вторая ступень, когда проверяется читабельность кода. Это гарантирует, что другие разработчики смогут поддерживать этот код в будущем. Легко ли понять и поддерживать данный код? Соответствует ли этот код стилю и идиомам языка? Хорошо ли задокументирован код?
Использование языка Dart в Google постепенно набирает обороты, и я много занимался подобными code review. Для разработчика языка это очень увлекательный процесс. Я получаю из первых рук представление о том, как люди используют Dart, что действительно полезно для его развития. У меня есть более четкое представление о том, какие ошибки являются общими, и какие функции интенсивно используются. Я чувствую себя как этнограф, ведущий дневник о жизни туземцев.
Но, в любом случае, дело не в этом. Черт бы его побрал, дело даже не в Darts. То, о чем я хожу поговорить, — это то, что я вижу во многих кодах, и что сводит меня с ума: излишне длинные имена.
Да, имена могут быть слишком короткими. В те времена, когда язык С требовал, чтобы только внешние идентификаторы были уникальными вплоть до первых шести символов; когда автозаполнение ещё не было изобретено; когда каждое нажатие клавиши было как подъем в гору сквозь снега — это было проблемой. Я рад, что теперь мы живем в футуристической утопии, где клавиатурные пердежи, как p
, idxcrpm
и x3
, редки.
Но маятник качнулся слишком далеко в другом направлении. Мы не должны быть Хемингуэем, нам также не нужно быть Теннесси Уильямсом. Излишне длинные имена также вредят ясности кода, в котором они используются. Очень длинные имена переменных затмевают операции, которые вы выполняете над ними. Код становится трудно визуально сканировать. Чтобы уложиться в требования по ширине кода, появляются дополнительные разрывы строк, которые прерывают логический поток кода. Длинные имена методов скрывают их одинаково важные списки аргументов. Длинные переменные раздражают от повторного использования, что приводит к растягиванию цепочек методов или каскадов.
Я видел имена переменных длинной более 60 символов. Вы можете поместить туда хайку или коан (и, вероятно, просветите читателя больше, чем выбранное имя). Но не бойтесь, я здесь чтобы помочь.
Выбор хорошего имени
У любого имени в программировании имеется две цели:
- Имя должно быть ясным: нужно знать к чему оно относится.
- Имя должно быть точным: нужно знать к чему оно не относится.
После того, как имя достигло этих целей, любые дополнительные символы являются мертвым грузом. Вот некоторые рекомендации, которые я использую, когда называю вещи в своем коде:
1. Избегайте слов, которые явным образом заданы в типе переменной или параметра
Если ваш язык имеет статическую систему типов, то пользователи обычно знают тип переменной. Как правило, методы бывают достаточно короткими, поэтому посмотрев даже на локальную переменную, где тип нельзя предположить сразy, или при code review, или в каком-либо месте, куда статический анализ кода не может попасть — редко потребуется чуть больше, чем просмотр нескольких строк выше, чтобы определить тип переменной.
Учитывая это, излишне указывать тип в имени переменной. Мы только отказались от Венгерской нотации. Отпусти и забудь:
// Плохо:
String nameString;
DockableModelessWindow dockableModelessWindow;
// Хорошо:
String name;
DockableModelessWindow window;
В частности для коллекций, почти всегда лучше использовать существительное во множественном числе, описывающее содержание, чем существительное в единственном числе, описывающее коллекцию. Если читателя больше заботит то, что находиться в коллекции, то название должно отражать это.
// Плохо:
List holidayDateList;
Map employeeRoleHashMap;
// Хорошо:
List holidays;
Map employeeRoles;
Это также относится к именам методов. Имя метода не нуждается в описании его параметром или их типов — список параметров сделает это за вас.
// Плохо:
mergeTableCells(List cells)
sortEventsUsingComparator(List events,
Comparator comparator)
// Хорошо:
merge(List cells)
sort(List events, Comparator comparator)
Это приводит к тому, что вызовы читаются лучше, чем это:
mergeTableCells(tableCells);
sortEventsUsingComparator(events, comparator);
Это только я, или здесь есть эхо-эхо?
2. Избегайте слов, которые не убирают неоднозначность в имени
Некоторые люди склонны впихивать все, что они знают о чем-либо, в имя переменной. Помните, что имя является идентификатором: оно указывает на то место, где оно определено. Это не исчерпывающий каталог всего, что читатель может захотеть узнать об объекте. Определение сделает это лучше. Имя только направит его туда.
Когда я вижу имя как recentlyUpdatedAnnualSalesBid
, я спрашиваю себя:
- Есть ли обновленные годовые заявки по продаже, которые не являются последними?
- Есть ли недавние годовые заявки по продаже, которые не были обновлены?
- Есть ли недавно обновленные годовые заявки, не связанные с продажами?
- Есть ли недавно обновленные данные по годовым продажам, которые не являются заявками?
Если ответ «нет» на хоть один из этих вопросов, то это, как правило, указывает на лишнее слово в имени.
// Плохо:
finalBattleMostDangerousBossMonster;
weaklingFirstEncounterMonster;
// Хорошо:
boss;
firstMonster;
Конечно, вы вполне можете зайти слишком далеко. Сокращение первого примера до bid
может быть слишком расплывчатым. Но, если вы сомневаетесь, оставьте его таким. Вы всегда можете добавить дополнительную классификацию позже, если имя окажется причиной коллизии или будет неточным. Однако вряд ли вы вернетесь позже, чтобы обрезать весь этот лишний жир.
3. Избегайте слов, которые понятны из контекста
Я могу использовать слово «Я» в этом параграфе, потому что вы знаете, что этот текст от Bob Nystrom. Мне не нужно постоянно повторять здесь «Bob Nystrom» (несмотря на соблазн Bob Nystrom усилить таким образом Bob Nystromа). Код работает абсолютно так же. Метод или поле встречается в контексте класса. Переменная встречается в контексте метода. Примите этот контекст как должное и не повторяйте его.
// Плохо:
class AnnualHolidaySale {
int _annualSaleRebate;
void promoteHolidaySale() { ... }
}
// Хорошо:
class AnnualHolidaySale {
int _rebate;
void promote() { ... }
}
На практике же это означает, что чем глубже вложено имя, тем больше окружающего контекста оно имеет. В итоге получается, что это имя будет более коротким. В результате можно заметить закономерность: идентификаторы, которые находятся в более узкой области, имеют более короткие имена.
4. Избегайте слов, которые ничего не значат
Я часто вижу эту ошибку в игровой индустрии. Некоторые разработчики поддаются искушению и раздувают имена своих переменных, добавляя слова из «серьезного бизнесе». Они считают, что это делает их код более важным и, соответственно, делает их более важными.
В большинстве случаев данные слова не несут никакой значимой информации для разработчика. Обычно подозрения падают на такие слова, как: data
, state
, amount
, value
, manager
, engine
, object
, entity
и instance
.
Хорошее имя рисует некоторую картину в сознании читателя. Называя что-либо «менеджером», мы не передаем читателю никакой информации о том, что этот объект должен делать. Делает ли он расчет оценки производительности? Назначает повышение своим работникам?
Задайте себе вопрос: «Будет ли это имя означать то же самое, если я уберу это слово?». Если да, то слово не имеет значения — выгоняйте с острова.
Применение руководства к… вафлям
Чтобы дать вам представление о том, как эти правила работают на практике, вот пример, который нарушает все из них. Этот надуманный пример очень похож на реальный код, который довольно часто попадается мне на code review.
class DeliciousBelgianWaffleObject {
void garnishDeliciousBelgianWaffleWithStrawberryList(
List strawberryList) { ... }
}
Благодаря типу параметра мы знаем, что метод принимает список клубники (#1). Давайте вырежем эту инфмормацию из имени:
class DeliciousBelgianWaffleObject {
void garnishDeliciousBelgianWaffle(
List strawberries) { ... }
}
Если в программе нет невкусных бельгийских вафель или вафель каких либо других национальностей, то мы спокойно можем отбросить все прилагательные (#2):
class WaffleObject {
void garnishWaffle(List strawberries) { ... }
}
Этот метод находиться внутри WaffleObject
, поэтому из контекста мы знает, что конкретно он собирается украсить (#3):
class WaffleObject {
void garnish(List strawberries) { ... }
}
Очевидно что это объект. Всё является объектами в объектно-ориентированном программировании (#4):
class Waffle {
void garnish(List strawberries) { ... }
}
Теперь намного лучше.
Я думаю что это довольно простые рекомендации. Вы вправе думать, что это бесполезно беспокоиться о таких мелочах. Но я считаю, что присвоение имен — это одна из самых фундаментальных задач, которые мы выполняем при программировании. Имена — это структура, которую мы накладываем не бесформенное море битов, которым является компьютер.