Именуйте классы, переменные и функции для людей, а не для машин

Чтобы не писать слишком длинный комментарий к статье Практическое руководство по именованию классов, функций и переменных, решил написать ответ в виде отдельной статьи.


TL,DR


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


Компилятор, машина, без проблем поймет хоть shouldDisplayPagination, хоть p, ей без разницы. В отличии от человека. Проблема с именем shouldDisplayPagination в том, что слишком перегружено ненужными деталями. Средний человек способен оперировать одновременно небольшим количеством объектов в памяти. Примерно три-четыре, до семи. Поэтому, пока он читает слова should, display, pagination, у него из памяти вытесняются другие объекты, возможно детали алгоритма, которые он пытается понять, читая исходный код. В итоге, человек хуже поймет алгоритм, и сделает ошибку при его исправлении или использовании. Слишком описательные названия, как ни странно, ведут к прямо противоположному результату, к ухудшению понимания и большему количеству ошибок.


Любая система именования переменных, хоть венгерская нотация (если кто-то её ещё помнит), хоть современный аббревиатурный подход (P)A/HC/LC, ухудшает читаемость и поддерживаемость кода. Потому что он делает код в виде текста более похожим на машинно генерируемый код, прекрасно воспринимаемый машиной, но плохо воспринимаемый человеком. Некоторые принципы именования переменных, о которых речь пойдет ниже, в целом хорошая идея, но системы с табличками, колонками и строками, однозначно нет. Названия не вписываются в жесткую структуру табличек.


Далее по пунктам.


Префиксы


От использования префиксов в целом лучше воздержаться. Потому что они повторяют очевидную вещь, тип значения, возвращаемого функцией. Нет смысла повторять уже существующую информацию. Единственным исключением является префикс is- который очень широко используется в стандартной библиотеке (речь про Java). has- и should- тоже есть, но уже гораздо меньше, и их использовать не надо.


Во всех приведенных примерах переменные вообще ненужны, потому что само выражение достаточно короткое, вместо


const color = 'blue';
const isBlue = (color === 'blue'); // свойство
const isPresent = true; // состояние

if (isBlue && isPresent) {
  console.log('Blue is present!');
}

лучше


const color = 'blue';
const isPresent = true; // состояние

if (color === 'blue' && isPresent) {
  console.log('Blue is present!');
}

Вместо


const hasProducts = (productsCount > 0);

лучше


productsCount > 0

там где используется переменная hasProducts. Да и productsCount слишком длинное название для переменной, лучше products или count.


Действие


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


Совершенно определенно следует отказаться от префиксов get- и set-. Если функция возвращает какое-то значение, то её название должно описывать, что возвращает функция, глагол get- совершенно избыточен


Соответственно, не


function getFruitsCount() {
  return this.fruits.length;
}

а


function fruitsCount() {
  return this.fruits.length;
}

Когда программист читает


if (getFruitsCount() > 10)

или


if (fruitsCount() > 10) 

понятность этого кусочка кода одна и та же, но в первом случае ему необходимо прочитать просто больше слов. Слово get- не добавляет нисколько дополнительной информации.


Префикс set- описывает действие на слишком низком уровне, тем самым разрушая инкапсуляцию. Например, вместо setName лучше название rename. Приведенный в статье пример опять слишком простой, и не описывает достаточно зачем нужна эта функция вообще, вместо


let fruits = 0;
const setFruits = (nextFruits) => {
  fruits = nextFruits;
};
setFruits(5);
console.log(fruits); // 5

опять гораздо лучше будет


let fruits = 0;
fruits = 5;
console.log(fruits); // 5

Далее, если название начинается с глагола, функция не должна возвращать значение. Таким образом


const fetchPosts = (postCount) => fetch('https://api.dev/posts', {...});

вариант не очень. Если это функция, то должна описывать то что возвращает, соответственно


const posts = (count) => fetch('https://api.dev/posts', {...});

Если это метод класса, то он должен изменять состояние объекта, но не возвращать значение


class ApiPosts {
    var posts
    void fetch(count) { posts = fetch('https://api.dev/posts', {...}); }
}

