вторник, 13 августа 2013 г.

Тонкости именования

Когда только начинаешь писать код, то кажется, что самое важное в работе это новомодные технологии, шаблоны проектирования, наследование объектов и прочие полиморфизмы. Что стоит использовать какую-то магическую инверсию зависимости и код сразу станет близок к идеальному. На всякие мелочи, типа стандартов оформления, именования переменных, комментирования методов, отвлекаться не хочется, да и банально жалко терять драгоценное время, которое можно потратить на написание крутой иерархии классов или реализацию какого-нибудь красивого всплывающего окошка. К тому же окошком можно похвастаться перед друзьями, а логичным названием переменной хвастаться как-то нелепо. "Смотрите, смотрите, переменную, в которой хранится, количество заказов, я назвал ordersCount!"

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

Вот несколько правил именования переменных и методов, благодаря которым мой код, на мой же несовершенный вкус, в последнее время стал понятнее и краше. Правила не отсортированы ни по важности, ни по какому другому признаку.

1. При перечислении объектов в цикле текущий объект всегда называется "oneObject"

Вот пример кода, который я писал раньше:

foreach ($orders as $order) {
    var_dump($order);
}

Проблема в том, что имена переменных, отличающиеся только одной буквой, слишком похожи друг на друга. Из-за этого возникают трудности при чтении такого кода. Кроме того, это потенциальное место для внесения ошибок. Перепутать переменные order и orders ничего не стоит. Теперь я пишу так:

foreach ($orders as $oneOrder) {
    var_dump($oneOrder);
}

Различие между переменными $orders и $oneOrder стали настолько существенными, что нельзя представить ситуацию, когда кто-нибудь случайно их перепутает. Также хорошей альтернативой oneObject может быть использование префикса current. Например, для нашего примера, текущий заказ в цикле можно назвать - currentOrder.

Мой выбор в пользу "one" основывался на том, что в функциях, с которыми мне приходится работать, префикс "current" часто используется для переменных вне циклов. Поэтому для меня oneObject имеет более определённый смысл, чем currentObject.

Upd. Ещё один вариант именования переменной внутри цикла подсмотрел в книжке у Кента Бека. Он предлагает использовать просто $each для небольших циклов и $each<Object> для более крупных. Например, $eachRow или $eachOrder.

2. Функция получения списка объектов называется getObjects, функция получения одного объекта - detailObject

Если используем getObjects и getObject, то опять же имеем проблемы с похожестью имён. Нужно правило именования, которое не позволяет вызвать функцию по ошибке. getObjects и detailObject решают проблему. Неплохим выбором может быть и выбор пар getObject и getObjectList или getObject и listObjects. Отдать предпочтение можно любому из вариантов. Главное условие - чтобы принятый вариант последовательно использовался во всём коде.

3. Если тип является значимым свойством переменной, то он должен указываться в имени

Со времён предания анафеме венгерской нотации во многих книгах можно прочитать, что использование типа в имени переменной - это зло. Аргумент: при изменении типа название теряет актуальность и, вместо того, чтобы облегчать понимание программы, только запутывает логику. Для языков без строгой типизации ситуация может быть особенно грустной. Да и повсеместно считается хорошим стилем, когда переменная отражает бизнес-сущность, а не машинное представление этой сущности. Но для правил всегда есть исключения. Рассмотрим такой фрагмент кода. Пусть нам надо преобразовать массив переменных в строку и передать его в другую функцию для формирования условия SQL-запроса.

$orderIds = array(1, 2, 3, 4, 5);
$order{???} = implode(',', $orderIds);
buildOrdersQuery($order{???});

Как назвать переменную - результат выполнения функции implode? Для данной переменной то, что она строкового типа, является значимым фактом, Именно для преобразования массива в строку она и была заведена. Нет ничего плохого, чтобы отразить этот факт в её имени,

$orderIds = array(1, 2, 3, 4, 5);
$orderIdsAsString = implode(',', $orderIds);
buildOrdersQuery($orderIdsAsString);

Теперь мы видим, что есть переменная-массив и есть строка, содержащая ту же информацию. Это понятно уже из имён переменных. Не требуется дополнительных пояснений и комментариев.

4. Булевские переменные называем с использованием префиксов не допускающих неоднозначного толкования хранящихся в них значений

Удобство следованию правилу "булевские переменные называем с использованием префикса is" доказано временем. Из названия переменной сразу понятно, что означает то или иное значение. Но кроме отличного слова "is" есть и другие слова, использование которых также не допускает неоднозначности в толковании значения. Например, has или allow. Не нужно ими пренебрегать

$clientHasDiscount = TRUE;
...
if ($user->allowAccessToCP) { ...

Нельзя предположить, что, если в переменной $clientHasDiscount хранится FALSE, то у клиента есть скидка. Скидка только в том случае, если $clientHasDiscount станет равно TRUE. Название переменной "самодокументировало" своё использование.

5. Если функция возвращает статус выполнения, то переменную, хранящую этот результата надо называть не $res, $result или $status, а $success или $done

Причины те же, что и в предыдущем правиле. Уже из название становится понятен смысл допустимых значений.

public function updateOrder(Order $order) {
    $success = FALSE;
    if ($options->allowOrderUpdate) {
        $success = Db::saveOrder($order);
    }
    return $success;
}

Абсолютно ясно, в каком случае функция отработала нормально, а в каком обновление не состоялось или прошло с ошибками.

Ну, и в качестве бонуса, дочитавшим до конца - немного хорошей музыки в тему заметки. Aрия Рейстлина из мюзикла "Последнее испытание". "Я дам тебе имя."