Слово дано человеку не для самоудовлетворения,
а для воплощения и передачи той мысли, того чувства,
той доли истины или вдохновения, которыми он обладает,
другим людям.
В. Г. Короленко
Идентификатор — элемент стиля
Понятие стиля программирования включает в себя много аспектов. Не претендуя на полноту, перечислим некоторые из них: применение структурного или объектно-ориентированного подхода, выделение алгоритмических конструкций в исходных текстах программ доступными изобразительными средствами (например, с помощью пробельных отступов, записи алгоритмов «лесенкой»), использование комментариев. Хороший стиль программирования способствует разработке качественных продуктов.
При работе над большим программным проектом специалист оказывается перед необходимостью дать имена (идентификаторы) множеству элементов программы: файлам с исходными текстами, процедурам и функциям, типам данных и переменным. Он не может позволить себе роскошь долго раздумывать над выбором удачного имени — решение нужно принимать в кратчайшие сроки. Но нельзя давать и первое пришедшее в голову название, потому что имя элемента программы должно характеризовать этот элемент, ассоциироваться с ним. Удачно выбранные идентификаторы являются хорошим подспорьем к комментариям в деле
документирования исходных текстов программ. Таким образом, правила составления идентификаторов являются неотъемлемой частью стиля программирования.
В этой статье приводятся примеры таких правил и излагаются некоторые общие соображения, которые должны помочь программисту в формировании своей системы создания идентификаторов.
На пути к собственной системе
По аналогии со словами естественных языков идентификатор можно представить в виде совокупности элементов (см. рисунок).
В простейшем случае идентификатор может содержать одну-единственную часть — корень. Но для уточнения характеристик обозначаемого им элемента программы зачастую прибегают к составлению идентификатора из нескольких слов. Префикс и суффикс используются для того, чтобы подчеркнуть отличия в каких-нибудь деталях.
Составляя идентификатор, нельзя забывать об ограничениях, которые накладываются спецификациями языков программирования. К таким ограничениям относятся: максимальная длина идентификатора, допустимый набор символов и наличие или отсутствие различия прописных (больших) и строчных (малых) букв на уровне транслятора.
Как правило, максимальная длина идентификатора для современных языков программирования не является принципиальным ограничением, однако в ранних версиях языка Бейсик имя переменной не могло превышать двух символов, а в dBase-подобных языках идентификаторы различаются только по первым восьми символам.
Для большинства языков программирования при построении идентификаторов можно использовать большие и малые буквы латинского алфавита, знак подчеркивания, который приравнивается к букве, а также цифры. В Cи-подобных языках (Cи, С++ и Java) прописные и строчные буквы считаются различными, в некоторых других (Паскаль, Бейсик, Фортран) — одинаковыми. Однако в любом случае смешанное использование больших и малых букв позволяет выделить составные части идентификатора. Например, hControlPanelWnd воспринимается легче, чем hcontrolpanelwnd. К сожалению, некоторые приемы, традиционные для языков первой группы, невозможно использовать в языках второй. Так, иногда бывает удобно дать переменной такое же имя, как и у ее типа, но в другом регистре, например HWND hwnd. Однако для языка Паскаль такая запись не годится, поскольку в нем идентификатор для типа и переменной окажется одним и тем же, что неправильно.
Возможность использования длинных идентификаторов, с одной стороны, позволяет заложить в них больше смысловой нагрузки, с другой — затрудняет их использование в тексте программы, требует больше времени на набор, уменьшает количество воспринимаемой с первого взгляда информации. Сократить идентификатор можно, если вместо слов использовать аббревиатуры и сокращения или путем исключения гласных букв.
Для программистов, не применяющих в своей практике английский язык, ограничение на использование символов кириллицы может создавать некоторые неудобства. В этом случае иногда прибегают к транслитерации — записи русских слов латинскими буквами, результатом чего становится появление идентификаторов KolvoElementov, LuchshijResultat или даже CymmaHaC4ete.
При разработке собственной системы образования идентификаторов программист должен иметь в виду, что подобные правила могли быть созданы еще до него разработчиками популярных программных средств. Так, в библиотеке Microsoft Foundation Class (MFC), которая широко используется при написании программ для операционной системы Windows на языке C++, в качестве префикса для обозначения класса как типа данных используется символ C (от слова Class), а для указания на то, что переменная является членом данных этого класса, используется префикс m_ (от Member — член). Если проигнорировать эти факты, то легко оказаться в ситуации, когда пространства имен используемой библиотеки и разрабатываемой программы пересекутся, что приведет к конфликтам идентификаторов и трудно обнаруживаемым ошибкам. Поэтому, выбирая существующую систему в качестве основы для разработки собственных правил именования идентификаторов, следует внести в нее некоторые изменения. Например, при назначении имен для классов можно использовать пре-префикс My. В этом случае класс MyCWnd будет отличаться от библиотечного класса CWnd и наложения имен не произойдет.
Типичной ситуацией при программировании является необходимость временного хранения значения переменной. В этом случае для временной переменной можно взять такое же имя, как у основной, и добавить к нему суффикс T. Например: PhoneNumberT:= PhoneNumber.
Как это делают в Borland?
Хороший пример использования системы при выборе имен для элементов программы можно найти в книге Стива Тейксейра и Ксавье Пачеко «Borland Delphi 4. Руководство разработчика». Рекомендации, изложенные в книге, ориентированы на разработку программ для операционной системы Microsoft Windows с использованием языка программирования Object Pascal. Однако можно выделить некие универсальные требования к именам подпрограмм, типов данных и переменных.
Имя может формироваться из нескольких слов, отражающих различные свойства элемента программы. В этом случае каждое новое слово должно начинаться с прописной буквы для лучшего восприятия идентификатора. Например, FirstDayОfWeek — первый день недели, SpaceChar — пробельный символ, BestRoundtripTime — лучшее цикловое время, BankAccount — счет в банке, TimesPerSecond — раз в секунду. Кстати, в последнем случае не менее наглядным было бы использование односимвольного идентификатора F, традиционно обозначающего частоту. Приведенные выше идентификаторы можно сократить: SpcChr, BestRTT, BankAcnt, TimesPerSec.
Имена подпрограмм (подпрограммами будем называть такие структурные элементы, как процедуры и функции) должны начинаться с прописной буквы. Имена процедур, выполняющих некоторое действие, должны начинаться со слова, обозначающего это действие. Например: RemoveSpaceCharacters — удалить пробельные символы, FormatHardDrive — отформатировать жесткий диск, SendLetter — отправить письмо. Имена процедур, устанавливающих значения переменных, должны начинаться со слова Set или Init. Пример: SetUserName — задать имя пользователя, InitRndTable — инициализировать таблицу случайных чисел. Имена подпрограмм, осуществляющих выборку значений, следует начинать со слова Get: GetAcntBalance — получить баланс счета, GetLastLoginTime — получить время последнего входа в систему.
Имена формальных параметров процедур и функций составляются на основе идентификаторов, которые передаются в подпрограмму. Для того чтобы избежать конфликтов с локальными переменными или именем поля класса, имена параметров предваряются символом A (от a, an — неопределенный артикль в английском языке). Например: AUserName — имя пользователя, AUserPassword — пароль пользователя.
Имена переменных формируются в соответствии с общими принципами. Счетчикам циклов обычно присваивается имя, состоящее из одного символа: I, J, K. Особое внимание следует уделить выбору имен для переменных логического типа с тем, чтобы смысл их значений True или False был ясен из контекста, например: InvalidUserName — неправильное имя пользователя (да/нет), NeedMoreTime — требуется дополнительное время (да/нет).
Названия типов, которые являются зарезервированными словами, должны быть записаны строчными буквами. Идентификаторы типов данных, определяемых пользователем: перечислений, записей, классов, массивов — должны начинаться с прописной буквы T (от Type — тип), а если описывается тип, являющийся указателем, то с буквы P (от Pointer — указатель). Экземплярам переменных пользовательского типа данных следует присваивать имена, совпадающие с именем типа, но без префикса T, если нет причин конкретизировать имя переменной. Идентификаторы элементов перечислимого типа (констант) должны содержать набранный строчными буквами дву- или трехсимвольный префикс, указывающий на имя перечислимого типа, к которому эти идентификаторы относятся. Рассмотрим несколько примеров, иллюстрирующих сформулированные правила.
-
Перечислимому типу TPenColor = (pcRed, pcBlue, pcGreen, pcYellow, pcBlack, pcBrown) соответствуют следующие экземпляры переменных: PenColor, FirstPenColor, SecondPenColor.
-
Массиву TCycleArray соответ-ствует указатель PCycleArray.
Классы являются типами-контейнерами, инкапсулирующими в себе поля — данные других типов, свойства — средства доступа к полям, и методы — подпрограммы для выполнения операций над полями. Идентификаторы классов подчиняются общим правилам, предложенным для пользовательских типов данных. Имена полей класса подчиняются соглашениям о присвоении имен переменным за исключением того, что они должны начинаться с префикса F (от Field — поле). Имена методов подчиняются соглашениям о выборе имен для процедур и функций. Имена свойств должны совпадать с идентификаторами полей, для доступа к которым они служат, но без начальной буквы F, и, в отличие от методов, должны быть существительными, а не глаголами. Свойствами представляются данные, а методами — действия. Имена свойств массивов имеют форму множественного числа, в то время как имена обычных свойств — форму единственного числа.
Свободный взгляд на систему
Интересный и в каком-то смысле противоположный взгляд на способ выбора названий для элементов программ демонстрируют разработчики операционной системы Linux (соглашение о стиле программирования можно найти в файле CodingStyle подкаталога Documentation дистрибутивного пакета с исходными текстами ядра). Они делают акцент на лаконичность языка программирования Cи и предлагают следовать этой традиции и при написании программ на этом языке.
В частности, критикуются длинные имена переменных, построенные путем слияния некоторых определяющих слов с разделением их с помощью прописных букв. Предполагается, что функции не должны иметь больше 10—15 локальных переменных, а для их именования вполне достаточно букв латинского алфавита. Любую временную переменную, независимо от ее типа, Cи-программист назовет tmp, что записывается кратко и нисколько не затрудняет понимание сути по сравнению с идентификатором ThisVariableIsATemporaryCounter.
С другой стороны, функции должны иметь названия, облегчающие чтение текста программы, и в этой ситуации сокращение может ухудшить читабельность. Однако вместо выделения слов, составляющих имя подпрограммы, с помощью прописных букв предлагается использовать разделяющие символы подчеркивания, например count_active_user.
Таким образом, все идентификаторы записываются с использованием только строчных букв, а прописные зарезервированы для имен макросов, определяющих константы.
Крайне неодобрительное отношение высказано и в адрес так называемой венгерской нотации, одной из ключевых идей которой является введение информации о типе переменной в ее название. По мнению разработчиков Linux, следить за типом — задача компилятора, а при внесении в программу изменений в процессе доработок следование принципам составления идентификаторов Win32 API может создать дополнительную путаницу.
Интересный подход к выбору имен для переменных предлагается в статье Джоэла Сполски «Как заставить неправильный код выглядеть неправильно» (в Интернете можно найти русский перевод этой статьи, выполненный Ильей Болодуриным). Он заключается в том, что префикс переменной должен характеризовать состояние данных, которые в ней хранятся. Например, если в какой-то системе осуществляется обработка текстовых строк, заключающаяся в неких преобразованиях входной информации в форму, пригодную для безопасного использования (это может быть и проверка орфографии в текстовом процессоре, и цензура на форуме, и проверка на наличие вредоносного кода в данных, полученных при заполнении форм), то в именах переменных должна содержаться информация о стадии этого преобразования. Так, переменные, в которые попадает сырая информация, должны предваряться префиксами ud (unsecure data — небезопасные данные), а переменные для хранения обработанных строк должны начинаться с sd (secure data — безопасные данные). Описанный подход способствует документированию исходного текста программы, по именам переменных можно определить ошибочные операторы, в которых, например, используются небезопасные данные в качестве выходной информации.
В этой же статье реабилитируется венгерская нотация, к которой уже сформировалось неоднозначное отношение профессионалов. Зародилась она в подразделении приложений Microsoft, которое занималось разработкой Word и Excel. И префиксы, используемые в именах переменных исходных текстов этих продуктов, были ориентированы как раз на отражение смысла значения, хранящегося в переменных. Например, префикс rw означал, что в переменной содержится номер строки, а col — что находится номер столбца (от слов row — строка и column — столбец соответственно). Такое использование префиксов удачно устанавливает визуальное разделение между сходными, но не одинаковыми понятиями. Так, например, xl может означать горизонтальную координату относительно листа, а xw — тоже горизонтальную координату, но относительно окна. И код, в котором имеется ошибочное присваивание вида xlDocument1:= xwDocument2 с высокой долей вероятности обратит на себя внимание программиста.
Заключение
Во все времена к выбору имени или названия люди подходили со всей серьезностью. Раньше с именем связывали действие магических сил. Социологические исследования показывают, что удачное имя, данное ребенку, оказывает влияние на успех его карьеры во взрослой жизни. Выбирая название для нового продукта, маркетологи анализируют значения одних и тех же слов на различных языках, чтобы обеспечить ему благозвучность с точки зрения покупателей разных национальностей. Не стоит пренебрежительно относиться и к выбору идентификаторов при разработке программы. В конце концов, «как вы лодку назовете –- так она и поплывет».
О больших и малых буквах
Чтобы устранить всякую неоднозначность толкования понятий, обозначающих регистр букв, отметим, что термин «прописная буква» является синонимом определению «большая», а «строчная» означает то же, что и «малая». Иногда в литературе можно встретить такую характеристику буквы, как «заглавная», однако ввиду отсутствия прямо соответствующего этому слову антонима в статье этот термин не используется.
Обратимся к истории
Возможности первых трансляторов с языков высокого уровня были ограничены характеристиками существовавших в то время ЭВМ. В целях экономии оперативной памяти допускалось использование идентификаторов, состоящих только из одного или двух символов. Поэтому появились неписаные правила использования кратких имен для переменных, имеющие своими истоками устоявшиеся в математике и физике обозначения (см. таблицу).
С развитием технической базы расширяется и сфера применения ЭВМ. Из мощного калькулятора компьютер превращается в средство хранения и обработки больших объемов информации, организуются вычислительные сети. Для решения новых задач создаются сложные программные комплексы, разработка которых требует использования новых подходов в технологии программирования. Возникают парадигмы структурного, а затем и объектно-ориентированного программирования. Совершенствуются и инструментальные средства разработки программ. Ограничения на длину идентификатора перестают носить принципиальный характер, что, с одной стороны, открывает простор для фантазии разработчика, а с другой — вынуждает задуматься о способе использования новых возможностей. В коллективах, занимающихся разработкой программ на профессиональной основе, зарождаются стандарты оформления исходных текстов. Соглашения, оговаривающие правила построения идентификаторов, являются важным разделом этих стандартов.
Венгерская нотация
Статья из «Википедии»: «Венгерская нотация — соглашение программистов об именовании переменных, констант и прочих идентификаторов в коде программ. Свое название венгерская нотация получила благодаря программисту компании «Майкрософт» венгерского происхождения Чарльзу Шимоньи (венг. Karoly Simonyi), предложившему ее еще во времена разработки первых версий MS-DOS. Со временем его система стала не только внутренним стандартом «Майкрософт», но и широко распространенным правилом среди программистов всего мира. Суть венгерской нотации сводится к тому, что имена идентификаторов предваряются заранее оговоренными префиксами, состоящими из одного или нескольких символов. При этом, как правило, ни само наличие префиксов, ни их написание не являются требованием языков программирования и у каждого программиста (или коллектива программистов) могут быть своими. Использование в каждом из языков программирования своей терминологии также вносит особенности в выбор префиксов».
Отметим, что программисты, разрабатывающие программы для операционной системы Unix, используют в своих программах другие принципы выбора идентификаторов, на которые, по всей видимости, наложен отпечаток как языка программирования Си и идеология операционной системы, так и основной сферы ее применения — образовательные, научные и исследовательские центры, государственные и правительственные учреждения.