Умеете ли вы правильно называть функции?
Как имя, которое хорошо описывает то, что делает функция, может на практике оказаться абсолютно бесполезным и что с этим делать? Рассказываем в статье.
14К открытий14К показов
Автор перевода — Мария Багулина
Названия функций крайне важны, особенно если вы занимаетесь разработкой пользовательского API. Иногда имя может прекрасно описывать то, что делает функция, но на практике оказывается абсолютно бесполезным. И с этим можно столкнуться даже в стандартных библиотеках. В качестве примеров рассмотрим несколько функций C++20.
Пример 1: std::log2p1()
Начнём с функции std::log2p1().
Её код выглядит так:
Функция просто возвращает двоичный логарифм числа плюс один, о чём и говорит её название.
Но какая от этого польза?
На самом деле std::log2p1(x)
возвращает количество битов, необходимых для хранения x. Это действительно нужная функция, но её название совсем не отражает суть выполняемой операции.
Пример 2: std::bless()
Если вы плохо знакомы с языком C++, то вот быстрое введение в его объектную модель.
В С++ существует такая штука, как указатель — переменная, в которую записывается адрес другого объекта в памяти. Над указателем можно выполнять арифметические действия, но только если он является элементом массива. Почему так? Потому что нельзя вычитать или прибавлять что-то к произвольному указателю — это не имеет смысла, так как расположение объектов в памяти неизвестно.
Однако такое действие не является явной ошибкой, а порождает неопределённое поведение (то есть результат, зависящий от состояния памяти, компилятора, фазы луны или каких-либо других случайных факторов).
Проблема сводится не к глупым программистам, которые зачем-то складывают указатели (ведь это не запрещено), а к самому языку С++. Поэтому Ричард Смит, исследователь из Google, предложил добавить в стандарт функцию std::bless(void* ptr, std::size_t n),
чтобы при необходимости автоматически выделять массив памяти и тем самым разрешить вопрос с арифметикой для указателей.
Имя bless, разумеется, никак не сообщает обо всех этих нововведениях, поэтому было решено придумать другое название для функции. За дело взялся комитет по развитию языка C++: в качестве кандидатов они выдвинули верси иimplicitly_create_objects()
и implicitly_create_objects_as_needed()
(«неявно создать объекты» и «неявно создать объекты при необходимости»). Имена вполне логичны, ведь функция делает именно то, что в них сказано. Но, согласитесь, если бы вы не знали предысторию, то совершенно не поняли, зачем нужно создавать какие-то объекты.
Пример 3: std::popcount()
Напоследок ещё одна стандартная функция C++ 20. Просто посмотрите на её имя и попробуйте угадать, что она делает. Кажется, что-то считает? Может быть, это связано со стековыми операциями pop и push?
Скорее всего, вы не догадаетесь, если только уже не знаете о низкоуровневой инструкции с таким же названием. Функция popcount (сокращённо от «population count») подсчитывает количество установленных битов в машинном слове.
С одной стороны, это было бы отличное имя, если бы все разработчики знали названия ассемблерных битовых операций. Но будет ли оно очевидно новичку, который не в курсе таких тонкостей?
Как же следует называть функции?
Ни одно из имён, представленных выше, нельзя назвать плохим: они действительно описывают то, что делают функции. Но для вас, как пользователя, эти имена бесполезны — они содержат не ту информацию, которая вам нужна.
Вряд ли вы подумаете: «Так, мне нужно вычислить двоичный логарифм плюс один, нет ли случайно стандартной функции для этого?». Скорее в вашей голове будет мысль: «Теперь мне нужно знать, сколько битов требуется для хранения этого значения». И ваш запрос в Google будет выглядеть примерно как: «bit width function C++» — почему бы тогда вместоlog2p1()
не использовать имяbit_width()
?
Имена, которые описывают реализацию функции, будут понятны для разработчиков, но могут оказаться совершенно бессмысленными для пользователей. Причём этими пользователями являются другие разработчики, которые хотят подключить к своему проекту кем-то созданную библиотеку. Согласитесь, в этом случае важно то, что делают функции, а не как они реализованы.
Happy end
Раз уж мы затронули тему C++20, спешим сообщить хорошие новости: кажется, комитет по развитию языка всё-таки переименует std::log2p1()
вstd::bit_width()
. Но проблема именования затрагивает не только стандартные библиотеки. Поэтому если ваш проект предполагает, что его кодом кто-то будет пользоваться в дальнейшем, выбирайте говорящие имена и смотрите на функции с точки зрения пользователей.
14К открытий14К показов