S-I-D


Да короткие названия это хорошо (S), но пример


const shouldDisplayPagination = (postsCount > 10); // альтернатива

совершенно точно таким не является


const pagination = (postsCount > 10); // альтернатива

или


postsCount > 10

уже гораздо лучше.


Сокращения


В целом сокращения это плохо, но иногда, если переменная используется в трех строчках кода, нет ничего плохого в сокращении


for (URI u: uris) {
    pages.fetch(u);
}

читается гораздо легче и понятнее, чем


for (URI uriToFetch : urisArray) {
    pagesCollection.fetch(uriToFetch);
}

PS: Наименования переменных, функций и классов это сложно. Сложно подобрать понятные, очевидные и подчеркивающие предметную область названия. Но совершенно точно эти названия нельзя уложить в стройную аббревиатурную систему (P)A/HC/LC, XYZ или любую другую. Потому что многообразие предметных областей, программ, обрабатывающих данные, не описывается простой табличкой.

@pin2t
25.05.2021 08:10 UTC
Первоисточник

Комментарии

@EndUser
25.05.2021 04:13 UTC
+3

Жесть.

У меня смутное ощущение, что с текстом что-то не так:

Именование несёт, грубо говоря, три функции:

  • Пока пишешь код - нужно создавать имена переменных. При наличии системы не нужно вспоминать какие переменные нужно использовать. Легче их сконструировать. При стандарте именования каждый раз должно получаться одинаковое имя. В вашей статье этого нет.

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

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

Разумеется, нельзя писать имена подробными фразами грамматиками естественного языка. Это просто затмит операции, так как они односимвольные. (Хотя для смеху можно их макросами препроцессора тоже заменить глаголами естественного языка).

Но весь ваш текст, как будто, борется только с этой одной мыслью.

@crystallize
25.05.2021 05:07 UTC
0
Для облегчения восприятия код надо писать так работает голова, а она не всегда до конца рациональна. Мы привыкли думать что функция выполняет какую-то работу, поэтому нормально напоминать об этом префиксами get set и прочими. Автор здесь занимается оверинжинирингом, как раз для машины как будто.
@
25.05.2021 06:05 UTC
0
НЛО прилетело и опубликовало эту надпись здесь
@pin2t
25.05.2021 07:16 UTC
-1

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


import io.javalin.Javalin;

public class HelloWorld {
    public static void main(String[] args) {
        Javalin app = Javalin.create().start(7000);
        app.get("/", ctx -> ctx.result("Hello World"));
    }
}

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


Название getOrCreateFolder() слишком переусложенено ненужными деталями. Опятьже, функци должна называться так, что она возвращает. Более хорошее название для этой функции folder(), оно нисколько не снижает понимание кода


folder().remove(file)

по сраунению с


getOrCreateFolder().remove(file)

но в то же врем легче читается

25.05.2021 08:53 UTC
-1

Опятьже, функци должна называться так, что она возвращает

У меня есть функция FolderId, есть идеи, что она делает? Подсказка: она принимает путь к каталогу

И ещё есть функция, ничего не возвращающая, как её назвать?

Более хорошее название для этой функции folder(), оно нисколько не снижает понимание кода

Вы серьёзно не видите разницу между folder и getOrCreateFolder?

25.05.2021 09:43 UTC
0
У меня есть функция FolderId, есть идеи, что она делает?

Возвращает какой-то номер из ОС, относящийся в папке. inode, например


И ещё есть функция, ничего не возвращающая, как её назвать?

функция, ничего не возвращающая, должна называться глаголом, add, remove, rename, modify, fetch, update, push, send, receive, connect, write… Все эти функции что-то делают, но не должны ничего возвращать

25.05.2021 09:53 UTC
+1
Возвращает какой-то номер из ОС, относящийся в папке. inode, например

Что она возвращает — это я и так сказал, я спросил 'что она делает?'
В чём суть функции?
Вот Вы видите код, вызывающий эту функцию, что Вы скажете про неё?
25.05.2021 11:46 UTC
0
НЛО прилетело и опубликовало эту надпись здесь
26.05.2021 12:54 UTC
-1

