open atlas
↑ К треку
Паттерны и качество кода CP · 01 · 01

Именование — это акт проектирования

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

CP Middle ◷ 18 min
Уровень
ОсновыJuniorMiddleSenior

Ты садишься выделить метод. Логика ясна, тесты проходят, осталось только назвать новую функцию — и ты застреваешь. processData? handleStuff? doWork? Каждый кандидат — либо ложь, либо пожимание плечами. Через десять минут у тебя есть имя, в которое ты не веришь, и смутное ощущение, что что-то не так с кодом, а не с тобой.

Что-то действительно не так с кодом. Трудность была не проблемой словарного запаса; это код говорил тебе, что у вещи, которую ты пытаешься назвать, нет единой ясной концепции за спиной. Именование — не последний косметический шаг после того, как дизайн готов. Именование и есть дизайн, проявляющийся в единственном месте, где его можно почувствовать.

Цель

После этого урока ты можешь объяснить, почему имя — это сжатие намерения и почему написать его значит очертить границы концепции; отличать имена, раскрывающие намерение (что/зачем), от имён, протекающих реализацией (как); читать трудность именования как обратную связь о дизайне — сигнал, что вещь делает слишком много; и избегать обратной ошибки — имён настолько переточённых, что они вваривают одноразовую деталь реализации в твой публичный словарь.

1

Имя — это сжатие намерения: оно хранит что и зачем, а не как. Читатель daysSinceLastLogin знает, что значение означает и зачем оно существует, не читая ни единой строки вычисления. Читатель d не знает ничего и вынужден каждый раз восстанавливать смысл из контекста. Имя — это интерфейс к концепции; тело — реализация. Хорошее имя позволяет читателю целиком пропустить тело на тех 95% прочтений, где ему нужен лишь смысл.

// имя несёт смысл; тело — деталь реализации
const d = Math.floor((Date.now() - user.lastLoginAt) / 86_400_000); // ✗ что такое d?
const daysSinceLastLogin = Math.floor((Date.now() - user.lastLoginAt) / DAY_MS); // ✓

Краткая версия не быстрее в работе — она быстрее набирается и медленнее читается, а время уходит именно на чтение. Имя — самая дешёвая документация, которую ты когда-либо напишешь, потому что оно бесплатно путешествует со значением к каждому месту вызова.

2

Именование заставляет понять концепцию и очертить её границы. Чтобы назвать что-то, ты должен ответить на вопрос «что это за одна-единственная вещь?» — а это работа по проектированию, а не наклеивание ярлыка. Акт выбора chargeExpiredSubscriptions вместо process заставляет тебя решить: эта функция только списывает деньги, или ещё шлёт письма, логирует и делает повторы? Если она делает все четыре вещи, честное имя — это абзац, а абзац — это запах дизайна. Именование — самое дешёвое ревью дизайна, какое ты можешь провести, потому что оно идёт у тебя в голове ещё до коммита.

function process(x: Account[]) { /* ...80 строк... */ }         // ✗ имя не раскрывает ничего
function chargeExpiredSubscriptions(accounts: Account[]) { }   // ✓ имя — это контракт

chargeExpiredSubscriptions — это обещание: оно списывает деньги, оно касается просроченных подписок, оно принимает аккаунты. Если тело потом начнёт ещё и продлевать подписки, имя начинает лгать — и эта ложь сигнал, что функция отрастила вторую обязанность.

3

Если ты не можешь назвать что-то чисто, оно, скорее всего, делает слишком много — или ты пока его не понимаешь. Классический признак — класс, который ты в итоге называешь UserManager, DataHelper или OrderService, потому что ничего более конкретного не подходит. Manager и Helper — не концепции; это признания, что коробка хранит всё, что не влезло в другие места. Тип, сопротивляющийся точному имени, сопротивляется ему именно потому, что у него нет единой ответственности — имя не может быть точным, потому что не точна сама вещь.

class UserManager {
  validateEmail(e: string) {}     // валидация
  hashPassword(p: string) {}      // криптография
  sendWelcomeEmail(u: User) {}    // уведомления
  recordSignupMetric(u: User) {}  // аналитика
}

Ты не можешь назвать это лучше, чем UserManager, потому что внутри живут четыре несвязанные обязанности; расплывчатое имя — это симптом. Раздели его — EmailValidator, PasswordHasher, WelcomeMailer, SignupMetrics — и каждое имя становится очевидным в тот миг, когда коробка хранит одну идею. Трудность именования была обратной связью о дизайне; разделение — то, на что она указывала.

4

Переименование и трудность именования — инструменты проектирования, но переточённые имена — это их собственный режим отказа. Относись к застрявшему имени как к зонду: когда имя не садится, спроси, неясна концепция или раздута, и пусть ответ ведёт к разделению или переосмыслению. Но есть равная и противоположная ошибка: имена настолько конкретные, что вваривают деталь реализации, которую ты захочешь изменить, в публичный словарь.

// переточённое: имя обещает конкретный алгоритм и хранилище
function fetchUsersFromMySQLWithBTreeIndex(): User[] {}  // ✗ вваривает «как» в имя
function findActiveUsers(): User[] {}                     // ✓ называет «что», прячет «как»