getOrCreateFolder() - это вообще не хорошо. Нарушен Принцип единственной ответственности

27.05.2021 06:18 UTC
0
НЛО прилетело и опубликовало эту надпись здесь
27.05.2021 10:36 UTC
0

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

Фасад тут не при чём. И с точки зрения нейминга подобное название Фасада то же плохое. Фасад прячет сложность, оставляя наружу только нужное.

Моё замечание касалось имени. Имя предполагает два действия и логический оператор. Что делает функция в данном конкретном месте нельзя понять не изучив её код.

Идея предложенной здесь https://habr.com/ru/post/558874/ методики (как и многих других) в том чтоб сделать код понятным для чтения.

getFolder - вообще ни когда не понятно что тут происходит. Получаем путь к папке? Получаем доступ к папке? Получаем содержимое папки? Имя папки?....

27.05.2021 15:18 UTC
0
НЛО прилетело и опубликовало эту надпись здесь
@vasya_robot
26.05.2021 02:56 UTC
+1
Про get вообще мимо. Глагол обязателен в массе конвенций.

В rust не используется приставка «get_» для геттеров, как мне теперь жить-то?
26.05.2021 04:41 UTC
+1

Совершенно спокойно, get- и set- это Java-догма

26.05.2021 08:42 UTC
0
НЛО прилетело и опубликовало эту надпись здесь
26.05.2021 11:19 UTC
0
И где в getOrCreateFolder или в методе get REST контроллера вы увидели getter?

А разве в моем комменте есть намек на то, что там есть геттер? Я ответил на то, что процитировал
Про get вообще мимо. Глагол обязателен в массе конвенций.
, жизнь не заканчивается на rest и методах типа getOrInsert
26.05.2021 12:16 UTC
0
НЛО прилетело и опубликовало эту надпись здесь
26.05.2021 13:10 UTC
0

Во-первых используется. Как исключение, но используется. И встречается в проектах не редко.

Во-вторых читать оригинальную статью не которую в начале текста ссылается автор. Там упоминается о том что в языках бывает своя специфика (правда вскользь)

В-третьих не стесняться думать. В оригинальной статьей предложена методика. Она не догма и, конечно, может и должна корректироваться

@qw1
25.05.2021 07:52 UTC
0
Автор набросился на модельные примеры, такие как
const isBlue = (color === 'blue');
const hasProducts = (productsCount > 0);

А в реальном коде у меня условия гораздо длиннее, например
const isVipClientOrder = (orderType == 932874) || (orderType == 90823475) || (orderType == 1893958) || (orderType == 29457171);

И что, по замыслу автора, нужно копипастить эту конструкцию каждый раз, когда она потребуется?
@pin2t
25.05.2021 08:20 UTC
0

Нет, конечно же. Ещё раз повторюсь, нет единой системы по которой придумываются и используются названия. В случае длинного выражения действительно имеет смысл использовать переменную. Но вполне достаточно её назвать isVip или даже просто vip, вполне понятно будет, нисколько не менее понятно чем длинное название isVipClientOrder.

28.05.2021 07:53 UTC
0

В языке по типу Котлин можно было бы описать метод в контексте его надобности (глобально описать трудно, т.к Vip в разном контексте может быть разный, тогда и методов будет куча) и сделать так

order.isVip()

И не надо никаких переменных. Но увы не все языки так могут. Вот и получаем кучу переменных с подробностями что бы не путаться

@Mikluho
25.05.2021 13:01 UTC
+1
Ну… Может иногда get и лишний, но ещё реже стоит следовать другим рекомендациям, а уж совсем не следует делать подмен вида fruitsCount -> fruits.
Во всех мне известных нотациях fruits — это коллекция/перечисление того или иного вида. И я совершенно точно хочу понять смысл функции по её названию, если это вообще возможно (бывают случаи, когда без документации никуда). Для примера, с fruits мне придётся как минимум посмотреть на возвращаемый тип, и это точно зря потратит моё время.
Примерно такая же ошибка в большинстве приведённых советов.