fetchUsersFromMySQLWithBTreeIndex читается как точное имя, но оно прибило MySQL, тип индекса и «fetch» к каждому месту вызова. Перейди на Postgres или добавь кэш — и либо имя начинает лгать, либо ты переименовываешь по всей кодовой базе. Навык в том, чтобы называть стабильное намерение (findActiveUsers — то, чего хочет вызывающий) и держать изменчивое как (какая база, какой индекс) внутри тела, где оно свободно меняться. Точно про смысл; молчаливо про механизм.

Разбор примера

Пусть трудность именования ведёт дизайн. Коллега написал это и попросил помочь с именем — он остановился на handle:

// как это назвать?
function handle(order: Order): void {
  if (order.total > 0 && order.items.length > 0) {       // валиден ли?
    const tax = order.total * 0.2;                        // вычислить налог
    order.finalTotal = order.total + tax;                // мутировать заказ
    db.save(order);                                      // сохранить
    mailer.send(order.customerEmail, "Order confirmed"); // уведомить
  }
}

Ты не можешь назвать это, потому что оно делает четыре вещи: валидирует, считает цену, сохраняет и уведомляет. handle и processOrder — оба пожимания плечами: они называют коробку, а не концепцию. Трудность именования — это дизайн, говорящий тебе раздели. Последуй ему, и каждый кусок назовёт себя сам:

function isPlaceable(order: Order): boolean {
  return order.total > 0 && order.items.length > 0;
}

function withTax(order: Order): Order {
  return { ...order, finalTotal: order.total + taxFor(order.total) };
}

function placeOrder(order: Order): void {
  if (!isPlaceable(order)) return;
  const priced = withTax(order);
  orders.save(priced);
  confirmation.send(priced);
}

Теперь ни одно имя не трудно. isPlaceable, withTax, placeOrder несут ровно по одной идее, поэтому каждое имя очевидно — трудноназываемый handle никогда не был проблемой именования, это была неочерченная концепция в расплывчатом ярлыке. Заметь, что имена говорят что (placeOrder, withTax) и никогда как (multiplyByPointTwoAndSave); налоговая ставка и почтальон свободны меняться за ними.

Почему это работает

Почему трудность именования — такой надёжный сигнал о дизайне, когда «я просто плохо придумываю имена» кажется более простым объяснением? Потому что имя — это сжатие с потерями концепции, а сжатие с потерями работает только тогда, когда есть единственный доминирующий сигнал, который стоит сохранить. daysSinceLastLogin сжимается чисто, потому что идея одна. UserManager не сжимается, потому что четыре идеи борются за единственный слот, и ни одно слово не может представить четыре несвязанные вещи, не солгав. Так что имя, которое отказывается приходить, — это обычно не пробел в словаре, а концепция, говорящая тебе, что у неё больше одного центра тяжести. Трение — это информация. Потрать её, а не замажь словом Manager.

Частая ошибка

Соблазнительная обратная ошибка: гнаться за максимальной точностью и вваривать как в имя. getUserByIdFromRedisCacheElseMySQL, sortUsersWithQuicksort, EmailValidatorUsingRegex — каждое читается как строгое и каждое ловушка, потому что обещает механизм, о котором вызывающий не спрашивал и который ты в итоге поменяешь. Вызывающий хочет getUser, sortUsers, validateEmail; кэш, алгоритм и регулярка — приватные решения. Когда реализация меняется (а она поменяется), имя-намерение остаётся правдой, а имя-механизм становится ложью или переименованием по всей кодовой базе. Называй стабильное намерение; пусть изменчивая деталь живёт и умирает внутри тела. Переточённое — не «лишняя безопасность», это преждевременное обязательство под маской.

Проверь себя
Викторина

Ты выделяешь функцию и честно не можешь найти имя лучше, чем `handleOrderStuff`. Сквозь призму этого урока, о чём эта трудность скорее всего тебе говорит?

Итог

Имя — это сжатие намерения: оно хранит, что есть вещь и зачем она существует, чтобы читатели могли пропустить как. Поэтому написать хорошее имя — это работа по проектированию: оно заставляет тебя очертить одну концепцию, а трудность именования — это обратная связь о дизайне, а не пробел в словаре. Имя, которое не садится (Manager, Helper, process, handle), обычно означает, что вещь делает слишком много или пока не понята — лекарство в том, чтобы разделять или переосмыслять, пока каждая коробка не будет хранить одну идею и не назовёт себя сама. Симметричная ловушка — переточённые имена, прибивающие изменчивую деталь реализации (FromMySQL, UsingRegex, WithQuicksort) к публичному словарю; они читаются как строгие, но становятся ложью в тот миг, когда механизм меняется. Называй стабильное намерение, прячь изменчивый механизм — а когда имя сопротивляется тебе, относись к трению как к информации о дизайне, а не к провалу твоего словарного запаса.

Практика

Начни сверху. Задачи идут от простого к сложному: вспомнить факт, применить к случаю, затем senior-уровень. Открой, попробуй, потом открой ответ.

вспомнитьприменитьуглубить0 из 4 завершено

Что-то непонятно?

Задай вопрос по этому уроку. Вопросы анонимны и попадают напрямую автору — урок станет лучше.

хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.