Отдельно про контекст и память. Тут аргументация вообще ясельная. Вот скажите, вы когда книги или статьи читаете, может уловить смысл фразы длиннее 7 слов? А смысле текста длиннее 7 фраз? Я думаю, что вы с этим справляетесь вполне успешно. Название переменной — это вообще всего лишь слово в тексте программы. Да, составное, да, возможно новое, что требует чуть больше внимания при прочтении, но сложность равно такая, как при чтении составных слов — главное чтобы структура слова была корректна, все корни известны и чтобы оно передавало правильный смысл.
Так что этой проблемы нет. А вот разгадывать, что именно значит то или иное название в той парадигме, которую вы предлагает, совсем не хочется.
@Mikluho
25.05.2021 13:02 UTC
0
-
@it-school
25.05.2021 13:33 UTC
+3

Использование имени функции без глагола сродни анекдоту:

  • Борт номер 1234, приборы!

  • 200

  • Что 200?

  • А что приборы?

Имя свойства можно и без глагола, а имя метода - очень желательно!

@GoodGod
25.05.2021 15:54 UTC
+1
Допустим мы имеем переменную isPresent. Мы можем упростить ее до present. Но если у нас есть несколько объектов (products и filters), то уже становится непонятно, что конкретно «present» — товар или фильтр. По-этому человеку придется держать в голове к чему относится переменная: products или filters. А человек может держать одновременно в голове не более 7 вещей. По-этому название переменной present придется расширить до productsPresent (а англичане может быть добавят туда еще «are», т.к. у них так принято). Теперь держать контекст переменной present в голове не нужно.
@pankraty
25.05.2021 18:28 UTC
0

class ApiPosts

{

var posts

void fetch(count) { posts = fetch('https://api.dev/posts', {...}); }

}

Вот это прям лютый антипаттерн - на ровном месте создать зависимость между членами класса, похоронить потокобезопасность, сделать вызовы более многословными (прощай expression body), и все это ради... А ради чего, собственно? Плюсов я не вижу.

@AABur
26.05.2021 13:57 UTC
0

Зря автор не стал писать комментарий к оригинальной статье. Интересное обсуждение развалилось на два. Ну да ладно.

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

А вот дальше у вас написано совсем не про чистый совершенный код:

Поэтому, пока он читает слова should, display, pagination, у него из памяти вытесняются другие объекты, возможно детали алгоритма, которые он пытается понять, читая исходный код. В итоге, человек хуже поймет алгоритм, и сделает ошибку при его исправлении или использовании. Слишком описательные названия, как ни странно, ведут к прямо противоположному результату, к ухудшению понимания и большему количеству ошибок.

Если ваш код так написан, что пока его читаешь забываешь о чём прочитал, то вам точно надо его переписать. Упростить, разбить на понятные блоки/модули/функции/классы и т.п.

Любая система именования...

...ухудшает читаемость и поддерживаемость кода

Гм... В Microsoft, Google, Apple и т.д. дураки сидят и от нечего делать naming conventions используют (легко гуглится). Соглашения об наименовании для того и принимаются чтоб облегчить дальнейшую поддержку кода.

Правила наименования прекрасно вписываются в табличку. Надо просто подобрать/придумать табличку для конкретного случая. Адаптировать чьи-то рекомендации всегда проще чем придумывать с нуля.

Дальше автор зачем-то начинает пытаться разрушить предложенную методику, не предлагая при этом ни какой. Точнее предлагая писать "как хочется"

Смысл статьи в первую очередь был в том чтоб показать читателям один из подходов и объяснить его.

Тем более глупо пытаться "улучшить" модельные примеры. И упущен важный момент! Мы программируем на английском языке!!! Пытаться поправить названия через прямой, а затем обратный, переводы - это вообще не уместно.

Ещё один важный плюс от применения соглашения об именовании: люди из разных стран, языков, культур - одинаково поймут написанный код. (Например английский заметно отличается у англичан и американцев, немецкий у германских и австрийских немцев тоже. Речь не о произношении а о правилах языка)

Резюме:

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

Но совершенно точно эти названия надо уложить в стройную систему. Для улучшение читаемости кода и качества его поддержки.